;(function() { const api = { post(url, body) { return m.request({method: "POST", url, body}) }, } function EmailFormField(formData, key) { return m(".form-field", [ m("label", "Email"), m("input[type=email]", { required: true, autocomplete: "email", value: formData[key], oninput: e => formData[key] = e.target.value, }), ]) } function PasswordFormField(formData, key) { return m(".form-field", [ m("label", "Password"), m("input[type=password]", { required: true, autocomplete: "current-password", value: formData[key], oninput: e => formData[key] = e.target.value, }), ]) } // Login page function LoginPage() { let form = {Email: "", Password: ""} let loading = false let error = null let showPassword = false const next = new URLSearchParams(window.location.search).get("next") || "/dashboard" const submit = e => { e.preventDefault() if (!showPassword) { api.post("/Sso.Check", {Email: form.Email}) .then(r => { if (r.Redirect != "") { window.location = r.Redirect } else { showPassword = true m.redraw() } }) return } loading = true error = null api.post("/Identity.Login", form) .then(() => { window.location.href = next }) .catch(err => { error = (err.response && err.response.Message) || "Invalid email or password"; loading = false; m.redraw() }) } return { view() { return m(".auth-card", [ m("h1", "Log In"), error && m(".error", error), m("form", {onsubmit: submit}, [ EmailFormField(form, "Email"), showPassword && PasswordFormField(form, "Password"), m("button[type=submit].btn", "Log In"), ]), m(".links", [ m(m.route.Link, {href: "/register"}, "Create account"), m("span", "|"), m(m.route.Link, {href: "/forgot-password"}, "Forgot password?"), ]), ]) }, } } // Registration page function RegisterPage() { let form = {Email: "", Password: "", Confirm: ""} let loading = false let error = null let done = false const bind = key => ({value: form[key], oninput: e => form[key] = e.target.value}) const submit = e => { e.preventDefault() if (form.Password !== form.Confirm) { error = "Passwords do not match" return } loading = true error = null api.post("/Account.Register", {Email: form.Email, Password: form.Password}) .then(() => { done = true; m.redraw() }) .catch(err => { error = (err.response && err.response.Message) || "Registration failed"; loading = false; m.redraw() }) } return { view() { if (done) { return m(".auth-card", [ m("h1", "Check Your Email"), m(".success", "We sent a verification link to " + form.Email + ". Please check your inbox to complete registration."), m(".links", [m(m.route.Link, {href: "/login"}, "Back to login")]), ]) } return m(".auth-card", [ m("h1", "Create Account"), error && m(".error", error), m("form", {onsubmit: submit}, [ m(".form-field", [m("label", "Email"), m("input[type=email]", {required: true, autocomplete: "email", ...bind("Email")})]), m(".form-field", [m("label", "Password"), m("input[type=password]", {required: true, autocomplete: "new-password", minlength: 8, ...bind("Password")})]), m(".form-field", [m("label", "Confirm Password"), m("input[type=password]", {required: true, autocomplete: "new-password", ...bind("Confirm")})]), m("button[type=submit].btn", {disabled: loading}, loading ? "Creating account..." : "Create Account"), ]), m(".links", [ m(m.route.Link, {href: "/login"}, "Already have an account? Log in"), ]), ]) }, } } // Forgot password page function ForgotPasswordPage() { let email = "" let loading = false let error = null let done = false const submit = e => { e.preventDefault() loading = true error = null api.post("/Account.RequestPasswordReset", {Email: email}) .then(() => { done = true; m.redraw() }) .catch(err => { error = (err.response && err.response.Message) || "An error occurred"; loading = false; m.redraw() }) } return { view() { if (done) { return m(".auth-card", [ m("h1", "Check Your Email"), m(".success", "If an account exists for " + email + ", we sent a password reset link."), m(".links", [m(m.route.Link, {href: "/login"}, "Back to login")]), ]) } return m(".auth-card", [ m("h1", "Reset Password"), error && m(".error", error), m("form", {onsubmit: submit}, [ m(".form-field", [m("label", "Email"), m("input[type=email]", {required: true, value: email, oninput: e => email = e.target.value})]), m("button[type=submit].btn", {disabled: loading}, loading ? "Sending..." : "Send Reset Link"), ]), m(".links", [m(m.route.Link, {href: "/login"}, "Back to login")]), ]) }, } } // Reset password page (arrives via link with token in query string) function ResetPasswordPage() { const params = new URLSearchParams(window.location.search) const token = params.get("token") || "" let form = {Password: "", Confirm: ""} let loading = false let error = null let done = false const bind = key => ({value: form[key], oninput: e => form[key] = e.target.value}) const submit = e => { e.preventDefault() if (form.Password !== form.Confirm) { error = "Passwords do not match" return } loading = true error = null api.post("/Account.ResetPassword", {Token: token, Password: form.Password}) .then(() => { done = true; m.redraw() }) .catch(err => { error = (err.response && err.response.Message) || "Reset failed"; loading = false; m.redraw() }) } return { view() { if (!token) { return m(".auth-card", [ m("h1", "Invalid Link"), m(".error", "This password reset link is invalid or has expired."), m(".links", [m(m.route.Link, {href: "/forgot-password"}, "Request a new link")]), ]) } if (done) { return m(".auth-card", [ m("h1", "Password Reset"), m(".success", "Your password has been reset successfully."), m(".links", [m(m.route.Link, {href: "/login"}, "Log in")]), ]) } return m(".auth-card", [ m("h1", "Set New Password"), error && m(".error", error), m("form", {onsubmit: submit}, [ m(".form-field", [m("label", "New Password"), m("input[type=password]", {required: true, autocomplete: "new-password", minlength: 8, ...bind("Password")})]), m(".form-field", [m("label", "Confirm Password"), m("input[type=password]", {required: true, autocomplete: "new-password", ...bind("Confirm")})]), m("button[type=submit].btn", {disabled: loading}, loading ? "Resetting..." : "Reset Password"), ]), ]) }, } } // Email verification page (arrives via link with token in query string) function VerifyPage() { const params = new URLSearchParams(window.location.search) const token = params.get("token") || "" let loading = true let error = null let done = false if (token) { api.post("/Account.Verify", {Token: token}) .then(() => { done = true; loading = false; m.redraw() }) .catch(err => { error = (err.response && err.response.Message) || "Verification failed"; loading = false; m.redraw() }) } else { loading = false error = "Invalid verification link." } return { view() { if (loading) { return m(".auth-card", [m("h1", "Verifying..."), m(".success", "Please wait...")]) } if (done) { return m(".auth-card", [ m("h1", "Email Verified"), m(".success", "Your email has been verified. You can now log in."), m(".links", [m(m.route.Link, {href: "/login"}, "Log in")]), ]) } return m(".auth-card", [ m("h1", "Verification Failed"), m(".error", error), m(".links", [m(m.route.Link, {href: "/login"}, "Back to login")]), ]) }, } } m.route.prefix = "" m.route(document.getElementById("app"), "/login", { "/login": LoginPage, "/register": RegisterPage, "/forgot-password": ForgotPasswordPage, "/reset-password": ResetPasswordPage, "/verify": VerifyPage, }) })()