06/16 Phase 6 (continue)
This commit is contained in:
@@ -5,7 +5,11 @@
|
||||
<a href="{{ url_for('admin.reports') }}" class="{{ 'on' if request.endpoint == 'admin.reports' else '' }}">{{ _('Reports') }}</a>
|
||||
<a href="{{ url_for('admin.categories') }}" class="{{ 'on' if request.endpoint in ('admin.categories', 'admin.toggle_category', 'admin.category_schema') else '' }}">{{ _('Categories') }}</a>
|
||||
<a href="{{ url_for('admin.plans') }}" class="{{ 'on' if request.endpoint in ('admin.plans', 'admin.plan_edit') else '' }}">{{ _('Plans') }}</a>
|
||||
<a href="{{ url_for('admin.admin_ads') }}" class="{{ 'on' if request.endpoint in ('admin.admin_ads', 'admin.admin_ad_new', 'admin.admin_ad_edit') else '' }}">{{ _('Ads') }}</a>
|
||||
<a href="{{ url_for('admin.admin_sponsors') }}" class="{{ 'on' if request.endpoint in ('admin.admin_sponsors', 'admin.admin_sponsor_new', 'admin.admin_sponsor_edit') else '' }}">{{ _('Sponsors') }}</a>
|
||||
<a href="{{ url_for('admin.admin_promoted_keywords') }}" class="{{ 'on' if request.endpoint in ('admin.admin_promoted_keywords', 'admin.admin_promoted_keyword_new') else '' }}">{{ _('Keywords') }}</a>
|
||||
<a href="{{ url_for('admin.transactions') }}" class="{{ 'on' if request.endpoint == 'admin.transactions' else '' }}">{{ _('Transactions') }}</a>
|
||||
<a href="{{ url_for('admin.analytics') }}" class="{{ 'on' if request.endpoint == 'admin.analytics' else '' }}">{{ _('Analytics') }}</a>
|
||||
<a href="{{ url_for('admin.audit_log') }}" class="{{ 'on' if request.endpoint == 'admin.audit_log' else '' }}">{{ _('Audit log') }}</a>
|
||||
<a href="{{ url_for('admin.settings') }}" class="{{ 'on' if request.endpoint == 'admin.settings' else '' }}">{{ _('Settings') }}</a>
|
||||
</nav>
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}{{ _('Admin · %(t)s', t=_('Edit ad') if ad else _('New ad')) }}{% endblock %}
|
||||
{% block content %}
|
||||
{% include "admin/_nav.html" %}
|
||||
<div class="card">
|
||||
<h2>{{ _('Edit ad') if ad else _('New ad') }}</h2>
|
||||
{% if error %}<p class="alert danger">{{ error }}</p>{% endif %}
|
||||
|
||||
<form method="post" class="settings-panel">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||||
|
||||
<div class="field">
|
||||
<label>{{ _('Advertiser name') }} *</label>
|
||||
<input class="input" name="advertiser_name" required
|
||||
value="{{ ad.advertiser_name if ad else '' }}">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>{{ _('Slot') }} *</label>
|
||||
<select class="input" name="slot" required>
|
||||
{% for s in ['header', 'sidebar', 'inline', 'footer'] %}
|
||||
<option value="{{ s }}" {{ 'selected' if ad and ad.slot == s }}>{{ s }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>{{ _('Target URL') }} *</label>
|
||||
<input class="input" name="target_url" required
|
||||
value="{{ ad.target_url if ad else '' }}">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>{{ _('Creative path (relative under media/ads/)') }}</label>
|
||||
<input class="input" name="creative_path"
|
||||
value="{{ ad.creative_path or '' if ad else '' }}"
|
||||
placeholder="{{ _('e.g. banner.jpg — leave blank for text-only ad') }}">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>{{ _('Alt text') }}</label>
|
||||
<input class="input" name="alt_text" value="{{ ad.alt_text or '' if ad else '' }}">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>{{ _('Language targeting (blank = all)') }}</label>
|
||||
<select class="input" name="lang">
|
||||
<option value="">{{ _('All') }}</option>
|
||||
{% for l in ['en', 'vi', 'es'] %}
|
||||
<option value="{{ l }}" {{ 'selected' if ad and ad.lang == l }}>{{ l }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>{{ _('State targeting (2-letter, blank = all)') }}</label>
|
||||
<input class="input" name="geo_state" maxlength="2"
|
||||
value="{{ ad.geo_state or '' if ad else '' }}" placeholder="CA">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>{{ _('Starts at (YYYY-MM-DD)') }}</label>
|
||||
<input class="input" name="starts_at" type="date"
|
||||
value="{{ ad.starts_at.strftime('%Y-%m-%d') if ad else '' }}">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>{{ _('Ends at (YYYY-MM-DD)') }}</label>
|
||||
<input class="input" name="ends_at" type="date"
|
||||
value="{{ ad.ends_at.strftime('%Y-%m-%d') if ad else '' }}">
|
||||
</div>
|
||||
<div class="check">
|
||||
<input type="checkbox" name="is_active" id="is_active"
|
||||
{{ 'checked' if (not ad or ad.is_active) }}>
|
||||
<label for="is_active">{{ _('Active') }}</label>
|
||||
</div>
|
||||
|
||||
<div class="billing-actions">
|
||||
<button class="btn" type="submit">{{ _('Save') }}</button>
|
||||
<a class="btn ghost" href="{{ url_for('admin.admin_ads') }}">{{ _('Cancel') }}</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,64 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}{{ _('Admin · Ads') }}{% endblock %}
|
||||
{% block content %}
|
||||
{% include "admin/_nav.html" %}
|
||||
<div class="card">
|
||||
<div style="display:flex;justify-content:space-between;align-items:center">
|
||||
<h2>{{ _('Ads') }}</h2>
|
||||
<a class="btn" href="{{ url_for('admin.admin_ad_new') }}">{{ _('+ New ad') }}</a>
|
||||
</div>
|
||||
|
||||
{% if not ad_list %}<p class="muted">{{ _('No ads yet.') }}</p>{% endif %}
|
||||
<table class="list">
|
||||
<thead><tr>
|
||||
<th>{{ _('Advertiser') }}</th>
|
||||
<th>{{ _('Slot') }}</th>
|
||||
<th>{{ _('Targeting') }}</th>
|
||||
<th>{{ _('Schedule') }}</th>
|
||||
<th>{{ _('Imp.') }}</th>
|
||||
<th>{{ _('Clicks') }}</th>
|
||||
<th>{{ _('CTR') }}</th>
|
||||
<th>{{ _('Status') }}</th>
|
||||
<th></th>
|
||||
</tr></thead>
|
||||
{% for ad in ad_list %}
|
||||
<tr>
|
||||
<td><a href="{{ url_for('admin.admin_ad_edit', ad_id=ad.id) }}">{{ ad.advertiser_name }}</a></td>
|
||||
<td><span class="badge cat">{{ ad.slot }}</span></td>
|
||||
<td class="muted small">
|
||||
{% if ad.lang %}lang={{ ad.lang }}{% endif %}
|
||||
{% if ad.geo_state %} state={{ ad.geo_state }}{% endif %}
|
||||
{% if not ad.lang and not ad.geo_state %}—{% endif %}
|
||||
</td>
|
||||
<td class="muted small">
|
||||
{{ ad.starts_at.strftime('%Y-%m-%d') }} –
|
||||
{{ ad.ends_at.strftime('%Y-%m-%d') }}
|
||||
</td>
|
||||
<td>{{ ad.impressions }}</td>
|
||||
<td>{{ ad.clicks }}</td>
|
||||
<td>{{ ad.ctr }}%</td>
|
||||
<td>
|
||||
{% if ad.is_running %}
|
||||
<span class="badge ok">{{ _('Live') }}</span>
|
||||
{% elif ad.is_active %}
|
||||
<span class="badge cat">{{ _('Scheduled') }}</span>
|
||||
{% else %}
|
||||
<span class="badge warn">{{ _('Off') }}</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<form method="post" action="{{ url_for('admin.admin_ad_toggle', ad_id=ad.id) }}" style="display:inline">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||||
<button class="btn ghost tiny" type="submit">{{ _('Toggle') }}</button>
|
||||
</form>
|
||||
<form method="post" action="{{ url_for('admin.admin_ad_delete', ad_id=ad.id) }}" style="display:inline"
|
||||
onsubmit="return confirm('{{ _('Delete this ad?') }}');">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||||
<button class="btn danger tiny" type="submit">{{ _('Del') }}</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,108 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}{{ _('Admin · Analytics') }}{% endblock %}
|
||||
{% block content %}
|
||||
{% include "admin/_nav.html" %}
|
||||
|
||||
<div class="card">
|
||||
<h2>{{ _('Analytics — last 30 days') }}</h2>
|
||||
|
||||
<div class="kpi-grid">
|
||||
<div class="card kpi-card">
|
||||
<div class="kpi-label">{{ _('New signups (30d)') }}</div>
|
||||
<div class="kpi-value">{{ signups|sum(attribute='n') }}</div>
|
||||
</div>
|
||||
<div class="card kpi-card">
|
||||
<div class="kpi-label">{{ _('Listings posted (30d)') }}</div>
|
||||
<div class="kpi-value">{{ listings_chart|sum(attribute='n') }}</div>
|
||||
</div>
|
||||
<div class="card kpi-card">
|
||||
<div class="kpi-label">{{ _('Revenue (30d)') }}</div>
|
||||
<div class="kpi-value">${{ '%.2f'|format((revenue_chart|sum(attribute='cents') or 0) / 100) }}</div>
|
||||
</div>
|
||||
<div class="card kpi-card">
|
||||
<div class="kpi-label">{{ _('Ad impressions (active ads)') }}</div>
|
||||
<div class="kpi-value">{{ ad_stats.total_impressions or 0 }}</div>
|
||||
</div>
|
||||
<div class="card kpi-card">
|
||||
<div class="kpi-label">{{ _('Ad clicks (active ads)') }}</div>
|
||||
<div class="kpi-value">{{ ad_stats.total_clicks or 0 }}</div>
|
||||
</div>
|
||||
<div class="card kpi-card">
|
||||
<div class="kpi-label">{{ _('Overall CTR') }}</div>
|
||||
<div class="kpi-value">
|
||||
{% if ad_stats.total_impressions %}
|
||||
{{ '%.2f'|format(ad_stats.total_clicks / ad_stats.total_impressions * 100) }}%
|
||||
{% else %}—{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h3>{{ _('Signups per day') }}</h3>
|
||||
{% if signups %}
|
||||
<table class="list">
|
||||
<thead><tr><th>{{ _('Date') }}</th><th>{{ _('Signups') }}</th></tr></thead>
|
||||
{% for row in signups %}
|
||||
<tr>
|
||||
<td class="muted small">{{ row.day }}</td>
|
||||
<td>{{ row.n }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% else %}
|
||||
<p class="muted">{{ _('No signups in the last 30 days.') }}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h3>{{ _('Listings posted per day') }}</h3>
|
||||
{% if listings_chart %}
|
||||
<table class="list">
|
||||
<thead><tr><th>{{ _('Date') }}</th><th>{{ _('Listings') }}</th></tr></thead>
|
||||
{% for row in listings_chart %}
|
||||
<tr>
|
||||
<td class="muted small">{{ row.day }}</td>
|
||||
<td>{{ row.n }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% else %}
|
||||
<p class="muted">{{ _('No listings posted in the last 30 days.') }}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h3>{{ _('Revenue per day') }}</h3>
|
||||
{% if revenue_chart %}
|
||||
<table class="list">
|
||||
<thead><tr><th>{{ _('Date') }}</th><th>{{ _('Revenue') }}</th></tr></thead>
|
||||
{% for row in revenue_chart %}
|
||||
<tr>
|
||||
<td class="muted small">{{ row.day }}</td>
|
||||
<td>${{ '%.2f'|format((row.cents or 0) / 100) }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% else %}
|
||||
<p class="muted">{{ _('No revenue in the last 30 days.') }}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<h3>{{ _('Top categories by active listings') }}</h3>
|
||||
{% if top_cats %}
|
||||
<table class="list">
|
||||
<thead><tr><th>{{ _('Category') }}</th><th>{{ _('Active listings') }}</th></tr></thead>
|
||||
{% for name, n in top_cats %}
|
||||
<tr>
|
||||
<td>{{ name }}</td>
|
||||
<td>{{ n }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% else %}
|
||||
<p class="muted">{{ _('No listings.') }}</p>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,37 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}{{ _('Admin · Assign keyword') }}{% endblock %}
|
||||
{% block content %}
|
||||
{% include "admin/_nav.html" %}
|
||||
<div class="card">
|
||||
<h2>{{ _('Assign promoted keyword') }}</h2>
|
||||
{% if error %}<p class="alert danger">{{ error }}</p>{% endif %}
|
||||
<p class="muted">
|
||||
{{ _('The listing will appear at the top of search results when the keyword is matched (accent-insensitive).') }}
|
||||
</p>
|
||||
<form method="post" class="settings-panel">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||||
<div class="field">
|
||||
<label>{{ _('Listing ID') }} *</label>
|
||||
<input class="input" name="listing_id" type="number" required min="1"
|
||||
placeholder="{{ _('Enter the numeric listing ID') }}">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>{{ _('Keyword') }} *</label>
|
||||
<input class="input" name="keyword" required maxlength="80"
|
||||
placeholder="{{ _('e.g. pho, cleaning service') }}">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>{{ _('Priority (higher = shown first)') }}</label>
|
||||
<input class="input" name="priority" type="number" value="0">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>{{ _('Expires at (YYYY-MM-DD)') }}</label>
|
||||
<input class="input" name="expires_at" type="date">
|
||||
</div>
|
||||
<div class="billing-actions">
|
||||
<button class="btn" type="submit">{{ _('Save') }}</button>
|
||||
<a class="btn ghost" href="{{ url_for('admin.admin_promoted_keywords') }}">{{ _('Cancel') }}</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,52 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}{{ _('Admin · Promoted Keywords') }}{% endblock %}
|
||||
{% block content %}
|
||||
{% include "admin/_nav.html" %}
|
||||
<div class="card">
|
||||
<div style="display:flex;justify-content:space-between;align-items:center">
|
||||
<h2>{{ _('Promoted keywords') }}</h2>
|
||||
<a class="btn" href="{{ url_for('admin.admin_promoted_keyword_new') }}">{{ _('+ Assign keyword') }}</a>
|
||||
</div>
|
||||
|
||||
{% if not pks %}<p class="muted">{{ _('No promoted keywords.') }}</p>{% endif %}
|
||||
<table class="list">
|
||||
<thead><tr>
|
||||
<th>{{ _('Keyword') }}</th>
|
||||
<th>{{ _('Listing') }}</th>
|
||||
<th>{{ _('Priority') }}</th>
|
||||
<th>{{ _('Expires') }}</th>
|
||||
<th>{{ _('Status') }}</th>
|
||||
<th></th>
|
||||
</tr></thead>
|
||||
{% for pk in pks %}
|
||||
<tr>
|
||||
<td><code>{{ pk.keyword }}</code></td>
|
||||
<td>
|
||||
{% if pk.listing %}
|
||||
<a href="{{ url_for('listings.detail', listing_id=pk.listing_id) }}">
|
||||
{{ pk.listing.title[:60] }}
|
||||
</a>
|
||||
{% else %}<span class="muted">—</span>{% endif %}
|
||||
</td>
|
||||
<td>{{ pk.priority }}</td>
|
||||
<td class="muted small">{{ pk.expires_at.strftime('%Y-%m-%d') }}</td>
|
||||
<td>
|
||||
{% if pk.is_active %}
|
||||
<span class="badge ok">{{ _('Active') }}</span>
|
||||
{% else %}
|
||||
<span class="badge warn">{{ _('Expired') }}</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<form method="post"
|
||||
action="{{ url_for('admin.admin_promoted_keyword_delete', pk_id=pk.id) }}"
|
||||
style="display:inline" onsubmit="return confirm('{{ _('Remove?') }}');">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||||
<button class="btn danger tiny" type="submit">{{ _('Remove') }}</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,75 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}{{ _('Admin · %(t)s', t=_('Edit sponsor') if sponsor else _('New sponsor')) }}{% endblock %}
|
||||
{% block content %}
|
||||
{% include "admin/_nav.html" %}
|
||||
<div class="card">
|
||||
<h2>{{ _('Edit sponsor') if sponsor else _('New sponsor') }}</h2>
|
||||
{% if error %}<p class="alert danger">{{ error }}</p>{% endif %}
|
||||
|
||||
<form method="post" class="settings-panel">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||||
|
||||
<div class="field">
|
||||
<label>{{ _('Name') }} *</label>
|
||||
<input class="input" name="name" required
|
||||
value="{{ sponsor.name if sponsor else '' }}">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>{{ _('URL') }} *</label>
|
||||
<input class="input" name="url" required
|
||||
value="{{ sponsor.url if sponsor else '' }}">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>{{ _('Tagline') }}</label>
|
||||
<input class="input" name="tagline"
|
||||
value="{{ sponsor.tagline or '' if sponsor else '' }}">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>{{ _('Logo path (relative under media/)') }}</label>
|
||||
<input class="input" name="logo_path"
|
||||
value="{{ sponsor.logo_path or '' if sponsor else '' }}"
|
||||
placeholder="{{ _('e.g. sponsors/logo.png') }}">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>{{ _('Tier') }}</label>
|
||||
<select class="input" name="tier">
|
||||
{% for t in ['directory', 'category'] %}
|
||||
<option value="{{ t }}" {{ 'selected' if sponsor and sponsor.tier == t }}>{{ t }}</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>{{ _('Category (for category-tier sponsors)') }}</label>
|
||||
<select class="input" name="category_id">
|
||||
<option value="">{{ _('None') }}</option>
|
||||
{% for cat in categories %}
|
||||
<option value="{{ cat.id }}"
|
||||
{{ 'selected' if sponsor and sponsor.category_id == cat.id }}>
|
||||
{{ cat.name }}
|
||||
</option>
|
||||
{% endfor %}
|
||||
</select>
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>{{ _('Starts at (YYYY-MM-DD)') }}</label>
|
||||
<input class="input" name="starts_at" type="date"
|
||||
value="{{ sponsor.starts_at.strftime('%Y-%m-%d') if sponsor else '' }}">
|
||||
</div>
|
||||
<div class="field">
|
||||
<label>{{ _('Ends at (YYYY-MM-DD)') }}</label>
|
||||
<input class="input" name="ends_at" type="date"
|
||||
value="{{ sponsor.ends_at.strftime('%Y-%m-%d') if sponsor else '' }}">
|
||||
</div>
|
||||
<div class="check">
|
||||
<input type="checkbox" name="is_active" id="is_active"
|
||||
{{ 'checked' if (not sponsor or sponsor.is_active) }}>
|
||||
<label for="is_active">{{ _('Active') }}</label>
|
||||
</div>
|
||||
|
||||
<div class="billing-actions">
|
||||
<button class="btn" type="submit">{{ _('Save') }}</button>
|
||||
<a class="btn ghost" href="{{ url_for('admin.admin_sponsors') }}">{{ _('Cancel') }}</a>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -0,0 +1,50 @@
|
||||
{% extends "base.html" %}
|
||||
{% block title %}{{ _('Admin · Sponsors') }}{% endblock %}
|
||||
{% block content %}
|
||||
{% include "admin/_nav.html" %}
|
||||
<div class="card">
|
||||
<div style="display:flex;justify-content:space-between;align-items:center">
|
||||
<h2>{{ _('Sponsors') }}</h2>
|
||||
<a class="btn" href="{{ url_for('admin.admin_sponsor_new') }}">{{ _('+ New sponsor') }}</a>
|
||||
</div>
|
||||
|
||||
{% if not sponsors %}<p class="muted">{{ _('No sponsors yet.') }}</p>{% endif %}
|
||||
<table class="list">
|
||||
<thead><tr>
|
||||
<th>{{ _('Name') }}</th>
|
||||
<th>{{ _('Tier') }}</th>
|
||||
<th>{{ _('Category') }}</th>
|
||||
<th>{{ _('Schedule') }}</th>
|
||||
<th>{{ _('Status') }}</th>
|
||||
<th></th>
|
||||
</tr></thead>
|
||||
{% for sp in sponsors %}
|
||||
<tr>
|
||||
<td><a href="{{ url_for('admin.admin_sponsor_edit', sponsor_id=sp.id) }}">{{ sp.name }}</a></td>
|
||||
<td><span class="badge cat">{{ sp.tier }}</span></td>
|
||||
<td class="muted small">{{ sp.category.name if sp.category else '—' }}</td>
|
||||
<td class="muted small">
|
||||
{{ sp.starts_at.strftime('%Y-%m-%d') }} –
|
||||
{{ sp.ends_at.strftime('%Y-%m-%d') }}
|
||||
</td>
|
||||
<td>
|
||||
{% if sp.is_running %}
|
||||
<span class="badge ok">{{ _('Live') }}</span>
|
||||
{% elif sp.is_active %}
|
||||
<span class="badge cat">{{ _('Scheduled') }}</span>
|
||||
{% else %}
|
||||
<span class="badge warn">{{ _('Off') }}</span>
|
||||
{% endif %}
|
||||
</td>
|
||||
<td>
|
||||
<form method="post" action="{{ url_for('admin.admin_sponsor_delete', sponsor_id=sp.id) }}"
|
||||
style="display:inline" onsubmit="return confirm('{{ _('Delete?') }}');">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||||
<button class="btn danger tiny" type="submit">{{ _('Del') }}</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
</div>
|
||||
{% endblock %}
|
||||
@@ -37,6 +37,7 @@
|
||||
<th>{{ _('Amount') }}</th>
|
||||
<th>{{ _('Status') }}</th>
|
||||
<th>{{ _('Stripe ID') }}</th>
|
||||
<th></th>
|
||||
</tr></thead>
|
||||
{% for txn in pagination.items %}
|
||||
<tr>
|
||||
@@ -46,6 +47,16 @@
|
||||
<td>${{ '%.2f'|format(txn.amount_cents / 100) }}</td>
|
||||
<td><span class="badge {{ 'ok' if txn.status == 'succeeded' else 'warn' }}">{{ txn.status }}</span></td>
|
||||
<td class="muted small">{{ txn.stripe_object_id or '—' }}</td>
|
||||
<td>
|
||||
{% if txn.status == 'succeeded' and txn.type != 'refund' %}
|
||||
<form method="post"
|
||||
action="{{ url_for('admin.refund_transaction', txn_id=txn.id) }}"
|
||||
onsubmit="return confirm('{{ _('Issue refund for $%(a)s?', a='%.2f'|format(txn.amount_cents/100)) }}');">
|
||||
<input type="hidden" name="csrf_token" value="{{ csrf_token() }}">
|
||||
<button class="btn ghost tiny" type="submit">{{ _('Refund') }}</button>
|
||||
</form>
|
||||
{% endif %}
|
||||
</td>
|
||||
</tr>
|
||||
{% else %}
|
||||
<tr><td colspan="6" class="muted">{{ _('No transactions.') }}</td></tr>
|
||||
|
||||
Reference in New Issue
Block a user