eb7fa98426
CI / changes (pull_request) Successful in 2s
CI / unit (pull_request) Successful in 12s
CI / integration (pull_request) Successful in 24s
CI / ui (pull_request) Has been skipped
CI / conformance (pull_request) Successful in 10s
CI / gate (pull_request) Successful in 1s
CI / deploy (pull_request) Successful in 1m57s
The "watch for chips" rewarded payout and its per-day / per-hour caps live in the shared payments.config row and had no admin surface — they could only be changed by SQL, contrary to D32 (the rewarded rate is meant to be admin-configurable). Add a "Rewarded ads" form on the /_gm/catalog page: RewardConfig / SetRewardConfig on the service (non-negative validated), a setRewardConfig store writer (the singleton config row), the consoleSetReward handler + POST /_gm/catalog/reward route, and the form pre-filled from the current config. No migration — the columns already exist. The reward rate is a config value, not a sellable catalog atom (so a "no-ads forever" style product is still out of scope by D32/D33). Test: an integration test sets the config, checks the page pre-fill, and refuses a negative value.
55 lines
3.8 KiB
Go Template
55 lines
3.8 KiB
Go Template
{{define "content" -}}
|
||
<h1>Product catalog</h1>
|
||
{{with .Data}}
|
||
<p class="note">A <strong>pack</strong> funds chips (a money price per rail — RUB via direct, VOTE via vk, XTR via telegram); a <strong>value</strong> buys benefits with chips (a CHIP price). Archived products are hidden from players but still credit an in-flight payment and can be granted. A product with transactions can only be archived, not deleted. Amounts are in minor units (RUB kopecks; VOTE/XTR/CHIP whole). The <code>tournament</code> atom is not sellable yet — keep such a product archived.</p>
|
||
<section class="panel"><h2>Add product</h2>
|
||
<form class="form col" method="post" action="/_gm/catalog">
|
||
<label>Title <input type="text" name="title" maxlength="120" required></label>
|
||
<fieldset><legend>Atoms (quantity; blank = none)</legend>
|
||
<label>Chips <input type="number" name="chips" min="0"></label>
|
||
<label>Hints <input type="number" name="hints" min="0"></label>
|
||
<label>No-ads days <input type="number" name="noads" min="0"></label>
|
||
<label>Tournament <input type="number" name="tournament" min="0"></label>
|
||
</fieldset>
|
||
<fieldset><legend>Prices (minor units; blank = none)</legend>
|
||
<label>RUB — direct (kopecks) <input type="number" name="price_rub" min="0"></label>
|
||
<label>VOTE — vk <input type="number" name="price_vote" min="0"></label>
|
||
<label>XTR — telegram <input type="number" name="price_star" min="0"></label>
|
||
<label>CHIP — value <input type="number" name="price_chip" min="0"></label>
|
||
</fieldset>
|
||
<label><input type="checkbox" name="active" value="true"> Active (on sale)</label>
|
||
<div><button type="submit">Add</button></div>
|
||
</form>
|
||
</section>
|
||
<section class="panel"><h2>Rewarded ads (watch for chips)</h2>
|
||
<p class="note">The chips a player earns per rewarded-video view (VK only), plus the per-day and per-hour caps that bound free chips — <strong>0 payout turns rewarded off</strong>. This is the shared reward config, not a product.</p>
|
||
<form class="form col" method="post" action="/_gm/catalog/reward">
|
||
<label>Chips per view <input type="number" name="reward_payout" min="0" value="{{.RewardPayout}}"></label>
|
||
<label>Daily cap <input type="number" name="reward_daily" min="0" value="{{.RewardDailyCap}}"></label>
|
||
<label>Hourly cap <input type="number" name="reward_hourly" min="0" value="{{.RewardHourlyCap}}"></label>
|
||
<div><button type="submit">Save</button></div>
|
||
</form>
|
||
</section>
|
||
<section class="panel"><h2>Products</h2>
|
||
<p class="note">Showing {{if .ShowAll}}<strong>all</strong> products (active + archived) — <a href="/_gm/catalog">active only</a>{{else}}<strong>active</strong> products — <a href="/_gm/catalog?all=1">show all (incl. archived)</a>{{end}}.</p>
|
||
<table class="list">
|
||
<thead><tr><th>Title</th><th>Status</th><th>Atoms</th><th>Prices</th><th></th></tr></thead>
|
||
<tbody>
|
||
{{range .Products}}
|
||
<tr>
|
||
<td><a href="/_gm/catalog/{{.ID}}">{{.Title}}</a></td>
|
||
<td>{{if .Active}}<span class="ok">active</span>{{else}}<span class="warn">archived</span>{{end}}{{if .Transacted}} <span class="pill">transacted</span>{{end}}</td>
|
||
<td>{{range .Atoms}}<code>{{.Atom}}×{{.Quantity}}</code> {{end}}</td>
|
||
<td>{{range .Prices}}<code>{{.Currency}}{{if .Method}}/{{.Method}}{{end}} {{.Amount}}</code> {{end}}</td>
|
||
<td class="row-actions">
|
||
<form class="form" method="post" action="/_gm/catalog/{{.ID}}/archive"><input type="hidden" name="active" value="{{if .Active}}false{{else}}true{{end}}"><button type="submit">{{if .Active}}Archive{{else}}Unarchive{{end}}</button></form>
|
||
{{if not .Transacted}}<form class="form" method="post" action="/_gm/catalog/{{.ID}}/delete" onsubmit="return confirm('Delete this product? It has never been transacted, so this is safe and permanent.')"><button type="submit">Delete</button></form>{{end}}
|
||
</td>
|
||
</tr>
|
||
{{else}}<tr><td colspan="5"><span class="note">no products</span></td></tr>{{end}}
|
||
</tbody>
|
||
</table>
|
||
</section>
|
||
{{end}}
|
||
{{- end}}
|