166 lines
9.3 KiB
HTML
166 lines
9.3 KiB
HTML
{% extends "user_layout.html" %}
|
|
|
|
{% block title %}My Landing Pages - BZOD{% endblock %}
|
|
|
|
{% block active_pages %}active{% endblock %}
|
|
|
|
{% block header_title %}My Landing Pages{% endblock %}
|
|
|
|
{% block content %}
|
|
{% if let Some(err) = error %}
|
|
<div class="alert alert-error">
|
|
{{ err }}
|
|
</div>
|
|
{% endif %}
|
|
|
|
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 1.5rem; align-items: start;">
|
|
<div class="card">
|
|
<h3 style="font-size: 1.1rem; margin-bottom: 1.25rem; display: flex; align-items: center; gap: 0.5rem;">
|
|
<svg width="18" height="18" 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>
|
|
Create a New Landing Page
|
|
</h3>
|
|
|
|
<form action="/user/pages/create" method="POST">
|
|
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
|
|
|
|
<div class="form-group">
|
|
<label for="title">Page Title *</label>
|
|
<input type="text" id="title" name="title" class="form-input" placeholder="e.g. Summer Campaign" required>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="slug">SEO Slug *</label>
|
|
<input type="text" id="slug" name="slug" class="form-input" placeholder="e.g. summer-promo" required pattern="[a-zA-Z0-9\-_]+" title="Only alphanumeric characters, dashes, and underscores allowed">
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="code">Short Code (4-Hex, optional)</label>
|
|
<input type="text" id="code" name="code" class="form-input" placeholder="e.g. a1b2" pattern="[0-9a-fA-F]{4}" title="Must be exactly 4 hex characters">
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="custom_slug">Custom Slug (optional)</label>
|
|
<input type="text" id="custom_slug" name="custom_slug" class="form-input" placeholder="e.g. !my-page" pattern="![a-z0-9\-_]{1,24}" title="Must start with ! followed by 1-24 characters (a-z, 0-9, -, _)">
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="state">Publish State</label>
|
|
<select id="state" name="state" class="form-input">
|
|
<option value="draft">Draft</option>
|
|
<option value="published" selected>Published</option>
|
|
<option value="archived">Archived</option>
|
|
</select>
|
|
</div>
|
|
<div class="form-group">
|
|
<label for="html_content">HTML Content *</label>
|
|
<textarea id="html_content" name="html_content" class="form-input" style="height: 260px; font-family: monospace;" placeholder="<!DOCTYPE html> <html> <head> <title>My Page</title> </head> <body> <h1>Hello</h1> </body> </html>" required></textarea>
|
|
</div>
|
|
<button type="submit" class="btn">Save Page</button>
|
|
</form>
|
|
</div>
|
|
|
|
<div class="card" style="padding: 0; overflow: hidden;">
|
|
<div style="padding: 1.25rem 1.5rem; border-bottom: 1px solid var(--border-color); display: flex; justify-content: space-between; align-items: center;">
|
|
<h3 style="font-size: 1.1rem; display: flex; align-items: center; gap: 0.5rem;">
|
|
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><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"/></svg>
|
|
My Landing Pages
|
|
</h3>
|
|
</div>
|
|
|
|
<div class="table-container">
|
|
<table>
|
|
<thead>
|
|
<tr>
|
|
<th>Page Title</th>
|
|
<th>Short Path</th>
|
|
<th>SEO Preview Path</th>
|
|
<th>Status</th>
|
|
<th>Analytics</th>
|
|
<th>QR Code</th>
|
|
<th>Created</th>
|
|
<th>Action</th>
|
|
</tr>
|
|
</thead>
|
|
<tbody>
|
|
{% if pages.is_empty() %}
|
|
<tr>
|
|
<td colspan="8" style="text-align: center; color: var(--text-secondary); padding: 3rem;">
|
|
No landing pages created yet.
|
|
</td>
|
|
</tr>
|
|
{% else %}
|
|
{% for page in pages %}
|
|
<tr>
|
|
<td>
|
|
<div style="font-weight: 700; color: var(--text-primary);">
|
|
{{ page.title }}
|
|
</div>
|
|
<div style="font-size: 0.75rem; color: var(--text-muted); font-family: monospace;">
|
|
UUID: {{ page.id[0..8] }}...
|
|
</div>
|
|
</td>
|
|
<td>
|
|
<a href="/p/{{ page.code }}" target="_blank" style="color: var(--accent-color); font-weight: 700; text-decoration: none; font-family: monospace;">
|
|
/p/{{ page.code }}
|
|
</a>
|
|
</td>
|
|
<td>
|
|
<a href="/p/{{ page.code }}/{{ page.slug }}" target="_blank" style="color: var(--text-secondary); text-decoration: none; font-size: 0.85rem; font-family: monospace;">
|
|
/p/{{ page.code }}/{{ page.slug }}
|
|
</a>
|
|
</td>
|
|
<td>
|
|
<span class="badge badge-{{ page.state }}">
|
|
{{ page.state }}
|
|
</span>
|
|
</td>
|
|
<td>
|
|
<a href="/user/analytics/page/{{ page.id }}" class="btn btn-secondary" style="padding: 0.4rem 0.6rem; font-size: 0.8rem; display: inline-flex; align-items: center; gap: 0.25rem;">
|
|
📊 Analytics
|
|
</a>
|
|
</td>
|
|
{% let code = page.code.as_str() %}
|
|
{% include "components/qr_preview.html" %}
|
|
<td style="font-size: 0.8rem; color: var(--text-secondary);">
|
|
{{ page.created_at[0..10] }}
|
|
</td>
|
|
<td>
|
|
<form action="/user/pages/delete/{{ page.id }}" method="POST" onsubmit="return confirm('Are you sure you want to delete this page?');">
|
|
<input type="hidden" name="csrf_token" value="{{ csrf_token }}">
|
|
<button type="submit" class="btn btn-danger" style="padding: 0.4rem 0.6rem; font-size: 0.8rem;">
|
|
Delete
|
|
</button>
|
|
</form>
|
|
</td>
|
|
</tr>
|
|
{% endfor %}
|
|
{% endif %}
|
|
</tbody>
|
|
</table>
|
|
</div>
|
|
|
|
<div class="pagination" style="display: flex; justify-content: center; align-items: center; gap: 0.5rem; margin-top: 1rem; padding: 1rem; border-top: 1px solid var(--border-color);">
|
|
{% if current_page > 1 %}
|
|
<a href="/user/pages?page=1" class="btn btn-secondary" style="padding: 0.4rem 0.8rem;"><< First</a>
|
|
<a href="/user/pages?page={{ current_page - 1 }}" class="btn btn-secondary" style="padding: 0.4rem 0.8rem;">< Prev</a>
|
|
{% else %}
|
|
<span class="btn btn-secondary" style="opacity: 0.5; cursor: not-allowed; padding: 0.4rem 0.8rem;"><< First</span>
|
|
<span class="btn btn-secondary" style="opacity: 0.5; cursor: not-allowed; padding: 0.4rem 0.8rem;">< Prev</span>
|
|
{% endif %}
|
|
|
|
{% for p in visible_pages %}
|
|
{% if self.is_current(p) %}
|
|
<span class="btn" style="padding: 0.4rem 0.8rem; font-weight: 700;">{{ p }}</span>
|
|
{% else %}
|
|
<a href="/user/pages?page={{ p }}" class="btn btn-secondary" style="padding: 0.4rem 0.8rem;">{{ p }}</a>
|
|
{% endif %}
|
|
{% endfor %}
|
|
|
|
{% if current_page < total_pages %}
|
|
<a href="/user/pages?page={{ current_page + 1 }}" class="btn btn-secondary" style="padding: 0.4rem 0.8rem;">Next ></a>
|
|
<a href="/user/pages?page={{ total_pages }}" class="btn btn-secondary" style="padding: 0.4rem 0.8rem;">Last >></a>
|
|
{% else %}
|
|
<span class="btn btn-secondary" style="opacity: 0.5; cursor: not-allowed; padding: 0.4rem 0.8rem;">Next ></span>
|
|
<span class="btn btn-secondary" style="opacity: 0.5; cursor: not-allowed; padding: 0.4rem 0.8rem;">Last >></span>
|
|
{% endif %}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|