Files
moto-adv-website/app/templates/admin/password_reset_email_template.html
ske087 30bd4c62ad Major Feature Update: Modern Chat System & Admin Management
Features Added:
🔥 Modern Chat System:
- Real-time messaging with modern Tailwind CSS design
- Post-linked discussions for adventure sharing
- Chat categories (general, technical-support, adventure-planning)
- Mobile-responsive interface with gradient backgrounds
- JavaScript polling for live message updates

🎯 Comprehensive Admin Panel:
- Chat room management with merge capabilities
- Password reset system with email templates
- User management with admin controls
- Chat statistics and analytics dashboard
- Room binding to posts and categorization

�� Mobile API Integration:
- RESTful API endpoints at /api/v1/chat
- Session-based authentication for mobile apps
- Comprehensive endpoints for rooms, messages, users
- Mobile app compatibility (React Native, Flutter)

🛠️ Technical Improvements:
- Enhanced database models with ChatRoom categories
- Password reset token system with email verification
- Template synchronization fixes for Docker deployment
- Migration scripts for database schema updates
- Improved error handling and validation

🎨 UI/UX Enhancements:
- Modern card-based layouts matching app design
- Consistent styling across chat and admin interfaces
- Mobile-optimized touch interactions
- Professional gradient designs and glass morphism effects

📚 Documentation:
- Updated README with comprehensive API documentation
- Added deployment instructions for Docker (port 8100)
- Configuration guide for production environments
- Mobile integration examples and endpoints

This update transforms the platform into a comprehensive motorcycle adventure community with modern chat capabilities and professional admin management tools.
2025-08-10 00:22:33 +03:00

270 lines
9.9 KiB
HTML

