use maud::{html, Markup};
use crate::primitives::{alert, button, input_otp};
#[derive(Clone, Debug)]
pub struct Props {
pub action: String,
pub method: Method,
pub sent_to: String,
pub length: usize,
pub error: Option<String>,
pub resend_url: Option<String>,
pub footer_hint: Option<String>,
pub cancel_url: Option<String>,
pub submit_label: String,
}
impl Default for Props {
fn default() -> Self {
Self {
action: "/auth/2fa/verify".into(),
method: Method::Email,
sent_to: String::new(),
length: 6,
error: None,
resend_url: None,
footer_hint: None,
cancel_url: None,
submit_label: "Verify".into(),
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Method {
Email,
Sms,
Authenticator,
}
pub fn render(props: Props) -> Markup {
let (heading, subheading) = match props.method {
Method::Email => (
"Check your email",
format!(
"We sent a {}-digit code to {}.",
props.length, props.sent_to
),
),
Method::Sms => (
"Check your phone",
format!(
"We texted a {}-digit code to {}.",
props.length, props.sent_to
),
),
Method::Authenticator => (
"Two-factor verification",
format!(
"Enter the {}-digit code from your authenticator app.",
props.length
),
),
};
html! {
div class="mui-block mui-block--auth" {
div class="mui-block__frame mui-block--auth__frame" {
div class="mui-block--auth__card" {
h1 class="mui-block--auth__heading" { (heading) }
p class="mui-block--auth__subheading" { (subheading) }
@if let Some(err) = &props.error {
div class="mui-block--auth__error" {
(alert::render(alert::Props {
title: "Couldn\u{2019}t verify code".into(),
description: Some(err.clone()),
variant: alert::Variant::Danger,
..Default::default()
}))
}
}
form action=(props.action) method="post"
class="mui-block--auth__form" {
div class="mui-block--auth__otp-wrap" {
(input_otp::render(input_otp::Props {
id: "mui-block-auth-otp".into(),
name: "code".into(),
length: props.length,
group_size: if props.length >= 6 { 3 } else { props.length },
disabled: false,
..Default::default()
}))
}
@if let Some(hint) = &props.footer_hint {
p class="mui-block--auth__hint" style="text-align:center;" { (hint) }
}
(button::render(button::Props {
label: props.submit_label.clone(),
variant: button::Variant::Primary,
size: button::Size::Md,
button_type: "submit",
..Default::default()
}))
}
@if let Some(url) = &props.resend_url {
p class="mui-block--auth__footer" {
"Didn\u{2019}t get it? "
a href=(url) class="mui-block--auth__footer-link" { "Resend code" }
}
}
@if let Some(url) = &props.cancel_url {
p class="mui-block--auth__footer" style="margin-top:0;" {
a href=(url) class="mui-block--auth__footer-link" {
"Use a different method"
}
}
}
}
}
}
}
}
pub fn preview() -> Markup {
render(Props {
action: "/auth/2fa/verify".into(),
method: Method::Email,
sent_to: "s***@acme.com".into(),
length: 6,
resend_url: Some("/auth/2fa/resend".into()),
cancel_url: Some("/auth/login".into()),
footer_hint: Some("The code expires in 10 minutes.".into()),
..Default::default()
})
}