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

O Operator
Diff

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

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

Added
+470
lines
Removed
-0
lines
Context
0
unchanged
Blobs
from
to 3ad49d98eb51
Restore this content →
Unified diff
Naive LCS-based line diff. Additions in emerald, deletions in rose. Both sides decompressed live from BLOB_STORE.
1<!-- =====================================================================
2 Admin Promotions -- platform-wide. ContactForge subscription
3 discounts (coupons, sales, flash sales) covering every customer.
4===================================================================== -->
5
6<div class="page-head">
7 <div>
8 <span class="page-eyebrow"><span class="dot"></span> Admin &middot; Catalogue</span>
9 <h1 class="page-title">Pricing &amp; Promotions</h1>
10 <p class="page-subtitle">Coupons, sales, and discounts that apply across every ContactForge subscription. Use for new-customer acquisition, seasonal pushes, and limited-time marketing events.</p>
11 </div>
12 <div class="page-actions">
13 [if:$is_list][if:$not_tutorial_mode]<a href="/admin_promotions.cgi?tutorial=1" class="btn btn-secondary"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>Sample Data View</a>[/if][/if]
14 [if:$is_list][if:$tutorial_mode]<a href="/admin_promotions.cgi" class="btn btn-secondary"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>Exit sample view</a>[/if][/if]
15 [if:$is_list]<a href="/admin_promotions.cgi?new=1" class="btn btn-primary"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><line x1="12" y1="5" x2="12" y2="19"/><line x1="5" y1="12" x2="19" y2="12"/></svg>New platform promotion</a>[/if]
16 [if:$is_new]<a href="/admin_promotions.cgi" class="btn btn-secondary">Cancel</a>[/if]
17 [if:$is_edit]<a href="/admin_promotions.cgi" class="btn btn-secondary">Back to list</a>[/if]
18 </div>
19</div>
20
21<style>
22 .pp-tabs { display:flex; gap:4px; padding:4px; background:var(--col-surface-2); border:1px solid var(--col-border); border-radius:10px; margin-bottom:22px; width:fit-content; }
23 .pp-tab { padding:8px 18px; font-size:13px; font-weight:600; color:var(--col-text-2); border-radius:7px; text-decoration:none; transition:background 0.12s, color 0.12s; white-space:nowrap; }
24 .pp-tab:hover { color:var(--col-text); }
25 .pp-tab.is-active { background:var(--col-surface-3); color:#fff; box-shadow:inset 0 0 0 1px var(--col-border-2); }
26 .pr-flash { padding:10px 14px; border-radius:8px; margin-bottom:14px; font-size:13px; }
27 .pr-flash.ok { background:rgba(34,197,94,0.10); color:#86efac; border:1px solid rgba(34,197,94,0.30); }
28 .pr-flash.err { background:rgba(239,68,68,0.10); color:#fca5a5; border:1px solid rgba(239,68,68,0.30); }
29 .pr-kpis { display:grid; grid-template-columns:repeat(6,1fr); gap:12px; margin-bottom:22px; }
30 .pr-kpi { background:var(--col-surface-1); border:1px solid var(--col-border); border-radius:10px; padding:14px 16px; }
31 .pr-kpi .lbl { font-size:11px; text-transform:uppercase; letter-spacing:0.08em; color:var(--col-text-2); font-weight:600; }
32 .pr-kpi .val { font-size:22px; font-weight:700; color:#fff; margin-top:4px; }
33 .pr-kpi.is-active .val { color:#86efac; }
34 .pr-kpi.is-scheduled .val { color:#7dd3fc; }
35 .pr-kpi.is-paused .val { color:#fcd34d; }
36 .pr-kpi.is-expired .val { color:#fca5a5; }
37 .pr-table { width:100%; border-collapse:collapse; background:var(--col-surface-1); border:1px solid var(--col-border); border-radius:10px; overflow:hidden; }
38 .pr-table th, .pr-table td { text-align:left; padding:12px 14px; border-bottom:1px solid var(--col-border); font-size:13px; vertical-align:top; }
39 .pr-table th { background:var(--col-surface-2); color:var(--col-text-2); font-weight:600; font-size:11px; text-transform:uppercase; letter-spacing:0.06em; }
40 .pr-table th.num, .pr-table td.num { text-align:right; }
41 .pr-table tr:last-child td { border-bottom:none; }
42 .pr-table .name { color:#fff; font-weight:600; }
43 .pr-table .meta { color:var(--col-text-2); font-size:12px; margin-top:3px; }
44 .pr-table .actions { white-space:nowrap; }
45 .pr-table .actions a, .pr-table .actions button { background:none; border:none; color:var(--col-brand); font-size:13px; padding:0; cursor:pointer; }
46 .pr-table .actions a:hover, .pr-table .actions button:hover { text-decoration:underline; }
47 .pr-table .actions form { display:inline; }
48 .pr-table .actions .sep { color:var(--col-text-2); padding:0 6px; }
49 .pill { display:inline-block; padding:3px 8px; border-radius:99px; font-size:11px; font-weight:600; }
50 .pill.kind { background:rgba(125,211,252,0.12); color:#7dd3fc; }
51 .pill.kind.sale { background:rgba(252,211,77,0.12); color:#fcd34d; }
52 .pill.kind.bundle { background:rgba(167,139,250,0.12); color:#a78bfa; }
53 .pill.kind.flash { background:rgba(244,114,182,0.12); color:#f472b6; }
54 .pill.active { background:rgba(34,197,94,0.12); color:#86efac; }
55 .pill.paused { background:rgba(252,211,77,0.12); color:#fcd34d; }
56 .pill.scheduled { background:rgba(125,211,252,0.12); color:#7dd3fc; }
57 .pill.expired { background:rgba(239,68,68,0.12); color:#fca5a5; }
58 .pill.draft { background:rgba(148,163,184,0.12); color:#94a3b8; }
59 .pr-form { background:var(--col-surface-1); border:1px solid var(--col-border); border-radius:12px; padding:24px; }
60 .pr-form-grid { display:grid; grid-template-columns:1fr 1fr; gap:18px 24px; }
61 .pr-field { display:flex; flex-direction:column; gap:6px; }
62 .pr-field.full { grid-column:1 / -1; }
63 .pr-field label { font-size:12px; font-weight:600; color:var(--col-text-2); text-transform:uppercase; letter-spacing:0.05em; }
64 .pr-field input[type=text], .pr-field input[type=number], .pr-field input[type=datetime-local], .pr-field textarea, .pr-field select { background:var(--col-surface-2); border:1px solid var(--col-border); color:#fff; padding:10px 12px; border-radius:8px; font-size:14px; font-family:inherit; }
65 .pr-field textarea { min-height:70px; resize:vertical; }
66 .pr-radio-row { display:flex; gap:8px; flex-wrap:wrap; }
67 .pr-radio { display:inline-flex; align-items:center; gap:6px; padding:8px 14px; background:var(--col-surface-2); border:1px solid var(--col-border); border-radius:8px; font-size:13px; cursor:pointer; }
68 .pr-radio.is-on { background:rgba(125,211,252,0.10); border-color:rgba(125,211,252,0.40); color:#7dd3fc; }
69 .pr-radio input { accent-color:#7dd3fc; }
70 .hint { font-size:12px; color:var(--col-text-2); }
71 .pr-actions { display:flex; gap:10px; margin-top:22px; }
72</style>
73<nav class="pp-tabs">
74 <a class="pp-tab" href="/admin_packages.cgi">Packages</a>
75 <a class="pp-tab is-active" href="/admin_promotions.cgi">Promotions</a>
76</nav>
77
78[if:$tutorial_mode][if:$is_list]
79<div class="tutorial-banner">
80 <div class="tutorial-banner-icon"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><line x1="12" y1="16" x2="12" y2="12"/><line x1="12" y1="8" x2="12.01" y2="8"/></svg></div>
81 <div class="tutorial-banner-body">
82 <div class="tutorial-banner-title">Tutorial &middot; Sample data only</div>
83 <div class="tutorial-banner-text">Three example platform promotions are shown: a new-customer coupon (active, no time limit), a summer sale (scheduled), and a Black Friday flash sale (scheduled with a 5000-redemption cap).</div>
84 </div>
85 <a href="/admin_promotions.cgi" class="btn btn-secondary btn-sm">Exit sample view</a>
86</div>
87[/if][/if]
88
89[if:$has_flash]
90<div class="pr-flash $flash_kind">$flash_msg</div>
91[/if]
92
93[if:$schema_missing]
94<div class="an-banner" style="background:rgba(245,158,11,0.10);border-color:rgba(245,158,11,0.30)">
95 <strong>Promotions schema not yet installed.</strong> Run the promotions migration against the database.
96</div>
97[/if]
98
99[if:$is_list][if:$schema_ready]
100<div class="pr-kpis">
101 <div class="pr-kpi is-active">
102 <div class="lbl">Active</div>
103 <div class="val">$cnt_active</div>
104 </div>
105 <div class="pr-kpi is-scheduled">
106 <div class="lbl">Scheduled</div>
107 <div class="val">$cnt_scheduled</div>
108 </div>
109 <div class="pr-kpi is-paused">
110 <div class="lbl">Paused</div>
111 <div class="val">$cnt_paused</div>
112 </div>
113 <div class="pr-kpi is-expired">
114 <div class="lbl">Expired</div>
115 <div class="val">$cnt_expired</div>
116 </div>
117 <div class="pr-kpi">
118 <div class="lbl">Total redemptions</div>
119 <div class="val">$sum_redemptions</div>
120 </div>
121 <div class="pr-kpi">
122 <div class="lbl">Discount given</div>
123 <div class="val">$sum_discount_display</div>
124 </div>
125</div>
126
127[if:$has_rows]
128<table class="pr-table">
129 <thead>
130 <tr>
131 <th>Promotion</th>
132 <th>Kind</th>
133 <th>Discount</th>
134 <th>Window</th>
135 <th>Usage</th>
136 <th class="num">Discount given</th>
137 <th>Status</th>
138 <th>Actions</th>
139 </tr>
140 </thead>
141 <tbody>
142 [loop:@rows]
143 <tr>
144 <td>
145 <div class="name">$loop1.name</div>
146 <div class="meta">
147 [if:$loop1.code_present]Code: <code>$loop1.code</code>[/if]
148 [if:$loop1.slug_present][if:$loop1.code_present] &middot; [/if]Link: <code>/promo/$loop1.public_slug</code>[/if]
149 [if:$loop1.new_customers_only] &middot; <span style="color:#86efac">New customers only</span>[/if]
150 </div>
151 </td>
152 <td>
153 [if:$loop1.is_coupon]<span class="pill kind">Coupon</span>[/if]
154 [if:$loop1.is_sale]<span class="pill kind sale">Sale</span>[/if]
155 [if:$loop1.is_bundle]<span class="pill kind bundle">Bundle</span>[/if]
156 [if:$loop1.is_flash]<span class="pill kind flash">Flash sale</span>[/if]
157 </td>
158 <td>$loop1.discount_label</td>
159 <td>
160 [if:$loop1.has_window]
161 <div class="meta" style="font-size:12px;color:var(--col-text-2)">
162 <span class="ts" data-ts="$loop1.starts_epoch" data-fmt="date">$loop1.starts_at</span> <br>&rarr; <span class="ts" data-ts="$loop1.ends_epoch" data-fmt="date">$loop1.ends_at</span>
163 </div>
164 [/if]
165 [if:!$loop1.has_window]<span class="text-xs text-dim">No time limit</span>[/if]
166 </td>
167 <td>$loop1.cap_label</td>
168 <td class="num">$loop1.discount_given_display</td>
169 <td>
170 [if:$loop1.is_active]<span class="pill active">Active</span>[/if]
171 [if:$loop1.is_paused]<span class="pill paused">Paused</span>[/if]
172 [if:$loop1.is_scheduled]<span class="pill scheduled">Scheduled</span>[/if]
173 [if:$loop1.is_expired]<span class="pill expired">Expired</span>[/if]
174 [if:$loop1.is_draft]<span class="pill draft">Draft</span>[/if]
175 </td>
176 <td class="actions">
177 <a href="$loop1.edit_url">Edit</a>
178 [if:$loop1.is_active]
179 <span class="sep">&middot;</span>
180 <form method="POST" action="/admin_promotion_action.cgi">
181 <input type="hidden" name="act" value="pause">
182 <input type="hidden" name="id" value="$loop1.id">
183 <button type="submit">Pause</button>
184 </form>
185 [/if]
186 [if:$loop1.is_scheduled]
187 <span class="sep">&middot;</span>
188 <form method="POST" action="/admin_promotion_action.cgi">
189 <input type="hidden" name="act" value="pause">
190 <input type="hidden" name="id" value="$loop1.id">
191 <button type="submit">Pause</button>
192 </form>
193 [/if]
194 [if:$loop1.is_paused]
195 <span class="sep">&middot;</span>
196 <form method="POST" action="/admin_promotion_action.cgi">
197 <input type="hidden" name="act" value="activate">
198 <input type="hidden" name="id" value="$loop1.id">
199 <button type="submit">Activate</button>
200 </form>
201 [/if]
202 [if:$loop1.is_draft]
203 <span class="sep">&middot;</span>
204 <form method="POST" action="/admin_promotion_action.cgi">
205 <input type="hidden" name="act" value="activate">
206 <input type="hidden" name="id" value="$loop1.id">
207 <button type="submit">Activate</button>
208 </form>
209 [/if]
210 <span class="sep">&middot;</span>
211 <form method="POST" action="/admin_promotion_action.cgi"
212 onsubmit="return confirm('Delete this promotion permanently? Redemption history is also removed.');">
213 <input type="hidden" name="act" value="delete">
214 <input type="hidden" name="id" value="$loop1.id">
215 <button type="submit" style="color:#fca5a5">Delete</button>
216 </form>
217 </td>
218 </tr>
219 [/loop]
220 </tbody>
221</table>
222[/if]
223
224[if:!$has_rows]
225<div style="text-align:center;padding:60px 24px;background:var(--col-surface-1);border:1px solid var(--col-border);border-radius:12px">
226 <div class="text-sm fw-600" style="color:#fff;margin-bottom:6px">No platform promotions yet</div>
227 <div class="text-xs text-dim" style="margin-bottom:14px;max-width:380px;margin-left:auto;margin-right:auto">Create coupons or sales to bring new customers onto the platform.</div>
228 <a href="/admin_promotions.cgi?new=1" class="btn btn-primary btn-sm">Create your first platform promotion</a>
229</div>
230[/if]
231[/if][/if]
232
233[if:$is_new][if:$schema_ready]
234<form class="pr-form" method="POST" action="/admin_promotion_action.cgi">
235 <input type="hidden" name="act" value="create">
236 <div class="pr-form-grid">
237 <div class="pr-field full">
238 <label>Name</label>
239 <input type="text" name="name" required maxlength="160" placeholder="e.g. New customer 20% off first month">
240 </div>
241
242 <div class="pr-field full">
243 <label>Description</label>
244 <textarea name="description" maxlength="500"></textarea>
245 </div>
246
247 <div class="pr-field full">
248 <label>Kind</label>
249 <div class="pr-radio-row">
250 <label class="pr-radio is-on"><input type="radio" name="kind" value="coupon" checked> Coupon</label>
251 <label class="pr-radio"><input type="radio" name="kind" value="sale"> Sale</label>
252 <label class="pr-radio"><input type="radio" name="kind" value="flash_sale"> Flash sale</label>
253 </div>
254 </div>
255
256 <div class="pr-field full">
257 <label>Applies to plans</label>
258 <div class="pr-radio-row" data-applies-radio>
259 <label class="pr-radio is-on"><input type="radio" name="plan_scope" value="all" checked data-plan-scope> All paid plans</label>
260 <label class="pr-radio"><input type="radio" name="plan_scope" value="specific" data-plan-scope> Specific plan(s)</label>
261 </div>
262 <div class="hint" style="margin-top:6px">Platform promos always discount ContactForge subscriptions.</div>
263 [if:$has_plan_options]
264 <div data-plan-picker style="display:none;margin-top:10px;padding:10px 12px;border:1px solid var(--col-border);border-radius:8px;background:rgba(255,255,255,0.02)">
265 <div class="hint" style="margin:0 0 8px">Check every plan this code should be redeemable on. Leave all unchecked to fall back to "All paid plans".</div>
266 [loop:@plan_options]
267 <label class="pr-radio" style="display:flex;align-items:center;gap:8px;margin-bottom:6px">
268 <input type="checkbox" name="plan_ids" value="[$loop1.id]"> <strong>[$loop1.display_name]</strong> &mdash; [$loop1.price_display] / mo
269 </label>
270 [/loop]
271 </div>
272 [/if]
273 </div>
274
275 <div class="pr-field">
276 <label>Discount type</label>
277 <select name="discount_type">
278 <option value="percent">Percent off</option>
279 <option value="fixed_amount">Fixed amount off</option>
280 </select>
281 </div>
282
283 <div class="pr-field">
284 <label>Discount value</label>
285 <div style="display:flex;gap:8px;align-items:center">
286 <input type="number" name="discount_value_pct" min="0" max="100" step="1" placeholder="0" style="width:80px">
287 <span class="hint">% off</span>
288 <span class="hint">or</span>
289 <input type="number" name="discount_value_amt" min="0" step="0.01" placeholder="0.00" style="width:100px">
290 <span class="hint">\$ off</span>
291 </div>
292 </div>
293
294 <div class="pr-field">
295 <label>Coupon code</label>
296 <input type="text" name="code" maxlength="64" placeholder="NEWBIE20" style="text-transform:uppercase">
297 </div>
298
299 <div class="pr-field">
300 <label>Public slug</label>
301 <input type="text" name="public_slug" maxlength="64" placeholder="newbie20" style="text-transform:lowercase">
302 </div>
303
304 <div class="pr-field">
305 <label>Starts at</label>
306 <input type="datetime-local" name="starts_at_local">
307 </div>
308
309 <div class="pr-field">
310 <label>Ends at</label>
311 <input type="datetime-local" name="ends_at_local">
312 </div>
313
314 <div class="pr-field">
315 <label>Max total redemptions</label>
316 <input type="number" name="max_redemptions" min="0" placeholder="Unlimited">
317 </div>
318
319 <div class="pr-field">
320 <label>Max per customer</label>
321 <input type="number" name="max_per_user" min="1" value="1">
322 </div>
323
324 <div class="pr-field">
325 <label>Minimum order total</label>
326 <input type="number" name="minimum_order" min="0" step="0.01" placeholder="0.00">
327 </div>
328
329 <div class="pr-field">
330 <label>New customers only</label>
331 <label class="pr-radio" style="display:inline-flex"><input type="checkbox" name="new_customers_only" value="1"> Only first-time subscribers</label>
332 </div>
333 </div>
334
335 <div class="pr-actions">
336 <button type="submit" name="publish_now" value="1" class="btn btn-primary">Create &amp; activate</button>
337 <button type="submit" class="btn btn-secondary">Save as draft</button>
338 </div>
339</form>
340[/if][/if]
341
342[if:$is_edit][if:$schema_ready][if:$has_edit]
343<form class="pr-form" method="POST" action="/admin_promotion_action.cgi">
344 <input type="hidden" name="act" value="update">
345 <input type="hidden" name="id" value="$edit.id">
346 <div class="pr-form-grid">
347 <div class="pr-field full">
348 <label>Name</label>
349 <input type="text" name="name" required maxlength="160" value="$edit.name">
350 </div>
351
352 <div class="pr-field full">
353 <label>Description</label>
354 <textarea name="description" maxlength="500">$edit.description</textarea>
355 </div>
356
357 <div class="pr-field full">
358 <label>Kind</label>
359 <div class="pr-radio-row">
360 <label class="pr-radio[if:$edit.is_coupon] is-on[/if]"><input type="radio" name="kind" value="coupon"[if:$edit.is_coupon] checked[/if]> Coupon</label>
361 <label class="pr-radio[if:$edit.is_sale] is-on[/if]"><input type="radio" name="kind" value="sale"[if:$edit.is_sale] checked[/if]> Sale</label>
362 <label class="pr-radio[if:$edit.is_flash] is-on[/if]"><input type="radio" name="kind" value="flash_sale"[if:$edit.is_flash] checked[/if]> Flash sale</label>
363 </div>
364 </div>
365
366 <div class="pr-field full">
367 <label>Applies to plans</label>
368 <div class="pr-radio-row" data-applies-radio>
369 <label class="pr-radio[if:$edit.applies_all_plans] is-on[/if]"><input type="radio" name="plan_scope" value="all"[if:$edit.applies_all_plans] checked[/if] data-plan-scope> All paid plans</label>
370 <label class="pr-radio[if:$edit.applies_specific] is-on[/if]"><input type="radio" name="plan_scope" value="specific"[if:$edit.applies_specific] checked[/if] data-plan-scope> Specific plan(s)</label>
371 </div>
372 [if:$has_plan_options]
373 <div data-plan-picker style="margin-top:10px;padding:10px 12px;border:1px solid var(--col-border);border-radius:8px;background:rgba(255,255,255,0.02)">
374 <div class="hint" style="margin:0 0 8px">Check every plan this code should be redeemable on. Leave all unchecked to fall back to "All paid plans".</div>
375 [loop:@plan_options]
376 <label class="pr-radio" style="display:flex;align-items:center;gap:8px;margin-bottom:6px">
377 <input type="checkbox" name="plan_ids" value="[$loop1.id]" [$loop1.checked]> <strong>[$loop1.display_name]</strong> &mdash; [$loop1.price_display] / mo
378 </label>
379 [/loop]
380 </div>
381 [/if]
382 </div>
383
384 <div class="pr-field">
385 <label>Discount type</label>
386 <select name="discount_type">
387 <option value="percent"[if:$edit.is_dtype_pct] selected[/if]>Percent off</option>
388 <option value="fixed_amount"[if:$edit.is_dtype_amt] selected[/if]>Fixed amount off</option>
389 </select>
390 </div>
391
392 <div class="pr-field">
393 <label>Discount value</label>
394 <div style="display:flex;gap:8px;align-items:center">
395 <input type="number" name="discount_value_pct" min="0" max="100" step="1" value="$edit.discount_value_pct" style="width:80px">
396 <span class="hint">% off</span>
397 <span class="hint">or</span>
398 <input type="number" name="discount_value_amt" min="0" step="0.01" value="$edit.discount_value_amt" style="width:100px">
399 <span class="hint">\$ off</span>
400 </div>
401 </div>
402
403 <div class="pr-field">
404 <label>Coupon code</label>
405 <input type="text" name="code" maxlength="64" value="$edit.code" style="text-transform:uppercase">
406 </div>
407
408 <div class="pr-field">
409 <label>Public slug</label>
410 <input type="text" name="public_slug" maxlength="64" value="$edit.public_slug" style="text-transform:lowercase">
411 </div>
412
413 <div class="pr-field">
414 <label>Starts at</label>
415 <input type="datetime-local" name="starts_at_local" value="$edit.starts_at_local">
416 </div>
417
418 <div class="pr-field">
419 <label>Ends at</label>
420 <input type="datetime-local" name="ends_at_local" value="$edit.ends_at_local">
421 </div>
422
423 <div class="pr-field">
424 <label>Max total redemptions</label>
425 <input type="number" name="max_redemptions" min="0" value="$edit.max_redemptions" placeholder="Unlimited">
426 </div>
427
428 <div class="pr-field">
429 <label>Max per customer</label>
430 <input type="number" name="max_per_user" min="1" value="$edit.max_per_user">
431 </div>
432
433 <div class="pr-field">
434 <label>Minimum order total</label>
435 <input type="number" name="minimum_order" min="0" step="0.01" value="$edit.minimum_order">
436 </div>
437
438 <div class="pr-field">
439 <label>New customers only</label>
440 <label class="pr-radio" style="display:inline-flex"><input type="checkbox" name="new_customers_only" value="1"[if:$edit.new_customers_only] checked[/if]> Only first-time subscribers</label>
441 </div>
442 </div>
443
444 <div class="pr-actions">
445 <button type="submit" class="btn btn-primary">Save changes</button>
446 <a href="/admin_promotions.cgi" class="btn btn-secondary">Cancel</a>
447 </div>
448</form>
449[/if][/if][/if]
450
451<script>
452 (function () {
453 document.querySelectorAll('form').forEach(function (form) {
454 var radios = form.querySelectorAll('[data-plan-scope]');
455 var picker = form.querySelector('[data-plan-picker]');
456 if (!radios.length || !picker) return;
457 function sync() {
458 var sel = form.querySelector('[data-plan-scope]:checked');
459 picker.style.display = (sel && sel.value === 'specific') ? '' : 'none';
460 if (!sel || sel.value !== 'specific') {
461 picker.querySelectorAll('input[type=checkbox]').forEach(function (cb) { cb.checked = false; });
462 }
463 }
464 radios.forEach(function (r) { r.addEventListener('change', sync); });
465 sync();
466 });
467 })();
468</script>
469
470<div class="adm-foot-spacer" data-end="admin-promotions">&nbsp;</div>