{% extends "admin/base.html" %}
{% block title %}Password Reset Email Template - Admin{% endblock %}
{% block admin_content %}
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pb-2 mb-3 border-bottom">
<h1 class="h2">Password Reset Email Template</h1>
<div class="btn-toolbar mb-2 mb-md-0">
<a href="{{ url_for('admin.password_reset_request_detail', request_id=token.request_id) }}"
class="btn btn-outline-secondary btn-sm me-2">
<i class="fas fa-arrow-left"></i> Back to Request
</a>
<a href="{{ url_for('admin.password_reset_tokens') }}" class="btn btn-outline-secondary btn-sm">
<i class="fas fa-list"></i> All Tokens
</a>
</div>
</div>
<!-- Token Status Alert -->
<div class="alert alert-info">
<h5 class="alert-heading">
<i class="fas fa-info-circle"></i> Token Information
</h5>
<p class="mb-2">
<strong>Token Status:</strong>
{% if token.is_used %}
<span class="badge bg-success">Used</span> - This token has already been used
{% elif token.is_expired %}
<span class="badge bg-secondary">Expired</span> - This token has expired
{% else %}
<span class="badge bg-warning">Active</span> - This token is ready to use
{% endif %}
</p>
<p class="mb-2">
<strong>Expires:</strong> {{ token.expires_at.strftime('%B %d, %Y at %I:%M %p') }}
</p>
<p class="mb-0">
<strong>For User:</strong> {{ token.user.nickname }} ({{ token.user.email }})
</p>
</div>
<!-- Email Template Card -->
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center">
<h6 class="m-0 fw-bold text-primary">Email Template - Copy and Send to User</h6>
<button type="button" class="btn btn-success btn-sm" onclick="copyEmailTemplate()">
<i class="fas fa-copy"></i> Copy All
</button>
</div>
<div class="card-body">
<!-- Email Subject -->
<div class="mb-4">
<label class="form-label fw-bold">Subject:</label>
<div class="input-group">
<input type="text" class="form-control" id="email-subject" readonly
value="Password Reset Request - Moto Adventure Website">
<button class="btn btn-outline-secondary" type="button" onclick="copyToClipboard('email-subject')">
<i class="fas fa-copy"></i>
</button>
</div>
</div>
<!-- Email Body -->
<div class="mb-4">
<label class="form-label fw-bold">Email Body:</label>
<div class="position-relative">
<textarea class="form-control" id="email-body" rows="12" readonly>Hello {{ token.user.nickname }},
We received your request for a password reset for your Moto Adventure website account.
To reset your password, please click the link below:
{{ reset_url }}
This link is valid for 24 hours and can only be used once. If you did not request this password reset, please ignore this email.
Important Security Information:
- This link expires on {{ token.expires_at.strftime('%B %d, %Y at %I:%M %p') }}
- Do not share this link with anyone
- If the link doesn't work, you may need to request a new password reset
If you have any questions or need assistance, please contact our support team.
Best regards,
Moto Adventure Team
---
This is an automated message. Please do not reply to this email.</textarea>
<button class="btn btn-outline-secondary position-absolute top-0 end-0 m-2"
type="button" onclick="copyToClipboard('email-body')">
<i class="fas fa-copy"></i> Copy
</button>
</div>
</div>
<!-- Reset Link Only -->
<div class="mb-4">
<label class="form-label fw-bold">Reset Link Only:</label>
<div class="input-group">
<input type="text" class="form-control" id="reset-link" readonly value="{{ reset_url }}">
<button class="btn btn-outline-secondary" type="button" onclick="copyToClipboard('reset-link')">
<i class="fas fa-copy"></i>
</button>
</div>
<small class="text-muted">Use this if you prefer to compose your own email message.</small>
</div>
</div>
</div>
<!-- Instructions Card -->
<div class="card mt-4">
<div class="card-header">
<h6 class="m-0 fw-bold text-primary">Instructions for Admin</h6>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-6">
<h6 class="fw-bold">How to Send:</h6>
<ol class="small">
<li>Copy the subject and email body above</li>
<li>Open your email client (Gmail, Outlook, etc.)</li>
<li>Create a new email to: <strong>{{ token.user.email }}</strong></li>
<li>Paste the subject and body</li>
<li>Send the email</li>
<li>Return here to monitor if the link was used</li>
</ol>
</div>
<div class="col-md-6">
<h6 class="fw-bold">Security Notes:</h6>
<ul class="small">
<li>Token expires in 24 hours automatically</li>
<li>Token can only be used once</li>
<li>Monitor token usage below</li>
<li>Do not share the reset link publicly</li>
<li>User must enter a new password to complete reset</li>
</ul>
</div>
</div>
</div>
</div>
<!-- Token Usage Tracking -->
<div class="card mt-4">
<div class="card-header">
<h6 class="m-0 fw-bold text-primary">Token Usage Tracking</h6>
</div>
<div class="card-body">
<div class="row">
<div class="col-md-3">
<div class="text-center">
<div class="h4 mb-0 {{ 'text-success' if token.is_used else 'text-muted' }}">
{{ 'Yes' if token.is_used else 'No' }}
</div>
<small class="text-muted">Used</small>
</div>
</div>
<div class="col-md-3">
<div class="text-center">
<div class="h4 mb-0 {{ 'text-danger' if token.is_expired else 'text-success' }}">
{{ 'Yes' if token.is_expired else 'No' }}
</div>
<small class="text-muted">Expired</small>
</div>
</div>
<div class="col-md-3">
<div class="text-center">
<div class="h4 mb-0">
{% if token.used_at %}
{{ token.used_at.strftime('%m/%d %H:%M') }}
{% else %}
-
{% endif %}
</div>
<small class="text-muted">Used At</small>
</div>
</div>
<div class="col-md-3">
<div class="text-center">
<div class="h4 mb-0">
{% if token.user_ip %}
{{ token.user_ip }}
{% else %}
-
{% endif %}
</div>
<small class="text-muted">User IP</small>
</div>
</div>
</div>
<div class="mt-3">
<button type="button" class="btn btn-outline-secondary btn-sm" onclick="location.reload()">
<i class="fas fa-sync-alt"></i> Refresh Status
</button>
</div>
</div>
</div>
<script>
function copyToClipboard(elementId) {
const element = document.getElementById(elementId);
element.select();
element.setSelectionRange(0, 99999); // For mobile devices
try {
document.execCommand('copy');
showCopySuccess();
} catch (err) {
console.error('Failed to copy: ', err);
showCopyError();
}
}
function copyEmailTemplate() {
const subject = document.getElementById('email-subject').value;
const body = document.getElementById('email-body').value;
const combined = `Subject: ${subject}\n\n${body}`;
navigator.clipboard.writeText(combined).then(function() {
showCopySuccess();
}, function(err) {
console.error('Failed to copy: ', err);
showCopyError();
});
}
function showCopySuccess() {
// Create temporary success alert
const alert = document.createElement('div');
alert.className = 'alert alert-success alert-dismissible fade show position-fixed';
alert.style.top = '20px';
alert.style.right = '20px';
alert.style.zIndex = '9999';
alert.innerHTML = `
<i class="fas fa-check"></i> Copied to clipboard!
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
`;
document.body.appendChild(alert);
// Auto-remove after 3 seconds
setTimeout(() => {
if (alert.parentNode) {
alert.parentNode.removeChild(alert);
}
}, 3000);
}
function showCopyError() {
// Create temporary error alert
const alert = document.createElement('div');
alert.className = 'alert alert-danger alert-dismissible fade show position-fixed';
alert.style.top = '20px';
alert.style.right = '20px';
alert.style.zIndex = '9999';
alert.innerHTML = `
<i class="fas fa-times"></i> Failed to copy. Please select and copy manually.
<button type="button" class="btn-close" data-bs-dismiss="alert"></button>
`;
document.body.appendChild(alert);
// Auto-remove after 5 seconds
setTimeout(() => {
if (alert.parentNode) {
alert.parentNode.removeChild(alert);
}
}, 5000);
}
</script>
{% endblock %}