Merge pull request 'Refactor/Feat: Enhanced HTML Structure, Styling, and Templating with Jinja2' (#4) from historical_newsletter into main
Reviewed-on: https://brew.bsd.cafe/RideAware/landing/pulls/4
This commit is contained in:
commit
778533b655
7 changed files with 268 additions and 138 deletions
13
server.py
13
server.py
|
|
@ -16,15 +16,16 @@ def send_confirmation_email(email):
|
|||
SMTP_USER = os.getenv('SMTP_USER')
|
||||
SMTP_PASSWORD = os.getenv('SMTP_PASSWORD')
|
||||
|
||||
# Create the message for the
|
||||
unsubscribe_link = f"{request.url_root}unsubscribe?email={email}"
|
||||
|
||||
subject = 'Thanks for subscribing!'
|
||||
body = ("Thanks for subscribing!\n\n"
|
||||
"We're excited to share our journey with you.\n\n"
|
||||
f"If you ever wish to unsubscribe, please click <a href='{unsubscribe_link}'>here</a>."
|
||||
|
||||
html_body = render_template(
|
||||
'confirmation_email.html',
|
||||
unsubscribe_link=unsubscribe_link
|
||||
)
|
||||
|
||||
msg = MIMEText(body, 'html', 'utf-8')
|
||||
msg = MIMEText(html_body, 'html', 'utf-8') # Specify HTML
|
||||
msg['Subject'] = subject
|
||||
msg['From'] = SMTP_USER
|
||||
msg['To'] = email
|
||||
|
|
@ -36,7 +37,7 @@ def send_confirmation_email(email):
|
|||
server.quit()
|
||||
except Exception as e:
|
||||
print(f"Failed to send email to {email}: {e}")
|
||||
|
||||
|
||||
@app.route("/")
|
||||
def index():
|
||||
return render_template("index.html")
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
/* Reset and General Styles */
|
||||
html {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
*,
|
||||
*:before,
|
||||
*:after {
|
||||
|
|
@ -13,18 +15,23 @@ body {
|
|||
padding: 0;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
/* Header and Nav */
|
||||
header {
|
||||
background-color: #fff;
|
||||
padding: 10px 20px;
|
||||
text-align: center;
|
||||
font-size: 24px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
margin: 5px auto;
|
||||
}
|
||||
|
||||
header nav {
|
||||
display: flex;
|
||||
justify-content: normal;
|
||||
justify-content: flex-start; /* Align items to the start */
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
|
|
@ -35,14 +42,16 @@ header nav a {
|
|||
font-size: 22px;
|
||||
}
|
||||
|
||||
/* Hero Section Styles */
|
||||
.hero-section-1 {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.hero-content, .hero-text {
|
||||
.hero-content,
|
||||
.hero-text {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
margin: 0;
|
||||
|
|
@ -50,9 +59,8 @@ header nav a {
|
|||
}
|
||||
|
||||
.hero-text img {
|
||||
width: 70%;
|
||||
height: auto;
|
||||
display: block;
|
||||
width: 70%;
|
||||
display: block;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
|
|
@ -109,6 +117,7 @@ header nav a {
|
|||
cursor: pointer;
|
||||
}
|
||||
|
||||
/* Feature Cards */
|
||||
.hero-section-3 {
|
||||
width: 100%;
|
||||
}
|
||||
|
|
@ -168,6 +177,7 @@ header nav a {
|
|||
margin-right: 10px;
|
||||
}
|
||||
|
||||
/* Main Content */
|
||||
main {
|
||||
width: 100%;
|
||||
margin: 0;
|
||||
|
|
@ -179,6 +189,13 @@ main .inner-container {
|
|||
padding: 10px 20px;
|
||||
}
|
||||
|
||||
main h1 {
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
color: #007bff;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
.normal-footer {
|
||||
background-color: #337cf2;
|
||||
color: #fff;
|
||||
|
|
@ -189,22 +206,101 @@ main .inner-container {
|
|||
}
|
||||
|
||||
.fixed-footer {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
background-color: #337cf2;
|
||||
color: #fff;
|
||||
padding: 10px;
|
||||
text-align: center;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
img {
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
background-color: #337cf2;
|
||||
color: #fff;
|
||||
padding: 10px;
|
||||
text-align: center;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
/* Newsletter Styles */
|
||||
.newsletter {
|
||||
margin-bottom: 40px;
|
||||
border-bottom: 1px solid #ddd;
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
||||
.newsletter h2 {
|
||||
color: #007bff;
|
||||
}
|
||||
|
||||
.newsletter-time {
|
||||
color: #666;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.newsletter-detail {
|
||||
max-width: 800px;
|
||||
margin: 20px auto;
|
||||
padding: 20px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.newsletter-detail h1 {
|
||||
color: #007bff;
|
||||
}
|
||||
|
||||
.back-link {
|
||||
margin-top: 20px;
|
||||
display: inline-block;
|
||||
color: #007bff;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.back-link:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.cards-container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.newsletter-content {
|
||||
width: 100%; /* Fixed to 100% */
|
||||
}
|
||||
|
||||
.newsletter-card {
|
||||
background: #fff;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
width: 300px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 20px;
|
||||
transition: transform 0.15s ease-in-out;
|
||||
}
|
||||
|
||||
.newsletter-card:hover {
|
||||
transform: translateY(-5px);
|
||||
}
|
||||
|
||||
.newsletter-card h2 {
|
||||
font-size: 1.5em;
|
||||
color: #007bff;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.newsletter-card p.newsletter-time {
|
||||
font-size: 0.8em;
|
||||
}
|
||||
|
||||
.newsletter-main {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
/* Media Queries */
|
||||
@media (max-width: 768px) {
|
||||
.hero-content {
|
||||
flex-direction: column;
|
||||
|
|
@ -216,11 +312,6 @@ img {
|
|||
padding: 10px;
|
||||
}
|
||||
|
||||
.hero-illustration {
|
||||
width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
|
||||
.hero-section-2 input {
|
||||
width: 80%;
|
||||
}
|
||||
|
|
@ -265,89 +356,3 @@ img {
|
|||
padding: 50px 0;
|
||||
}
|
||||
}
|
||||
|
||||
.newsletter {
|
||||
margin-bottom: 40px;
|
||||
border-bottom: 1px solid #ddd;
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
||||
.newsletter h2 {
|
||||
color: #007bff;
|
||||
}
|
||||
|
||||
.newsletter-time {
|
||||
color: #666;
|
||||
font-size: 0.9em;
|
||||
}
|
||||
|
||||
.newsletter-detail {
|
||||
max-width: 800px;
|
||||
margin: 20px auto;
|
||||
padding: 20px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
.newsletter-detail h1 {
|
||||
color: #007bff;
|
||||
}
|
||||
.back-link {
|
||||
margin-top: 20px;
|
||||
display: inline-block;
|
||||
color: #007bff;
|
||||
text-decoration: none;
|
||||
}
|
||||
.back-link:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
main h1 {
|
||||
text-align: center;
|
||||
margin-bottom: 20px;
|
||||
color: #007bff;
|
||||
}
|
||||
|
||||
.cards-container {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.newsletter-content {
|
||||
width: 5000px%;
|
||||
}
|
||||
|
||||
.newsletter-card {
|
||||
background: #fff;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
width: 300px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 20px;
|
||||
transition: transform 0.15s ease-in-out;
|
||||
}
|
||||
|
||||
.newsletter-card:hover {
|
||||
transform: translateY(-5px);
|
||||
}
|
||||
|
||||
.newsletter-card h2 {
|
||||
font-size: 1.5em;
|
||||
color: #007bff;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.newsletter-card p.newsletter-time {
|
||||
font-size: 0.8em
|
||||
}
|
||||
|
||||
.newsletter-main {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
|
|
|
|||
35
templates/confirmation_email.html
Normal file
35
templates/confirmation_email.html
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Thanks for Subscribing!</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
line-height: 1.6;
|
||||
color: #333;
|
||||
}
|
||||
.container {
|
||||
max-width: 600px;
|
||||
margin: 20px auto;
|
||||
padding: 20px;
|
||||
border: 1px solid #ddd;
|
||||
}
|
||||
a {
|
||||
color: #007bff;
|
||||
text-decoration: none;
|
||||
}
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<h2>Thanks for subscribing to RideAware newsletter!</h2>
|
||||
<p>We're excited to share our journey with you.</p>
|
||||
<p>If you ever wish to unsubscribe, please click <a href="{{ unsubscribe_link }}">here</a>.</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -4,7 +4,7 @@
|
|||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>RideAware</title>
|
||||
<link rel="stylesheet" href="/static/css/styles.css">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/styles.css') }}">
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
|
|
@ -19,7 +19,7 @@
|
|||
<section class="hero-section-1">
|
||||
<div class="hero-content">
|
||||
<div class="hero-text">
|
||||
<img src="/static/assets/RideAwareLogo.svg" alt="RideAware Logo">
|
||||
<img src="{{ url_for('static', filename='assets/RideAwareLogo.svg') }}" alt="RideAware Logo">
|
||||
</div>
|
||||
</section>
|
||||
<section class="hero-section-2">
|
||||
|
|
@ -90,6 +90,6 @@
|
|||
<footer class="normal-footer">
|
||||
Copyright © 2025 RideAware. All rights reserved.
|
||||
</footer>
|
||||
<script src="/static/js/main.js"></script>
|
||||
<script src="{{ url_for('static', filename='js/main.js') }}"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@
|
|||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>RideAware - Newsletter Detail</title>
|
||||
<link rel="stylesheet" href="/static/css/styles.css" />
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/styles.css') }}" />
|
||||
</head>
|
||||
<body>
|
||||
<header>
|
||||
|
|
@ -17,17 +17,16 @@
|
|||
</header>
|
||||
|
||||
<main>
|
||||
<a href="/newsletters" class="back-link">← Back to Newsletters</a>
|
||||
<h1>{{ newsletter.subject }}</h1>
|
||||
<div class="newsletter-content">
|
||||
{{ newsletter.body | safe }}
|
||||
</div>
|
||||
<a href="/newsletters" class="back-link">← Back to Newsletters</a>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<footer class="fixed-footer">
|
||||
<footer class="normal-footer">
|
||||
<p>© 2025 RideAware. All rights reserved.</p>
|
||||
</footer>
|
||||
<script src="/static/js/main.js"></script>
|
||||
<script src="{{ url_for('static', filename='js/main.js') }}"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -4,15 +4,7 @@
|
|||
<meta charset="UTF-8">
|
||||
<title>RideAware - Newsletters</title>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<link rel="stylesheet" href="/static/css/styles.css">
|
||||
<style>
|
||||
main {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
</style>
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/styles.css') }}">
|
||||
|
||||
</head>
|
||||
<body>
|
||||
|
|
@ -30,10 +22,10 @@
|
|||
{% for nl in newsletters %}
|
||||
<div class="newsletter">
|
||||
<h2>
|
||||
<a href="{{ url_for('newsletter_detail', newsletter_id=nl['id']) }}">{{ nl['subject'] }}</a>
|
||||
<a href="/newsletter/{{ nl['id'] }}">{{ nl['subject'] }}</a>
|
||||
</h2>
|
||||
<p class="newsletter-time">Sent on: {{ nl['sent_at'] }}</p>
|
||||
<a href="{{ url_for('newsletter_detail', newsletter_id=nl['id']) }}" class="read-more">Read More</a>
|
||||
<a href="/newsletter/{{ nl['id'] }}" class="read-more">Read More</a>
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% else %}
|
||||
|
|
@ -43,6 +35,6 @@
|
|||
<footer class="fixed-footer">
|
||||
<p>© 2025 RideAware. All rights reserved.</p>
|
||||
</footer>
|
||||
<script src="/static/js/main.js"></script>
|
||||
<script src="{{ url_for('static', filename='js/main.js') }}"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
98
templates/template.html
Normal file
98
templates/template.html
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
line-height: 1.6;
|
||||
color: #333;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
.container {
|
||||
max-width: 750px;
|
||||
margin: 20px auto;
|
||||
padding: 20px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
h1, h2 {
|
||||
color: #007bff;
|
||||
}
|
||||
h1 {
|
||||
text-align: center;
|
||||
}
|
||||
ul {
|
||||
padding-left: 20px;
|
||||
}
|
||||
.footer {
|
||||
text-align: center;
|
||||
margin-top: 20px;
|
||||
font-size: 0.9em;
|
||||
color: #666;
|
||||
}
|
||||
a {
|
||||
color: #007bff;
|
||||
text-decoration: none;
|
||||
}
|
||||
a:hover {
|
||||
text-decoration: underline;
|
||||
}
|
||||
</style>
|
||||
|
||||
<body>
|
||||
<div class="container">
|
||||
<h1>🚀 RideAware Development Update</h1>
|
||||
<p>Dear RideAware Community,</p>
|
||||
<p>We’re excited to share the latest updates on the development of the RideAware platform! Over the past few weeks, we’ve been hard at work implementing new features, improving the backend infrastructure, and enhancing the user experience. Here’s a summary of what we’ve accomplished:</p>
|
||||
|
||||
<h2>🔒 User Authentication and Security</h2>
|
||||
<ul>
|
||||
<li><strong>Signup and Login System:</strong> We’ve implemented a robust user authentication system that allows users to securely sign up and log in to the platform. Passwords are hashed using industry-standard algorithms (<code>pbkdf2:sha256</code>) to ensure user data is stored securely.</li>
|
||||
<li><strong>Error Handling and Validation:</strong> Input validation has been added to ensure usernames and passwords meet security and usability standards. Users are notified of errors such as invalid credentials or duplicate accounts with clear and helpful messages.</li>
|
||||
<li><strong>Logout Functionality:</strong> A logout feature has been added, allowing users to securely end their sessions.</li>
|
||||
</ul>
|
||||
|
||||
<h2>👤 User Profiles</h2>
|
||||
<ul>
|
||||
<li><strong>User Profile Creation:</strong> A new <code>UserProfile</code> system has been introduced, allowing users to store additional information such as their first name, last name, bio, and profile picture.</li>
|
||||
<li><strong>Automatic Profile Creation:</strong> User profiles are now automatically created when a new user account is registered, ensuring a seamless onboarding experience.</li>
|
||||
<li><strong>One-to-One Relationship:</strong> The backend now supports a one-to-one relationship between users and their profiles, making it easy to manage and query user data.</li>
|
||||
</ul>
|
||||
|
||||
<h2>🛠 Backend Improvements</h2>
|
||||
<ul>
|
||||
<li><strong>PostgreSQL Integration:</strong> We’ve migrated from a local SQLite database to a robust PostgreSQL database hosted on a separate server. This change improves scalability, performance, and environment separation.</li>
|
||||
<li><strong>SQLAlchemy ORM:</strong> The backend now uses SQLAlchemy for database interactions, simplifying the codebase and improving maintainability.</li>
|
||||
<li><strong>Error Logging and Debugging:</strong> Enhanced logging has been added to track issues during signup, login, and database operations. This ensures that any problems can be quickly identified and resolved.</li>
|
||||
</ul>
|
||||
|
||||
<h2>🌐 Frontend Enhancements</h2>
|
||||
<ul>
|
||||
<li><strong>Vue.js Login Component:</strong> A new login component has been developed using Vue.js, providing a clean and user-friendly interface for authentication.</li>
|
||||
<li><strong>Error Feedback:</strong> The frontend now displays clear error messages for invalid login attempts or missing input fields, improving the user experience.</li>
|
||||
<li><strong>Environment-Specific API Configuration:</strong> The frontend now dynamically uses environment variables to configure the API base URL, making it easier to switch between development and production environments.</li>
|
||||
</ul>
|
||||
|
||||
<h2>📧 Newsletter System</h2>
|
||||
<ul>
|
||||
<li><strong>User Notifications:</strong> A foundation has been laid for a newsletter system, allowing users to stay updated on the latest developments and announcements.</li>
|
||||
<li><strong>Unsubscribe Links:</strong> Unsubscribe links are now included in all emails, ensuring compliance with best practices and user preferences.</li>
|
||||
</ul>
|
||||
|
||||
<h2>🎯 What’s Next?</h2>
|
||||
<p>Looking ahead, we’re planning to:</p>
|
||||
<ul>
|
||||
<li>Implement a password reset feature for users who forget their credentials.</li>
|
||||
<li>Add two-factor authentication (2FA) for enhanced account security.</li>
|
||||
<li>Build a dashboard for users to manage their profiles and preferences.</li>
|
||||
<li>Continue refining the frontend design for a more polished user experience.</li>
|
||||
</ul>
|
||||
|
||||
<h2>💡 Get Involved</h2>
|
||||
<p>We’re proud to share that the RideAware platform is open source! If you’re a developer or enthusiast, you can view the codebase, contribute to the project, or provide feedback. Check out the repository here: <a href="https://github.com/rideaware" target="_blank">RideAware GitHub</a>.</p>
|
||||
|
||||
<p>Thank you for your continued support as we build RideAware into a platform that empowers cyclists and fosters a connected community. Stay tuned for more updates!</p>
|
||||
|
||||
<p>Best regards,</p>
|
||||
<p><strong>The RideAware Development Team 🚴♂️</strong></p>
|
||||
</div>
|
||||
</body>
|
||||
Loading…
Add table
Add a link
Reference in a new issue