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:
blake 2025-04-06 03:24:22 +02:00
commit 778533b655
7 changed files with 268 additions and 138 deletions

View file

@ -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")

View file

@ -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;
}

View 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>

View file

@ -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 &copy; 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>

View file

@ -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>

View file

@ -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
View 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>Were excited to share the latest updates on the development of the RideAware platform! Over the past few weeks, weve been hard at work implementing new features, improving the backend infrastructure, and enhancing the user experience. Heres a summary of what weve accomplished:</p>
<h2>🔒 User Authentication and Security</h2>
<ul>
<li><strong>Signup and Login System:</strong> Weve 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> Weve 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>🎯 Whats Next?</h2>
<p>Looking ahead, were 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>Were proud to share that the RideAware platform is open source! If youre 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>