005
This commit is contained in:
0
src/templates/first_name.html
Normal file
0
src/templates/first_name.html
Normal file
253
src/templates/index.html
Normal file
253
src/templates/index.html
Normal file
@@ -0,0 +1,253 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Flight Booking</title>
|
||||
<style>
|
||||
/* Reset and Base Styles */
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
line-height: 1.6;
|
||||
background-color: #f4f4f4;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
/* Container */
|
||||
.container {
|
||||
width: 90%;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
/* Header */
|
||||
.header {
|
||||
background-color: #2c3e50;
|
||||
color: white;
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
/* Flight Search Form */
|
||||
.flight-search {
|
||||
background-color: white;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||
padding: 30px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.form-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
flex: 1;
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
.form-group:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.form-group label {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.form-group input,
|
||||
.form-group select {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.search-btn {
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
background-color: #3498db;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
transition: background-color 0.3s ease;
|
||||
}
|
||||
|
||||
.search-btn:hover {
|
||||
background-color: #2980b9;
|
||||
}
|
||||
|
||||
/* Flight Results */
|
||||
.flight-results {
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
.flight-card {
|
||||
background-color: white;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||
padding: 20px;
|
||||
margin-bottom: 15px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.flight-details {
|
||||
flex: 2;
|
||||
}
|
||||
|
||||
.flight-price {
|
||||
flex: 1;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.flight-price .price {
|
||||
font-size: 24px;
|
||||
color: #2ecc71;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.book-btn {
|
||||
background-color: #2ecc71;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 10px 20px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.book-btn:hover {
|
||||
background-color: #27ae60;
|
||||
}
|
||||
|
||||
/* Responsive Design */
|
||||
@media (max-width: 768px) {
|
||||
.form-row {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-right: 0;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.flight-card {
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.flight-price {
|
||||
margin-top: 15px;
|
||||
text-align: center;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<header class="header">
|
||||
<h1>Flight Booking</h1>
|
||||
<p>Find and book your perfect flight</p>
|
||||
</header>
|
||||
|
||||
<section class="flight-search">
|
||||
<form id="flight-search-form">
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label for="from">From</label>
|
||||
<input
|
||||
type="text"
|
||||
id="from"
|
||||
name="from"
|
||||
placeholder="Departure City"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="to">To</label>
|
||||
<input
|
||||
type="text"
|
||||
id="to"
|
||||
name="to"
|
||||
placeholder="Arrival City"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label for="departure-date">Departure Date</label>
|
||||
<input
|
||||
type="date"
|
||||
id="departure-date"
|
||||
name="departure-date"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="passengers">Passengers</label>
|
||||
<select id="passengers" name="passengers">
|
||||
<option value="1">1 Passenger</option>
|
||||
<option value="2">2 Passengers</option>
|
||||
<option value="3">3 Passengers</option>
|
||||
<option value="4">4 Passengers</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<button type="submit" class="search-btn">
|
||||
Search Flights
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<section class="flight-results">
|
||||
<div class="flight-card">
|
||||
<div class="flight-details">
|
||||
<h3>New York (JFK) to Los Angeles (LAX)</h3>
|
||||
<p>Departure: June 15, 2024 at 08:00 AM</p>
|
||||
<p>Airline: Delta Air Lines</p>
|
||||
<p>Flight Duration: 6h 15m</p>
|
||||
</div>
|
||||
<div class="flight-price">
|
||||
<span class="price">$345.99</span>
|
||||
<button class="book-btn">Book Now</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="flight-card">
|
||||
<div class="flight-details">
|
||||
<h3>Chicago (ORD) to San Francisco (SFO)</h3>
|
||||
<p>Departure: June 16, 2024 at 11:30 AM</p>
|
||||
<p>Airline: United Airlines</p>
|
||||
<p>Flight Duration: 4h 45m</p>
|
||||
</div>
|
||||
<div class="flight-price">
|
||||
<span class="price">$289.50</span>
|
||||
<button class="book-btn">Book Now</button>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
371
src/templates/signup.html
Normal file
371
src/templates/signup.html
Normal file
@@ -0,0 +1,371 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>User Registration</title>
|
||||
<style>
|
||||
/* Reset and Base Styles */
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
line-height: 1.6;
|
||||
background-color: #f4f4f4;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
/* Container */
|
||||
.container {
|
||||
width: 90%;
|
||||
max-width: 600px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
/* Header */
|
||||
.header {
|
||||
background-color: #2c3e50;
|
||||
color: white;
|
||||
text-align: center;
|
||||
padding: 20px;
|
||||
border-radius: 8px 8px 0 0;
|
||||
}
|
||||
|
||||
.header h1 {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
/* Registration Form */
|
||||
.registration-form {
|
||||
background-color: white;
|
||||
border-radius: 0 0 8px 8px;
|
||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
|
||||
padding: 30px;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.form-group label {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.form-group input,
|
||||
.form-group select {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.form-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.form-row .form-group {
|
||||
flex: 1;
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
.form-row .form-group:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.register-btn {
|
||||
width: 100%;
|
||||
padding: 12px;
|
||||
background-color: #3498db;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
font-size: 16px;
|
||||
transition: background-color 0.3s ease;
|
||||
}
|
||||
|
||||
.register-btn:hover {
|
||||
background-color: #2980b9;
|
||||
}
|
||||
|
||||
/* Gender Radio Buttons */
|
||||
.gender-group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.gender-group .form-group {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
.gender-group input[type="radio"] {
|
||||
margin-right: 5px;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
/* Responsive Design */
|
||||
@media (max-width: 600px) {
|
||||
.form-row {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.form-row .form-group {
|
||||
margin-right: 0;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Error Handling */
|
||||
.error-message {
|
||||
color: #e74c3c;
|
||||
font-size: 0.9em;
|
||||
margin-top: 5px;
|
||||
display: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<header class="header">
|
||||
<h1>User Registration</h1>
|
||||
<p>Create your account to start booking flights</p>
|
||||
</header>
|
||||
|
||||
<section class="registration-form">
|
||||
<form id="user-registration-form">
|
||||
<div class="form-row">
|
||||
<div class="form-group">
|
||||
<label for="first-name">First Name</label>
|
||||
<input
|
||||
type="text"
|
||||
id="first-name"
|
||||
name="first_name"
|
||||
placeholder="Enter your first name"
|
||||
required
|
||||
/>
|
||||
<div class="error-message" id="first-name-error">
|
||||
Please enter a valid first name
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
class="form-group"
|
||||
hx-target="this"
|
||||
hx-swap="outerHTML"
|
||||
>
|
||||
<label for="last-name">Last Name</label>
|
||||
<input
|
||||
type="text"
|
||||
id="last-name"
|
||||
name="last_name"
|
||||
placeholder="Enter your last name"
|
||||
required
|
||||
/>
|
||||
<div class="error-message" id="last-name-error">
|
||||
Please enter a valid last name
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="email">Email Address</label>
|
||||
<input
|
||||
type="email"
|
||||
id="email"
|
||||
name="email"
|
||||
placeholder="Enter your email"
|
||||
required
|
||||
/>
|
||||
<div class="error-message" id="email-error">
|
||||
Please enter a valid email address
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="password">Password</label>
|
||||
<input
|
||||
type="password"
|
||||
id="password"
|
||||
name="password"
|
||||
placeholder="Create a strong password"
|
||||
required
|
||||
/>
|
||||
<div class="error-message" id="password-error">
|
||||
Password must be at least 8 characters long
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-row">
|
||||
<!-- <div class="form-group">
|
||||
<label for="date-of-birth">Date of Birth</label>
|
||||
<input
|
||||
type="date"
|
||||
id="date-of-birth"
|
||||
name="date-of-birth"
|
||||
required
|
||||
/>
|
||||
<div class="error-message" id="dob-error">
|
||||
You must be at least 18 years old
|
||||
</div>
|
||||
</div> -->
|
||||
<div class="form-group">
|
||||
<label>Gender</label>
|
||||
<div class="gender-group">
|
||||
<div class="form-group">
|
||||
<input
|
||||
type="radio"
|
||||
id="male"
|
||||
name="gender"
|
||||
value="male"
|
||||
required
|
||||
/>
|
||||
<label for="male">Male</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input
|
||||
type="radio"
|
||||
id="female"
|
||||
name="gender"
|
||||
value="female"
|
||||
required
|
||||
/>
|
||||
<label for="female">Female</label>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<input
|
||||
type="radio"
|
||||
id="other"
|
||||
name="gender"
|
||||
value="other"
|
||||
required
|
||||
/>
|
||||
<label for="other">Other</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- <div class="form-group">
|
||||
<label for="country">Country</label>
|
||||
<select id="country" name="country" required>
|
||||
<option value="">Select your country</option>
|
||||
<option value="us">United States</option>
|
||||
<option value="ca">Canada</option>
|
||||
<option value="uk">United Kingdom</option>
|
||||
<option value="au">Australia</option>
|
||||
<option value="other">Other</option>
|
||||
</select>
|
||||
<div class="error-message" id="country-error">
|
||||
Please select a country
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
<div class="form-group">
|
||||
<button type="submit" class="register-btn">
|
||||
Create Account
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Basic client-side validation
|
||||
document
|
||||
.getElementById("user-registration-form")
|
||||
.addEventListener("submit", function (event) {
|
||||
let isValid = true;
|
||||
|
||||
// First Name Validation
|
||||
const firstName = document.getElementById("first-name");
|
||||
const firstNameError =
|
||||
document.getElementById("first-name-error");
|
||||
if (firstName.value.trim().length < 2) {
|
||||
firstNameError.style.display = "block";
|
||||
isValid = false;
|
||||
} else {
|
||||
firstNameError.style.display = "none";
|
||||
}
|
||||
|
||||
// Last Name Validation
|
||||
const lastName = document.getElementById("last-name");
|
||||
const lastNameError =
|
||||
document.getElementById("last-name-error");
|
||||
if (lastName.value.trim().length < 2) {
|
||||
lastNameError.style.display = "block";
|
||||
isValid = false;
|
||||
} else {
|
||||
lastNameError.style.display = "none";
|
||||
}
|
||||
|
||||
// Email Validation
|
||||
const email = document.getElementById("email");
|
||||
const emailError = document.getElementById("email-error");
|
||||
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
|
||||
if (!emailRegex.test(email.value)) {
|
||||
emailError.style.display = "block";
|
||||
isValid = false;
|
||||
} else {
|
||||
emailError.style.display = "none";
|
||||
}
|
||||
|
||||
// Password Validation
|
||||
const password = document.getElementById("password");
|
||||
const passwordError =
|
||||
document.getElementById("password-error");
|
||||
if (password.value.length < 8) {
|
||||
passwordError.style.display = "block";
|
||||
isValid = false;
|
||||
} else {
|
||||
passwordError.style.display = "none";
|
||||
}
|
||||
|
||||
// // Date of Birth Validation
|
||||
// const dob = document.getElementById("date-of-birth");
|
||||
// const dobError = document.getElementById("dob-error");
|
||||
// const today = new Date();
|
||||
// const birthDate = new Date(dob.value);
|
||||
// let age = today.getFullYear() - birthDate.getFullYear();
|
||||
// const monthDiff = today.getMonth() - birthDate.getMonth();
|
||||
// if (
|
||||
// monthDiff < 0 ||
|
||||
// (monthDiff === 0 &&
|
||||
// today.getDate() < birthDate.getDate())
|
||||
// ) {
|
||||
// age--;
|
||||
// }
|
||||
// if (age < 18) {
|
||||
// dobError.style.display = "block";
|
||||
// isValid = false;
|
||||
// } else {
|
||||
// dobError.style.display = "none";
|
||||
// }
|
||||
|
||||
// // Country Validation
|
||||
// const country = document.getElementById("country");
|
||||
// const countryError =
|
||||
// document.getElementById("country-error");
|
||||
// if (country.value === "") {
|
||||
// countryError.style.display = "block";
|
||||
// isValid = false;
|
||||
// } else {
|
||||
// countryError.style.display = "none";
|
||||
// }
|
||||
|
||||
// Prevent form submission if validation fails
|
||||
if (!isValid) {
|
||||
event.preventDefault();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
41
src/user_controller.v
Normal file
41
src/user_controller.v
Normal file
@@ -0,0 +1,41 @@
|
||||
module main
|
||||
|
||||
import veb
|
||||
import encoding.base64
|
||||
import json
|
||||
|
||||
@['/controller/user/create'; post]
|
||||
pub fn (mut app App) controller_create_user(mut ctx Context) veb.Result {
|
||||
first_name := ctx.form['first_name']
|
||||
last_name := ctx.form['last_name']
|
||||
email := ctx.form['email']
|
||||
password := ctx.form['password']
|
||||
gender := ctx.form['gender']
|
||||
|
||||
// Create a map of field names and their values
|
||||
fields := {
|
||||
'first_name': first_name
|
||||
'last_name': last_name
|
||||
'email': email
|
||||
'password': password
|
||||
// Gender can be optional, so not including it in validation
|
||||
}
|
||||
|
||||
// Check for empty fields
|
||||
mut empty_fields := []string{}
|
||||
for field_name, value in fields {
|
||||
if value == '' {
|
||||
empty_fields << field_name
|
||||
}
|
||||
}
|
||||
|
||||
// If any fields are empty, return error message
|
||||
if empty_fields.len > 0 {
|
||||
return ctx.text('The following fields cannot be empty: ${empty_fields.join(", ")}')
|
||||
}
|
||||
|
||||
app.service_add_user(first_name, last_name, email, password, gender) or {
|
||||
return ctx.text('error: ${err}')
|
||||
}
|
||||
return ctx.text('User created successfully')
|
||||
}
|
@@ -3,8 +3,11 @@ module main
|
||||
@[table: 'users']
|
||||
pub struct User {
|
||||
mut:
|
||||
id int @[primary; sql: serial]
|
||||
username string @[sql_type: 'TEXT'; unique]
|
||||
password string @[sql_type: 'TEXT']
|
||||
tickets []Ticket @[fkey: 'user_id']
|
||||
id int @[primary; sql: serial]
|
||||
first_name string @[sql_type: 'TEXT']
|
||||
last_name string @[sql_type: 'TEXT']
|
||||
email string @[sql_type: 'TEXT'; unique]
|
||||
password string @[sql_type: 'TEXT']
|
||||
gender string @[sql_type: 'TEXT']
|
||||
tickets []Ticket @[fkey: 'user_id']
|
||||
}
|
||||
|
33
src/user_service.v
Normal file
33
src/user_service.v
Normal file
@@ -0,0 +1,33 @@
|
||||
module main
|
||||
|
||||
import crypto.bcrypt
|
||||
import databases
|
||||
|
||||
fn (app &App) service_add_user(first_name string, last_name string, email string, password string, gender string) ! {
|
||||
mut db := databases.create_db_connection()!
|
||||
|
||||
defer {
|
||||
db.close() or { panic(err) }
|
||||
}
|
||||
|
||||
hashed_password := bcrypt.generate_from_password(password.bytes(), bcrypt.min_cost) or {
|
||||
eprintln(err)
|
||||
return err
|
||||
}
|
||||
|
||||
user_model := User{
|
||||
first_name: first_name
|
||||
last_name: last_name
|
||||
email: email
|
||||
password: hashed_password
|
||||
gender: gender
|
||||
}
|
||||
|
||||
mut insert_error := ''
|
||||
sql db {
|
||||
insert user_model into User
|
||||
} or { insert_error = err.msg() }
|
||||
if insert_error != '' {
|
||||
return error(insert_error)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user