This commit is contained in:
2025-03-06 02:56:45 +05:30
parent ca4dda7dcd
commit a1c43ec977
5 changed files with 12234 additions and 127 deletions

View File

@@ -3,12 +3,14 @@ module main
import veb
import db.sqlite
import veb.auth
import veb.assets
pub struct App {
veb.StaticHandler
pub mut:
db sqlite.DB
auth auth.Auth[sqlite.DB]
am assets.AssetManager
}
pub struct Context {

View File

@@ -4,6 +4,7 @@
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>User Registration</title>
@css '/static/css/bootstrap.css'
<style>
/* Reset and Base Styles */
* {
@@ -132,8 +133,52 @@
margin-top: 5px;
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>
<script src="https://unpkg.com/htmx.org@2.0.4"></script>
@js 'https://unpkg.com/htmx.org@2.0.4'
</head>
<body>
<div class="container">
@@ -147,34 +192,42 @@
id="user-registration-form"
hx-trigger="submit"
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-group">
<label for="first-name">First Name</label>
<label for="first-name">First Name <span class="field-error first_name-error"></span></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>
<div class="form-group">
<label for="last-name">Last Name <span class="field-error last_name-error"></span></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
@@ -183,13 +236,12 @@
</div>
<div class="form-group">
<label for="email">Email Address</label>
<label for="email">Email Address <span class="field-error email-error"></span></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
@@ -197,13 +249,12 @@
</div>
<div class="form-group">
<label for="password">Password</label>
<label for="password">Password <span class="field-error password-error"></span></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
@@ -224,7 +275,7 @@
</div>
</div> -->
<div class="form-group">
<label>Gender</label>
<label>Gender <span class="field-error gender-error"></span></label>
<div class="gender-group">
<div class="form-group">
<input
@@ -232,7 +283,6 @@
id="male"
name="gender"
value="male"
required
/>
<label for="male">Male</label>
</div>
@@ -242,7 +292,6 @@
id="female"
name="gender"
value="female"
required
/>
<label for="female">Female</label>
</div>
@@ -252,7 +301,6 @@
id="other"
name="gender"
value="other"
required
/>
<label for="other">Other</label>
</div>
@@ -283,95 +331,6 @@
</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>
@js '/static/js/bootstrap.js'
</html>

View File

@@ -2,11 +2,21 @@
id="user-registration-form"
hx-trigger="submit"
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-group">
<label for="first-name">First Name</label>
<label for="first-name">First Name <span class="field-error first_name-error"></span></label>
<input
type="text"
id="first-name"
@@ -18,8 +28,8 @@
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>
<div class="form-group">
<label for="last-name">Last Name <span class="field-error last_name-error"></span></label>
<input
type="text"
id="last-name"
@@ -34,7 +44,7 @@
</div>
<div class="form-group">
<label for="email">Email Address</label>
<label for="email">Email Address <span class="field-error email-error"></span></label>
<input
type="email"
id="email"
@@ -48,7 +58,7 @@
</div>
<div class="form-group">
<label for="password">Password</label>
<label for="password">Password <span class="field-error password-error"></span></label>
<input
type="password"
id="password"
@@ -130,3 +140,63 @@
<button type="submit" class="register-btn">Create Account</button>
</div>
</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>

View File

@@ -3,20 +3,14 @@ module main
import veb
@['/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']
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 {
// 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
'gender': gender
}
// 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 {
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");'
}
response += '</script>'
response += '<div class="alert alert-danger">Please fill in all required fields</div>'
return ctx.html(response)
}
// Try to add the user
app.service_add_user(first_name, last_name, email, password, gender) or {
return ctx.text('error: ${err}')
error_html := '<div class="alert alert-danger">Error: ${err}</div>'
return ctx.html(error_html)
}
return ctx.text('User created successfully')
// 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

File diff suppressed because it is too large Load Diff