0011
This commit is contained in:
24
src/templates/footer.html
Normal file
24
src/templates/footer.html
Normal file
@@ -0,0 +1,24 @@
|
||||
</main>
|
||||
<footer class="bg-dark text-light py-4 mt-5">
|
||||
<div class="container">
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<h5>Air Bookings</h5>
|
||||
<p>Your trusted partner for air travel bookings.</p>
|
||||
<p>© 2024 Air Bookings. All rights reserved.</p>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<h5>Connect With Us</h5>
|
||||
<div class="social-links">
|
||||
<a href="#" class="text-light me-3"><i class="bi bi-facebook"></i></a>
|
||||
<a href="#" class="text-light me-3"><i class="bi bi-twitter"></i></a>
|
||||
<a href="#" class="text-light me-3"><i class="bi bi-instagram"></i></a>
|
||||
<a href="#" class="text-light"><i class="bi bi-linkedin"></i></a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
@js '/static/js/bootstrap.js'
|
||||
</body>
|
||||
</html>
|
35
src/templates/header.html
Normal file
35
src/templates/header.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>Air Bookings</title>
|
||||
@css '/static/css/bootstrap.css'
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.7.2/font/bootstrap-icons.css">
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar navbar-expand-lg navbar-dark bg-primary">
|
||||
<div class="container">
|
||||
<a class="navbar-brand" href="/">Air Bookings</a>
|
||||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav">
|
||||
<span class="navbar-toggler-icon"></span>
|
||||
</button>
|
||||
<div class="collapse navbar-collapse" id="navbarNav">
|
||||
<ul class="navbar-nav ms-auto">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/">Home</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/profile">Profile</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/login">Login</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" href="/signup">Sign Up</a>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
<main>
|
@@ -177,7 +177,10 @@
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="email">Email Address <span class="field-error email-error"></span></label>
|
||||
<label for="email"
|
||||
>Email Address
|
||||
<span class="field-error email-error"></span
|
||||
></label>
|
||||
<input
|
||||
type="email"
|
||||
id="email"
|
||||
@@ -190,7 +193,10 @@
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="password">Password <span class="field-error password-error"></span></label>
|
||||
<label for="password"
|
||||
>Password
|
||||
<span class="field-error password-error"></span
|
||||
></label>
|
||||
<input
|
||||
type="password"
|
||||
id="password"
|
||||
@@ -203,13 +209,17 @@
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<button type="submit" class="login-btn">
|
||||
Sign In
|
||||
</button>
|
||||
<button type="submit" class="login-btn">Sign In</button>
|
||||
</div>
|
||||
|
||||
<div class="form-group text-center" style="margin-top: 20px;">
|
||||
<p>Don't have an account? <a href="/signup">Sign up now</a></p>
|
||||
<div
|
||||
class="form-group text-center"
|
||||
style="margin-top: 20px"
|
||||
>
|
||||
<p>
|
||||
Don't have an account?
|
||||
<a href="/signup">Sign up now</a>
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
|
85
src/templates/profile.html
Normal file
85
src/templates/profile.html
Normal file
@@ -0,0 +1,85 @@
|
||||
@include 'header.html'
|
||||
|
||||
<div class="container mt-5">
|
||||
<div class="row">
|
||||
<div class="col-md-8 mx-auto">
|
||||
<div class="card">
|
||||
<div class="card-header bg-primary text-white">
|
||||
<h3>My Profile</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="mb-4">
|
||||
<h4>@{user.first_name} @{user.last_name}</h4>
|
||||
<p><i class="bi bi-envelope"></i> @{user.email}</p>
|
||||
<p><i class="bi bi-gender-ambiguous"></i> @{user.gender}</p>
|
||||
</div>
|
||||
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<h5>Update Profile</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form action="/controller/user/update" method="post">
|
||||
<input type="hidden" name="id" value="@{user.id}">
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="first_name" class="form-label">First Name</label>
|
||||
<input type="text" class="form-control" id="first_name" name="first_name" value="@{user.first_name}">
|
||||
<div class="text-danger first_name-error field-error"></div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="last_name" class="form-label">Last Name</label>
|
||||
<input type="text" class="form-control" id="last_name" name="last_name" value="@{user.last_name}">
|
||||
<div class="text-danger last_name-error field-error"></div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="password" class="form-label">New Password (leave empty to keep current)</label>
|
||||
<input type="password" class="form-control" id="password" name="password">
|
||||
<div class="text-danger password-error field-error"></div>
|
||||
</div>
|
||||
|
||||
<div class="d-grid">
|
||||
<button type="submit" class="btn btn-primary">Update Profile</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5>Account Actions</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="d-grid gap-2">
|
||||
<a href="/logout" class="btn btn-danger">Logout</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="response-container"></div>
|
||||
|
||||
<script>
|
||||
document.querySelector("form").addEventListener("submit", function (e) {
|
||||
e.preventDefault();
|
||||
const form = e.target;
|
||||
const formData = new FormData(form);
|
||||
|
||||
fetch(form.action, {
|
||||
method: "POST",
|
||||
body: formData,
|
||||
})
|
||||
.then((response) => response.text())
|
||||
.then((html) => {
|
||||
document.getElementById("response-container").innerHTML = html;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
@include 'footer.html'
|
@@ -210,7 +210,12 @@
|
||||
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label for="first-name">First Name <span class="field-error first_name-error"></span></label>
|
||||
<label for="first-name"
|
||||
>First Name
|
||||
<span
|
||||
class="field-error first_name-error"
|
||||
></span
|
||||
></label>
|
||||
<input
|
||||
type="text"
|
||||
id="first-name"
|
||||
@@ -222,7 +227,10 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="last-name">Last Name <span class="field-error last_name-error"></span></label>
|
||||
<label for="last-name"
|
||||
>Last Name
|
||||
<span class="field-error last_name-error"></span
|
||||
></label>
|
||||
<input
|
||||
type="text"
|
||||
id="last-name"
|
||||
@@ -236,7 +244,10 @@
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="email">Email Address <span class="field-error email-error"></span></label>
|
||||
<label for="email"
|
||||
>Email Address
|
||||
<span class="field-error email-error"></span
|
||||
></label>
|
||||
<input
|
||||
type="email"
|
||||
id="email"
|
||||
@@ -249,7 +260,10 @@
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="password">Password <span class="field-error password-error"></span></label>
|
||||
<label for="password"
|
||||
>Password
|
||||
<span class="field-error password-error"></span
|
||||
></label>
|
||||
<input
|
||||
type="password"
|
||||
id="password"
|
||||
@@ -275,7 +289,10 @@
|
||||
</div>
|
||||
</div> -->
|
||||
<div class="form-group">
|
||||
<label>Gender <span class="field-error gender-error"></span></label>
|
||||
<label
|
||||
>Gender
|
||||
<span class="field-error gender-error"></span
|
||||
></label>
|
||||
<div class="gender-group">
|
||||
<div class="form-group">
|
||||
<input
|
||||
|
131
src/templates/user.html
Normal file
131
src/templates/user.html
Normal file
@@ -0,0 +1,131 @@
|
||||
@include 'header.html'
|
||||
|
||||
<div class="container mt-5">
|
||||
<div class="row">
|
||||
<div class="col-md-8 mx-auto">
|
||||
<div class="card">
|
||||
<div class="card-header bg-primary text-white">
|
||||
<h3>User Profile</h3>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="mb-4">
|
||||
<h4>@{user.first_name} @{user.last_name}</h4>
|
||||
<p><i class="bi bi-envelope"></i> @{user.email}</p>
|
||||
<p><i class="bi bi-gender-ambiguous"></i> @{user.gender}</p>
|
||||
</div>
|
||||
|
||||
<div class="card mb-4">
|
||||
<div class="card-header">
|
||||
<h5>Update Profile</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form
|
||||
action="/controller/user/update"
|
||||
method="post"
|
||||
>
|
||||
<input
|
||||
type="hidden"
|
||||
name="id"
|
||||
value="@{user.id}"
|
||||
/>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="first_name" class="form-label"
|
||||
>First Name</label
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="first_name"
|
||||
name="first_name"
|
||||
value="@{user.first_name}"
|
||||
/>
|
||||
<div
|
||||
class="text-danger first_name-error field-error"
|
||||
></div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="last_name" class="form-label"
|
||||
>Last Name</label
|
||||
>
|
||||
<input
|
||||
type="text"
|
||||
class="form-control"
|
||||
id="last_name"
|
||||
name="last_name"
|
||||
value="@{user.last_name}"
|
||||
/>
|
||||
<div
|
||||
class="text-danger last_name-error field-error"
|
||||
></div>
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="password" class="form-label"
|
||||
>New Password (leave empty to keep
|
||||
current)</label
|
||||
>
|
||||
<input
|
||||
type="password"
|
||||
class="form-control"
|
||||
id="password"
|
||||
name="password"
|
||||
/>
|
||||
<div
|
||||
class="text-danger password-error field-error"
|
||||
></div>
|
||||
</div>
|
||||
|
||||
<div class="d-grid">
|
||||
<button
|
||||
type="submit"
|
||||
class="btn btn-primary"
|
||||
>
|
||||
Update Profile
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h5>Account Actions</h5>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="d-grid gap-2">
|
||||
<a href="/logout" class="btn btn-danger"
|
||||
>Logout</a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="response-container"></div>
|
||||
|
||||
<script>
|
||||
// Handle form submission via AJAX
|
||||
document.querySelector("form").addEventListener("submit", function (e) {
|
||||
e.preventDefault();
|
||||
|
||||
const form = e.target;
|
||||
const formData = new FormData(form);
|
||||
|
||||
fetch(form.action, {
|
||||
method: "POST",
|
||||
body: formData,
|
||||
})
|
||||
.then((response) => response.text())
|
||||
.then((html) => {
|
||||
document.getElementById("response-container").innerHTML = html;
|
||||
});
|
||||
});
|
||||
</script>
|
||||
|
||||
@include 'footer.html'
|
@@ -52,7 +52,7 @@ pub fn (mut app App) controller_create_user(mut ctx Context, first_name string,
|
||||
// Generate and insert the token using user ID
|
||||
token := app.auth.add_token(x.id) or { '' }
|
||||
// Authenticate the user by adding the token to the cookies
|
||||
ctx.set_cookie(name: 'token', value: token)
|
||||
ctx.set_cookie(name: 'token', value: token, path: '/')
|
||||
}
|
||||
|
||||
// Return success message with HTML
|
||||
@@ -110,7 +110,7 @@ pub fn (mut app App) controller_get_user(mut ctx Context, email string, password
|
||||
|
||||
token := app.auth.add_token(user.id) or { '' }
|
||||
// Authenticate the user by adding the token to the cookies
|
||||
ctx.set_cookie(name: 'token', value: token)
|
||||
ctx.set_cookie(name: 'token', value: token, path: '/')
|
||||
|
||||
// Return success message with HTML and redirect
|
||||
success_html := '<div class="alert alert-success">
|
||||
|
@@ -41,35 +41,55 @@ fn (app &App) service_update_user(id ?string, first_name string, last_name strin
|
||||
return error('User ID is required')
|
||||
}
|
||||
|
||||
// Unwrap the id value
|
||||
user_id := id or { return error('Invalid ID') }
|
||||
|
||||
// Get current user data
|
||||
current_user := sql app.db {
|
||||
select from User where id == id limit 1
|
||||
select from User where id == user_id limit 1
|
||||
}!
|
||||
|
||||
if current_user.len == 0 {
|
||||
return error('User not found')
|
||||
}
|
||||
|
||||
mut updates := []string{}
|
||||
|
||||
// Check which fields have changed
|
||||
if first_name != current_user[0].first_name {
|
||||
sql app.db {
|
||||
update User set first_name = first_name where id == id
|
||||
update User set first_name = first_name where id == user_id
|
||||
}!
|
||||
}
|
||||
if last_name != current_user[0].last_name {
|
||||
sql app.db {
|
||||
update User set last_name = last_name where id == id
|
||||
update User set last_name = last_name where id == user_id
|
||||
}!
|
||||
}
|
||||
if password != '' { // Only update password if a new one is provided
|
||||
if !auth.compare_password_with_hash(password, current_user[0].salt, current_user[0].password) {
|
||||
salt := auth.generate_salt()
|
||||
hashed_password := auth.hash_password_with_salt(password, salt)
|
||||
sql app.db {
|
||||
update User set password = hashed_password, salt = salt where id == id
|
||||
update User set password = hashed_password, salt = salt where id == user_id
|
||||
}!
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
fn (app &App) service_get_user(id int) !User {
|
||||
if id == 0 {
|
||||
return error('User ID is required')
|
||||
}
|
||||
|
||||
user_id := id
|
||||
|
||||
// Get user data
|
||||
user := sql app.db {
|
||||
select from User where id == user_id limit 1
|
||||
}!
|
||||
|
||||
if user.len == 0 {
|
||||
return error('User not found')
|
||||
}
|
||||
|
||||
return user[0]
|
||||
}
|
||||
|
@@ -9,3 +9,30 @@ pub fn (app &App) signup(mut ctx Context) veb.Result {
|
||||
pub fn (app &App) login(mut ctx Context) veb.Result {
|
||||
return $veb.html()
|
||||
}
|
||||
|
||||
pub fn (app &App) user(mut ctx Context) veb.Result {
|
||||
user_id := ctx.get_cookie('token') or { '' }
|
||||
token := app.auth.find_token(user_id) or { return ctx.redirect('/login') }
|
||||
|
||||
if token.user_id == 0 {
|
||||
// Redirect to login if not logged in
|
||||
return ctx.redirect('/login')
|
||||
}
|
||||
|
||||
user := app.service_get_user(token.user_id) or { return ctx.redirect('/login') }
|
||||
|
||||
return $veb.html()
|
||||
}
|
||||
|
||||
pub fn (app &App) profile(mut ctx Context) veb.Result {
|
||||
user_id := ctx.get_cookie('token') or { '' }
|
||||
token := app.auth.find_token(user_id) or { return ctx.redirect('/login') }
|
||||
|
||||
if token.user_id == 0 {
|
||||
return ctx.redirect('/login')
|
||||
}
|
||||
|
||||
user := app.service_get_user(token.user_id) or { return ctx.redirect('/login') }
|
||||
|
||||
return $veb.html()
|
||||
}
|
||||
|
Reference in New Issue
Block a user