| 1 |
<div class="pricing-strategies"> |
| 2 |
<div class="pricing-strategies-title">Pricing strategies</div> |
| 3 |
<div class="pricing-strategies-grid"> |
| 4 |
<div><strong>Fixed price</strong>: set in the Details tab. Good for finished products.</div> |
| 5 |
<div><strong>Pay What You Want</strong>: fans choose their price. Set a minimum below.</div> |
| 6 |
<div><strong>Free + promo codes</strong>: set price to $0, then generate codes for specific access.</div> |
| 7 |
<div><strong>License keys</strong>: per-seat licensing for software. Works with any pricing model.</div> |
| 8 |
</div> |
| 9 |
</div> |
| 10 |
|
| 11 |
|
| 12 |
<div class="content-section"> |
| 13 |
<div class="section-header"> |
| 14 |
<h2 class="subsection-title">Pay What You Want</h2> |
| 15 |
</div> |
| 16 |
|
| 17 |
<form hx-put="/api/items/{{ item.id }}" |
| 18 |
hx-target="#pwyw-save-status" |
| 19 |
hx-swap="innerHTML" |
| 20 |
hx-indicator="#pwyw-spinner"> |
| 21 |
<div class="form-group"> |
| 22 |
<label class="checkbox-label"> |
| 23 |
<input type="hidden" name="pwyw_enabled" value="off"> |
| 24 |
<input type="checkbox" name="pwyw_enabled" value="on" |
| 25 |
{% if item.pwyw_enabled %}checked{% endif %} |
| 26 |
onchange="document.getElementById('pwyw-settings').classList.toggle('hidden', !this.checked)"> |
| 27 |
Enable pay-what-you-want pricing |
| 28 |
</label> |
| 29 |
<p class="form-hint">The item price becomes the suggested amount. Buyers can choose any amount at or above the minimum.</p> |
| 30 |
</div> |
| 31 |
|
| 32 |
<div id="pwyw-settings"{% if !item.pwyw_enabled %} class="hidden"{% endif %}> |
| 33 |
<div class="form-group"> |
| 34 |
<label for="pwyw-min-dollars">Minimum price ($)</label> |
| 35 |
<input type="number" id="pwyw-min-dollars" step="0.01" min="0" placeholder="0.00"> |
| 36 |
<input type="hidden" id="pwyw-min-cents" name="pwyw_min_cents" |
| 37 |
value="{% if let Some(min) = item.pwyw_min_cents %}{{ min }}{% endif %}"> |
| 38 |
<p class="form-hint">The lowest amount a buyer can pay. $0 = any amount.</p> |
| 39 |
</div> |
| 40 |
</div> |
| 41 |
<script> |
| 42 |
(function() { |
| 43 |
var dollars = document.getElementById('pwyw-min-dollars'); |
| 44 |
var cents = document.getElementById('pwyw-min-cents'); |
| 45 |
if (cents.value) dollars.value = (parseInt(cents.value, 10) / 100).toFixed(2); |
| 46 |
dollars.addEventListener('input', function() { |
| 47 |
cents.value = Math.round(parseFloat(this.value || 0) * 100); |
| 48 |
}); |
| 49 |
})(); |
| 50 |
</script> |
| 51 |
|
| 52 |
<button class="btn-primary" type="submit"> |
| 53 |
Save PWYW Settings |
| 54 |
<span id="pwyw-spinner" class="htmx-indicator"> ...</span> |
| 55 |
</button> |
| 56 |
<span id="pwyw-save-status"></span> |
| 57 |
</form> |
| 58 |
</div> |
| 59 |
|
| 60 |
|
| 61 |
<div class="content-section"> |
| 62 |
<div class="section-header"> |
| 63 |
<h2 class="subsection-title">License Terms</h2> |
| 64 |
</div> |
| 65 |
|
| 66 |
<form hx-put="/api/items/{{ item.id }}/license-settings" |
| 67 |
hx-target="#license-save-status" |
| 68 |
hx-swap="innerHTML" |
| 69 |
hx-indicator="#license-spinner"> |
| 70 |
<div class="form-group"> |
| 71 |
<label for="dash-license-preset">License</label> |
| 72 |
<select id="dash-license-preset" name="license_preset" |
| 73 |
onchange="document.getElementById('dash-custom-license').classList.toggle('hidden', this.value !== 'custom')"> |
| 74 |
<option value="">None (no license)</option> |
| 75 |
{% for opt in license_preset_options %} |
| 76 |
<option value="{{ opt.0 }}"{% if item.license_preset.as_deref() == Some(opt.0) %} selected{% endif %}>{{ opt.1 }}</option> |
| 77 |
{% endfor %} |
| 78 |
</select> |
| 79 |
<p class="form-hint">Displayed on the item page and included with downloads as LICENSE.txt.</p> |
| 80 |
</div> |
| 81 |
|
| 82 |
<div id="dash-custom-license"{% if item.license_preset.as_deref() != Some("custom") %} class="hidden"{% endif %}> |
| 83 |
<div class="form-group"> |
| 84 |
<label for="dash-custom-text">Custom License Text</label> |
| 85 |
<textarea id="dash-custom-text" name="custom_license_text" rows="6" |
| 86 |
placeholder="Enter your custom license terms...">{{ item.custom_license_text.as_deref().unwrap_or("") }}</textarea> |
| 87 |
</div> |
| 88 |
</div> |
| 89 |
|
| 90 |
<div class="form-group"> |
| 91 |
<label class="checkbox-label"> |
| 92 |
<input type="checkbox" name="enable_license_keys" value="on" |
| 93 |
{% if item.enable_license_keys %}checked{% endif %} |
| 94 |
onchange="document.getElementById('license-keys-section').classList.toggle('hidden', !this.checked)"> |
| 95 |
Enable license keys for this item |
| 96 |
</label> |
| 97 |
<p class="form-hint">Buyers receive a unique license key with their purchase. Each key can be activated on a limited number of devices (you choose). Useful for software with per-seat licensing. Works with any pricing model including Pay What You Want.</p> |
| 98 |
</div> |
| 99 |
|
| 100 |
<div class="form-group"> |
| 101 |
<label for="default-max-activations">Default max activations per key</label> |
| 102 |
<input type="number" id="default-max-activations" name="default_max_activations" |
| 103 |
value="{% if let Some(max) = item.default_max_activations %}{{ max }}{% endif %}" |
| 104 |
min="1" placeholder="Leave blank for unlimited"> |
| 105 |
<p class="form-hint">How many machines can activate each key. Blank = unlimited.</p> |
| 106 |
</div> |
| 107 |
|
| 108 |
<button class="btn-primary" type="submit"> |
| 109 |
Save License Settings |
| 110 |
<span id="license-spinner" class="htmx-indicator"> ...</span> |
| 111 |
</button> |
| 112 |
<span id="license-save-status"></span> |
| 113 |
</form> |
| 114 |
|
| 115 |
<div id="license-keys-section" class="license-keys-section{% if !item.enable_license_keys %} hidden{% endif %}"> |
| 116 |
<div class="section-header"> |
| 117 |
<h3>Generated Keys</h3> |
| 118 |
<form hx-post="/api/items/{{ item.id }}/keys" |
| 119 |
hx-target="#license-keys-list" |
| 120 |
hx-swap="outerHTML"> |
| 121 |
<button class="btn-secondary" type="submit">Generate Key</button> |
| 122 |
</form> |
| 123 |
</div> |
| 124 |
|
| 125 |
{% include "partials/item_license_keys.html" %} |
| 126 |
</div> |
| 127 |
</div> |
| 128 |
|
| 129 |
|
| 130 |
<div class="content-section"> |
| 131 |
<div class="section-header"> |
| 132 |
<h2 class="subsection-title">Promo Codes</h2> |
| 133 |
</div> |
| 134 |
<p class="form-hint promo-codes-hint">Generate codes that grant free access to this item. Share them with reviewers, collaborators, or for promotions.</p> |
| 135 |
|
| 136 |
<form hx-post="/api/promo-codes" |
| 137 |
hx-target="#promo-codes-list" |
| 138 |
hx-swap="outerHTML" |
| 139 |
class="promo-codes-form"> |
| 140 |
<input type="hidden" name="item_id" value="{{ item.id }}"> |
| 141 |
<input type="hidden" name="code_purpose" value="free_access"> |
| 142 |
<div class="promo-codes-row"> |
| 143 |
<input type="number" name="max_uses" min="1" placeholder="Max uses (blank = unlimited)" |
| 144 |
class="promo-max-uses"> |
| 145 |
<input type="date" name="expires_at" placeholder="Expires (optional)" |
| 146 |
title="Expiry date (optional)" |
| 147 |
class="promo-expires"> |
| 148 |
<button class="btn-secondary" type="submit">Generate Code</button> |
| 149 |
</div> |
| 150 |
</form> |
| 151 |
|
| 152 |
{% include "partials/promo_codes_list.html" %} |
| 153 |
</div> |
| 154 |
|