007
This commit is contained in:
@@ -3,12 +3,14 @@ module main
|
|||||||
import veb
|
import veb
|
||||||
import db.sqlite
|
import db.sqlite
|
||||||
import veb.auth
|
import veb.auth
|
||||||
|
import veb.assets
|
||||||
|
|
||||||
pub struct App {
|
pub struct App {
|
||||||
veb.StaticHandler
|
veb.StaticHandler
|
||||||
pub mut:
|
pub mut:
|
||||||
db sqlite.DB
|
db sqlite.DB
|
||||||
auth auth.Auth[sqlite.DB]
|
auth auth.Auth[sqlite.DB]
|
||||||
|
am assets.AssetManager
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct Context {
|
pub struct Context {
|
||||||
|
@@ -4,6 +4,7 @@
|
|||||||
<meta charset="UTF-8" />
|
<meta charset="UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
<title>User Registration</title>
|
<title>User Registration</title>
|
||||||
|
@css '/static/css/bootstrap.css'
|
||||||
<style>
|
<style>
|
||||||
/* Reset and Base Styles */
|
/* Reset and Base Styles */
|
||||||
* {
|
* {
|
||||||
@@ -132,8 +133,52 @@
|
|||||||
margin-top: 5px;
|
margin-top: 5px;
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Field error styling */
|
||||||
|
.field-error {
|
||||||
|
color: #e74c3c;
|
||||||
|
font-size: 0.9em;
|
||||||
|
font-weight: normal;
|
||||||
|
margin-left: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Style for input fields with errors */
|
||||||
|
.input-error {
|
||||||
|
border: 1px solid #e74c3c !important;
|
||||||
|
background-color: #fff8f8;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Spinner styling */
|
||||||
|
.spinner {
|
||||||
|
text-align: center;
|
||||||
|
padding: 10px;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.htmx-request .htmx-indicator {
|
||||||
|
display: block !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Alert styling */
|
||||||
|
.alert {
|
||||||
|
padding: 15px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert-danger {
|
||||||
|
background-color: #f8d7da;
|
||||||
|
color: #721c24;
|
||||||
|
border: 1px solid #f5c6cb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert-success {
|
||||||
|
background-color: #d4edda;
|
||||||
|
color: #155724;
|
||||||
|
border: 1px solid #c3e6cb;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
<script src="https://unpkg.com/htmx.org@2.0.4"></script>
|
@js 'https://unpkg.com/htmx.org@2.0.4'
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<div class="container">
|
<div class="container">
|
||||||
@@ -147,34 +192,42 @@
|
|||||||
id="user-registration-form"
|
id="user-registration-form"
|
||||||
hx-trigger="submit"
|
hx-trigger="submit"
|
||||||
hx-post="/controller/user/create"
|
hx-post="/controller/user/create"
|
||||||
hx-target="#user-registration-form"
|
hx-target="#form-response"
|
||||||
|
hx-swap="innerHTML"
|
||||||
|
hx-indicator="#form-spinner"
|
||||||
>
|
>
|
||||||
|
<!-- Response container at the top of the form -->
|
||||||
|
<div id="form-response"></div>
|
||||||
|
|
||||||
|
<!-- Loading indicator -->
|
||||||
|
<div
|
||||||
|
id="form-spinner"
|
||||||
|
class="htmx-indicator"
|
||||||
|
style="display: none"
|
||||||
|
>
|
||||||
|
<div class="spinner">Processing...</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="form-row">
|
<div class="form-row">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="first-name">First Name</label>
|
<label for="first-name">First Name <span class="field-error first_name-error"></span></label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
id="first-name"
|
id="first-name"
|
||||||
name="first_name"
|
name="first_name"
|
||||||
placeholder="Enter your first name"
|
placeholder="Enter your first name"
|
||||||
required
|
|
||||||
/>
|
/>
|
||||||
<div class="error-message" id="first-name-error">
|
<div class="error-message" id="first-name-error">
|
||||||
Please enter a valid first name
|
Please enter a valid first name
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div
|
<div class="form-group">
|
||||||
class="form-group"
|
<label for="last-name">Last Name <span class="field-error last_name-error"></span></label>
|
||||||
hx-target="this"
|
|
||||||
hx-swap="outerHTML"
|
|
||||||
>
|
|
||||||
<label for="last-name">Last Name</label>
|
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
id="last-name"
|
id="last-name"
|
||||||
name="last_name"
|
name="last_name"
|
||||||
placeholder="Enter your last name"
|
placeholder="Enter your last name"
|
||||||
required
|
|
||||||
/>
|
/>
|
||||||
<div class="error-message" id="last-name-error">
|
<div class="error-message" id="last-name-error">
|
||||||
Please enter a valid last name
|
Please enter a valid last name
|
||||||
@@ -183,13 +236,12 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="email">Email Address</label>
|
<label for="email">Email Address <span class="field-error email-error"></span></label>
|
||||||
<input
|
<input
|
||||||
type="email"
|
type="email"
|
||||||
id="email"
|
id="email"
|
||||||
name="email"
|
name="email"
|
||||||
placeholder="Enter your email"
|
placeholder="Enter your email"
|
||||||
required
|
|
||||||
/>
|
/>
|
||||||
<div class="error-message" id="email-error">
|
<div class="error-message" id="email-error">
|
||||||
Please enter a valid email address
|
Please enter a valid email address
|
||||||
@@ -197,13 +249,12 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="password">Password</label>
|
<label for="password">Password <span class="field-error password-error"></span></label>
|
||||||
<input
|
<input
|
||||||
type="password"
|
type="password"
|
||||||
id="password"
|
id="password"
|
||||||
name="password"
|
name="password"
|
||||||
placeholder="Create a strong password"
|
placeholder="Create a strong password"
|
||||||
required
|
|
||||||
/>
|
/>
|
||||||
<div class="error-message" id="password-error">
|
<div class="error-message" id="password-error">
|
||||||
Password must be at least 8 characters long
|
Password must be at least 8 characters long
|
||||||
@@ -224,7 +275,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div> -->
|
</div> -->
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label>Gender</label>
|
<label>Gender <span class="field-error gender-error"></span></label>
|
||||||
<div class="gender-group">
|
<div class="gender-group">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<input
|
<input
|
||||||
@@ -232,7 +283,6 @@
|
|||||||
id="male"
|
id="male"
|
||||||
name="gender"
|
name="gender"
|
||||||
value="male"
|
value="male"
|
||||||
required
|
|
||||||
/>
|
/>
|
||||||
<label for="male">Male</label>
|
<label for="male">Male</label>
|
||||||
</div>
|
</div>
|
||||||
@@ -242,7 +292,6 @@
|
|||||||
id="female"
|
id="female"
|
||||||
name="gender"
|
name="gender"
|
||||||
value="female"
|
value="female"
|
||||||
required
|
|
||||||
/>
|
/>
|
||||||
<label for="female">Female</label>
|
<label for="female">Female</label>
|
||||||
</div>
|
</div>
|
||||||
@@ -252,7 +301,6 @@
|
|||||||
id="other"
|
id="other"
|
||||||
name="gender"
|
name="gender"
|
||||||
value="other"
|
value="other"
|
||||||
required
|
|
||||||
/>
|
/>
|
||||||
<label for="other">Other</label>
|
<label for="other">Other</label>
|
||||||
</div>
|
</div>
|
||||||
@@ -283,95 +331,6 @@
|
|||||||
</form>
|
</form>
|
||||||
</section>
|
</section>
|
||||||
</div>
|
</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>
|
</body>
|
||||||
|
@js '/static/js/bootstrap.js'
|
||||||
</html>
|
</html>
|
||||||
|
@@ -2,11 +2,21 @@
|
|||||||
id="user-registration-form"
|
id="user-registration-form"
|
||||||
hx-trigger="submit"
|
hx-trigger="submit"
|
||||||
hx-post="/controller/user/create"
|
hx-post="/controller/user/create"
|
||||||
hx-target="#user-registration-form"
|
hx-target="#form-response"
|
||||||
|
hx-swap="innerHTML"
|
||||||
|
hx-indicator="#form-spinner"
|
||||||
>
|
>
|
||||||
|
<!-- Add a response container at the top of the form -->
|
||||||
|
<div id="form-response"></div>
|
||||||
|
|
||||||
|
<!-- Add a loading indicator -->
|
||||||
|
<div id="form-spinner" class="htmx-indicator" style="display: none;">
|
||||||
|
<div class="spinner">Processing...</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="form-row">
|
<div class="form-row">
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="first-name">First Name</label>
|
<label for="first-name">First Name <span class="field-error first_name-error"></span></label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
id="first-name"
|
id="first-name"
|
||||||
@@ -18,8 +28,8 @@
|
|||||||
Please enter a valid first name
|
Please enter a valid first name
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="form-group" hx-target="this" hx-swap="outerHTML">
|
<div class="form-group">
|
||||||
<label for="last-name">Last Name</label>
|
<label for="last-name">Last Name <span class="field-error last_name-error"></span></label>
|
||||||
<input
|
<input
|
||||||
type="text"
|
type="text"
|
||||||
id="last-name"
|
id="last-name"
|
||||||
@@ -34,7 +44,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="email">Email Address</label>
|
<label for="email">Email Address <span class="field-error email-error"></span></label>
|
||||||
<input
|
<input
|
||||||
type="email"
|
type="email"
|
||||||
id="email"
|
id="email"
|
||||||
@@ -48,7 +58,7 @@
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="form-group">
|
<div class="form-group">
|
||||||
<label for="password">Password</label>
|
<label for="password">Password <span class="field-error password-error"></span></label>
|
||||||
<input
|
<input
|
||||||
type="password"
|
type="password"
|
||||||
id="password"
|
id="password"
|
||||||
@@ -130,3 +140,63 @@
|
|||||||
<button type="submit" class="register-btn">Create Account</button>
|
<button type="submit" class="register-btn">Create Account</button>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
/* Add styles for the alerts and spinner */
|
||||||
|
.alert {
|
||||||
|
padding: 15px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert-danger {
|
||||||
|
background-color: #f8d7da;
|
||||||
|
color: #721c24;
|
||||||
|
border: 1px solid #f5c6cb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.alert-success {
|
||||||
|
background-color: #d4edda;
|
||||||
|
color: #155724;
|
||||||
|
border: 1px solid #c3e6cb;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Field error styling */
|
||||||
|
.field-error {
|
||||||
|
color: #e74c3c;
|
||||||
|
font-size: 0.9em;
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Style for input fields with errors */
|
||||||
|
.input-error {
|
||||||
|
border: 1px solid #e74c3c !important;
|
||||||
|
background-color: #fff8f8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn {
|
||||||
|
display: inline-block;
|
||||||
|
padding: 8px 16px;
|
||||||
|
text-decoration: none;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-primary {
|
||||||
|
background-color: #3498db;
|
||||||
|
color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mt-3 {
|
||||||
|
margin-top: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spinner {
|
||||||
|
text-align: center;
|
||||||
|
padding: 10px;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
.htmx-request .htmx-indicator {
|
||||||
|
display: block !important;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
@@ -3,20 +3,14 @@ module main
|
|||||||
import veb
|
import veb
|
||||||
|
|
||||||
@['/controller/user/create'; post]
|
@['/controller/user/create'; post]
|
||||||
pub fn (mut app App) controller_create_user(mut ctx Context) veb.Result {
|
pub fn (mut app App) controller_create_user(mut ctx Context, first_name string, last_name string, email string, password string, gender string) 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
|
// Create a map of field names and their values
|
||||||
fields := {
|
fields := {
|
||||||
'first_name': first_name
|
'first_name': first_name
|
||||||
'last_name': last_name
|
'last_name': last_name
|
||||||
'email': email
|
'email': email
|
||||||
'password': password
|
'password': password
|
||||||
// Gender can be optional, so not including it in validation
|
'gender': gender
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for empty fields
|
// Check for empty fields
|
||||||
@@ -27,13 +21,38 @@ pub fn (mut app App) controller_create_user(mut ctx Context) veb.Result {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If any fields are empty, return error message
|
// If any fields are empty, return field-specific error messages
|
||||||
if empty_fields.len > 0 {
|
if empty_fields.len > 0 {
|
||||||
return ctx.text('The following fields cannot be empty: ${empty_fields.join(', ')}')
|
mut response := '<script>'
|
||||||
|
|
||||||
|
// Reset all field errors first
|
||||||
|
response += 'document.querySelectorAll(".field-error").forEach(el => el.textContent = "");'
|
||||||
|
response += 'document.querySelectorAll("input").forEach(el => el.classList.remove("input-error"));'
|
||||||
|
|
||||||
|
// Set error for each empty field
|
||||||
|
for field in empty_fields {
|
||||||
|
response += 'document.querySelector(".' + field + '-error").textContent = "(Required)";'
|
||||||
|
response += 'document.querySelector("[name=' + field + ']").classList.add("input-error");'
|
||||||
}
|
}
|
||||||
|
|
||||||
app.service_add_user(first_name, last_name, email, password, gender) or {
|
response += '</script>'
|
||||||
return ctx.text('error: ${err}')
|
response += '<div class="alert alert-danger">Please fill in all required fields</div>'
|
||||||
|
|
||||||
|
return ctx.html(response)
|
||||||
}
|
}
|
||||||
return ctx.text('User created successfully')
|
|
||||||
|
// Try to add the user
|
||||||
|
app.service_add_user(first_name, last_name, email, password, gender) or {
|
||||||
|
error_html := '<div class="alert alert-danger">Error: ${err}</div>'
|
||||||
|
return ctx.html(error_html)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return success message with HTML
|
||||||
|
success_html := '<div class="alert alert-success">
|
||||||
|
<h4>User created successfully!</h4>
|
||||||
|
<p>Welcome, ${first_name}! Your account has been created.</p>
|
||||||
|
<a href="/login" class="btn btn-primary mt-3">Login Now</a>
|
||||||
|
</div>'
|
||||||
|
|
||||||
|
return ctx.html(success_html)
|
||||||
}
|
}
|
||||||
|
12057
static/css/bootstrap.css
vendored
Normal file
12057
static/css/bootstrap.css
vendored
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user