Compare commits
2 Commits
eb9c2b1da1
...
8ccf9f281c
Author | SHA1 | Date | |
---|---|---|---|
8ccf9f281c | |||
a9df6fa559 |
@ -24,22 +24,6 @@ func (h *UserHandler) RegisterRoutes(api chi.Router) {
|
|||||||
api.Post("/register", h.register)
|
api.Post("/register", h.register)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (h *UserHandler) loginPage(w http.ResponseWriter, r *http.Request) {
|
|
||||||
data := map[string]any{
|
|
||||||
"Title": "Login",
|
|
||||||
}
|
|
||||||
|
|
||||||
web.RenderTemplate(w, "login", data)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (h *UserHandler) registerPage(w http.ResponseWriter, r *http.Request) {
|
|
||||||
data := map[string]any{
|
|
||||||
"Title": "Register",
|
|
||||||
}
|
|
||||||
|
|
||||||
web.RenderTemplate(w, "register", data)
|
|
||||||
}
|
|
||||||
|
|
||||||
type RegisterParams struct {
|
type RegisterParams struct {
|
||||||
FullName string `json:"full_name"`
|
FullName string `json:"full_name"`
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
|
@ -1,14 +0,0 @@
|
|||||||
|
|
||||||
@import url('https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap');
|
|
||||||
|
|
||||||
* {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
box-sizing: border-box;
|
|
||||||
}
|
|
||||||
|
|
||||||
html, body {
|
|
||||||
height: 100%;
|
|
||||||
font-family: Inter, Arial, sans-serif;
|
|
||||||
background-color: #f2f2f2;
|
|
||||||
}
|
|
@ -1,20 +0,0 @@
|
|||||||
|
|
||||||
.footer {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 1rem;
|
|
||||||
text-align: center;
|
|
||||||
color: #0005;
|
|
||||||
font-size: 0.9rem;
|
|
||||||
user-select: none;
|
|
||||||
pointer-events: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media only screen and (max-width: 450px) {
|
|
||||||
.footer {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 1rem;
|
|
||||||
left: 50%;
|
|
||||||
translate: -50% 0;
|
|
||||||
z-index: 999;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,106 +0,0 @@
|
|||||||
@import "./ui.css";
|
|
||||||
|
|
||||||
.container {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
min-height: 100vh;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
padding: 2rem;
|
|
||||||
position: relative;
|
|
||||||
background: #ffffff;
|
|
||||||
background: linear-gradient(0deg,rgba(255, 255, 255, 1) 90%, rgba(30, 144, 255, 1) 100%);
|
|
||||||
background-image: url(/static/overlay.jpg);
|
|
||||||
background-position: center;
|
|
||||||
background-size: cover;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-box {
|
|
||||||
background-color: white;
|
|
||||||
padding: 2rem;
|
|
||||||
border-radius: 8px;
|
|
||||||
border: 1px solid #ddd;
|
|
||||||
max-width: 400px;
|
|
||||||
width: 100%;
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-title {
|
|
||||||
font-size: 28px;
|
|
||||||
font-weight: 600;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-description {
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: 400;
|
|
||||||
margin-bottom: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form {
|
|
||||||
margin-top: 25px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.validation_box {
|
|
||||||
display: none;
|
|
||||||
width: 100%;
|
|
||||||
padding: 5px;
|
|
||||||
background: #D0000011;
|
|
||||||
border: 1px solid #D00000aa;
|
|
||||||
border-radius: 5px;
|
|
||||||
margin: 10px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.validation_box__msg {
|
|
||||||
color: #111;
|
|
||||||
font-size: 0.85rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.success_box {
|
|
||||||
display: none;
|
|
||||||
width: 100%;
|
|
||||||
padding: 5px;
|
|
||||||
background: #6AB54711;
|
|
||||||
border: 1px solid #6AB547aa;
|
|
||||||
border-radius: 5px;
|
|
||||||
margin: 10px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.success_box__msg {
|
|
||||||
color: #111;
|
|
||||||
font-size: 0.85rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login_link {
|
|
||||||
font-size: 0.8rem;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
justify-content: center;
|
|
||||||
margin: 20px 0 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login_link > a {
|
|
||||||
padding-left: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media only screen and (max-width: 450px) {
|
|
||||||
.container {
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
width: 100vw;
|
|
||||||
height: 100vh;
|
|
||||||
min-width: 100vw;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-box {
|
|
||||||
flex: 1;
|
|
||||||
width: 100vw;
|
|
||||||
min-width: 100vw;
|
|
||||||
height: 100vh;
|
|
||||||
padding: 20px;
|
|
||||||
padding-top: 50px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,106 +0,0 @@
|
|||||||
@import "./ui.css";
|
|
||||||
|
|
||||||
.container {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
min-height: 100vh;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
padding: 2rem;
|
|
||||||
position: relative;
|
|
||||||
background: #ffffff;
|
|
||||||
background: linear-gradient(0deg,rgba(255, 255, 255, 1) 90%, rgba(30, 144, 255, 1) 100%);
|
|
||||||
background-image: url(/static/overlay.jpg);
|
|
||||||
background-position: center;
|
|
||||||
background-size: cover;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-box {
|
|
||||||
background-color: white;
|
|
||||||
padding: 2rem;
|
|
||||||
border-radius: 8px;
|
|
||||||
border: 1px solid #ddd;
|
|
||||||
max-width: 400px;
|
|
||||||
width: 100%;
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-title {
|
|
||||||
font-size: 28px;
|
|
||||||
font-weight: 600;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-description {
|
|
||||||
font-size: 16px;
|
|
||||||
font-weight: 400;
|
|
||||||
margin-bottom: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.form {
|
|
||||||
margin-top: 25px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.validation_box {
|
|
||||||
display: none;
|
|
||||||
width: 100%;
|
|
||||||
padding: 5px;
|
|
||||||
background: #D0000011;
|
|
||||||
border: 1px solid #D00000aa;
|
|
||||||
border-radius: 5px;
|
|
||||||
margin: 10px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.validation_box__msg {
|
|
||||||
color: #111;
|
|
||||||
font-size: 0.85rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.success_box {
|
|
||||||
display: none;
|
|
||||||
width: 100%;
|
|
||||||
padding: 5px;
|
|
||||||
background: #6AB54711;
|
|
||||||
border: 1px solid #6AB547aa;
|
|
||||||
border-radius: 5px;
|
|
||||||
margin: 10px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.success_box__msg {
|
|
||||||
color: #111;
|
|
||||||
font-size: 0.85rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login_link {
|
|
||||||
font-size: 0.8rem;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
justify-content: center;
|
|
||||||
margin: 20px 0 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.login_link > a {
|
|
||||||
padding-left: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media only screen and (max-width: 450px) {
|
|
||||||
.container {
|
|
||||||
padding: 0;
|
|
||||||
margin: 0;
|
|
||||||
width: 100vw;
|
|
||||||
height: 100vh;
|
|
||||||
min-width: 100vw;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-box {
|
|
||||||
flex: 1;
|
|
||||||
width: 100vw;
|
|
||||||
min-width: 100vw;
|
|
||||||
height: 100vh;
|
|
||||||
padding: 20px;
|
|
||||||
padding-top: 50px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,73 +0,0 @@
|
|||||||
|
|
||||||
.input-group {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: stretch;
|
|
||||||
border: 1px solid #ccc;
|
|
||||||
border-radius: 5px;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-icon {
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-field {
|
|
||||||
flex: 1;
|
|
||||||
border: none;
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-field:focus {
|
|
||||||
border: none;
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-icon {
|
|
||||||
font-size: 1rem;
|
|
||||||
color: #777;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-icon > img {
|
|
||||||
width: 1rem;
|
|
||||||
height: 1rem;
|
|
||||||
}
|
|
||||||
|
|
||||||
.checkbox-group {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: row;
|
|
||||||
align-items: flex-start;
|
|
||||||
gap: 10px;
|
|
||||||
font-size: 0.85rem;
|
|
||||||
color: #0008;
|
|
||||||
margin: 15px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button {
|
|
||||||
display: block;
|
|
||||||
padding: 10px 15px;
|
|
||||||
border-radius: 5px;
|
|
||||||
cursor: pointer;
|
|
||||||
border: none;
|
|
||||||
outline: none;
|
|
||||||
width: 100%;
|
|
||||||
font-size: 0.9rem;
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
.button.primary {
|
|
||||||
background-color: rgb(13, 112, 212);
|
|
||||||
color: #fefefe;
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-wrapper {
|
|
||||||
display: flex;
|
|
||||||
justify-content: center;
|
|
||||||
align-items: center;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon {
|
|
||||||
width: 64px;
|
|
||||||
height: 64px;
|
|
||||||
}
|
|
BIN
static/icon.png
BIN
static/icon.png
Binary file not shown.
Before Width: | Height: | Size: 9.5 KiB |
Binary file not shown.
Before Width: | Height: | Size: 499 B |
Binary file not shown.
Before Width: | Height: | Size: 584 B |
Binary file not shown.
Before Width: | Height: | Size: 563 B |
@ -1,71 +0,0 @@
|
|||||||
const $form = document.querySelector(".form")
|
|
||||||
|
|
||||||
const $validationBox = document.querySelector("#validationBox")
|
|
||||||
const $validationMsg = document.querySelector("#validationMsg")
|
|
||||||
|
|
||||||
const $successBox = document.querySelector("#successBox")
|
|
||||||
const $successMsg = document.querySelector("#successMsg")
|
|
||||||
|
|
||||||
const showError = (message) => {
|
|
||||||
$validationBox.style.display = "block"
|
|
||||||
$validationMsg.innerText = message
|
|
||||||
}
|
|
||||||
|
|
||||||
const clearError = () => {
|
|
||||||
$validationMsg.innerText = ""
|
|
||||||
$validationBox.style.display = "none"
|
|
||||||
}
|
|
||||||
|
|
||||||
const showSuccess = (message) => {
|
|
||||||
$successBox.style.display = "block"
|
|
||||||
$successMsg.innerText = message
|
|
||||||
}
|
|
||||||
|
|
||||||
const clearSuccess = () => {
|
|
||||||
$successMsg.innerText = ""
|
|
||||||
$successBox.style.display = "none"
|
|
||||||
}
|
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", (e) => {
|
|
||||||
$form.addEventListener("submit", async (e) => {
|
|
||||||
e.preventDefault()
|
|
||||||
|
|
||||||
clearError()
|
|
||||||
clearSuccess()
|
|
||||||
|
|
||||||
const formData = new FormData($form)
|
|
||||||
const data = Object.fromEntries(formData)
|
|
||||||
|
|
||||||
if ([{key: "email", name: "Email"}, {key: "password", name: "Password"}].some(({key, name}) => {
|
|
||||||
if (!data[key]) {
|
|
||||||
showError(`${name} is required`)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
})) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await fetch("/api/v1/login", {
|
|
||||||
method: "POST",
|
|
||||||
body: JSON.stringify(data),
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
if (response.status != 200) {
|
|
||||||
const json = await response.json()
|
|
||||||
const text = json.error || "Unexpected error happened"
|
|
||||||
showError(`Failed to log into account. ${text[0].toUpperCase() + text.slice(1)}`)
|
|
||||||
} else {
|
|
||||||
showSuccess("Successfully logged into your account")
|
|
||||||
$form.reset()
|
|
||||||
}
|
|
||||||
} catch(err) {
|
|
||||||
showError("Failed to log into account. Unexpected error happened")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
@ -1,84 +0,0 @@
|
|||||||
|
|
||||||
const $form = document.querySelector(".form")
|
|
||||||
|
|
||||||
const $validationBox = document.querySelector("#validationBox")
|
|
||||||
const $validationMsg = document.querySelector("#validationMsg")
|
|
||||||
|
|
||||||
const $successBox = document.querySelector("#successBox")
|
|
||||||
const $successMsg = document.querySelector("#successMsg")
|
|
||||||
|
|
||||||
const showError = (message) => {
|
|
||||||
$validationBox.style.display = "block"
|
|
||||||
$validationMsg.innerText = message
|
|
||||||
}
|
|
||||||
|
|
||||||
const clearError = () => {
|
|
||||||
$validationMsg.innerText = ""
|
|
||||||
$validationBox.style.display = "none"
|
|
||||||
}
|
|
||||||
|
|
||||||
const showSuccess = (message) => {
|
|
||||||
$successBox.style.display = "block"
|
|
||||||
$successMsg.innerText = message
|
|
||||||
}
|
|
||||||
|
|
||||||
const clearSuccess = () => {
|
|
||||||
$successMsg.innerText = ""
|
|
||||||
$successBox.style.display = "none"
|
|
||||||
}
|
|
||||||
|
|
||||||
document.addEventListener("DOMContentLoaded", (e) => {
|
|
||||||
$form.addEventListener("submit", async (e) => {
|
|
||||||
e.preventDefault()
|
|
||||||
|
|
||||||
clearError()
|
|
||||||
clearSuccess()
|
|
||||||
|
|
||||||
const formData = new FormData($form)
|
|
||||||
const data = Object.fromEntries(formData)
|
|
||||||
|
|
||||||
if ([{key: "full_name", name: "Full Name"}, {key: "email", name: "Email"}, {key: "password", name: "Password"}, {key: "repeat_password", name: "Password"}].some(({key, name}) => {
|
|
||||||
if (!data[key]) {
|
|
||||||
showError(`${name} is required`)
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
})) {
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.repeat_password != data.password) {
|
|
||||||
console.log("passwords do not match")
|
|
||||||
showError("Password does not match")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
if (data.terms_and_conditions !== "on") {
|
|
||||||
console.log("terms and conditions are not accepted")
|
|
||||||
showError("Terms and Conditions are not accepted. Cannot proceed without agreement")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await fetch("/api/v1/register", {
|
|
||||||
method: "POST",
|
|
||||||
body: JSON.stringify(data),
|
|
||||||
headers: {
|
|
||||||
"Content-Type": "application/json",
|
|
||||||
},
|
|
||||||
})
|
|
||||||
if (response.status != 200) {
|
|
||||||
const json = await response.json()
|
|
||||||
const text = json.error || "Unexpected error happened"
|
|
||||||
showError(`Failed to create an account. ${text[0].toUpperCase() + text.slice(1)}`)
|
|
||||||
} else {
|
|
||||||
showSuccess("Account has been created. You can now log into your new account")
|
|
||||||
$form.reset()
|
|
||||||
}
|
|
||||||
} catch(err) {
|
|
||||||
showError("Failed to create account. Unexpected error happened")
|
|
||||||
}
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 279 KiB |
Reference in New Issue
Block a user