1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
use yew::prelude::*;

use embedded_svc::utils::role::Role;

use crate::field::Field;
use crate::loading::*;
use crate::util::if_true;

#[derive(Properties, Clone, Default, Debug, PartialEq)]
pub struct AuthProps {
    #[prop_or_default]
    pub username: String,

    #[prop_or_default]
    pub password: String,

    #[prop_or_default]
    pub auth_failed: bool,

    #[prop_or_default]
    pub authenticating: bool,

    pub submit: Callback<(String, String)>,
}

#[function_component(Auth)]
pub fn auth(props: &AuthProps) -> Html {
    let mut username = Field::text(Ok);
    let mut password = Field::text(Ok);

    username.update(props.username.clone());
    password.update(props.password.clone());

    let disabled = props.authenticating;
    let hidden = if_true(
        !props.auth_failed || props.authenticating,
        "visibility: hidden;",
    );

    let onclick = {
        let username = username.clone();
        let password = password.clone();
        let submit = props.submit.clone();

        Callback::from(move |_| {
            submit.emit((
                username.value().unwrap_or_default(),
                password.value().unwrap_or_default(),
            ))
        })
    };

    html! {
        <div class="box has-text-centered">
            <div class="field">
                <label class="label">{"Username"}</label>
                <div class="control">
                    <input
                        class="input"
                        type="text"
                        placeholder="0..24 characters"
                        value={username.raw_value()}
                        oninput={username.change()}
                        {disabled}
                        />
                </div>
            </div>
            <div class="field">
                <label class="label">{"Password"}</label>
                <div class="control">
                    <input
                        class="input"
                        type="password"
                        placeholder="0..24 characters"
                        value={password.raw_value()}
                        oninput={password.change()}
                        {disabled}
                        />
                </div>
            </div>
            <p class="help is-danger" style={hidden}>{"Invalid username or password"}</p>
            <button
                class={classes!("button", "my-4", if_true(props.authenticating, "is-loading"))}
                {disabled}
                {onclick}
            >
                {"Login"}
            </button>
        </div>
    }
}

#[derive(Properties, Clone, Default, Debug, PartialEq)]
pub struct AuthStateProps {
    pub role: Option<Role>,
}

#[function_component(AuthState)]
pub fn auth_state(props: &AuthStateProps) -> Html {
    match props.role {
        Some(Role::None) => {
            html! {
                <div class="box has-text-centered">{"You are logged out."}</div>
            }
        }
        None => {
            html! {
                <Loading/>
            }
        }
        Some(role) => {
            html! {
                <div class="box has-text-centered">{format!("You are logged in as {}.", role)}</div>
            }
        }
    }
}

#[function_component(NoPerm)]
pub fn no_perm() -> Html {
    html! {
        <div class="box has-text-centered">
            {"You have no permissions to access this content"}
        </div>
    }
}