Skip to content

Commit 52d27d1

Browse files
committed
improvements
1 parent dc1d36e commit 52d27d1

11 files changed

Lines changed: 372 additions & 53 deletions

File tree

Cargo.lock

Lines changed: 33 additions & 32 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[package]
2-
name = "new-avored"
2+
name = "avored-rust-cms"
33
version = "0.1.0"
44
edition = "2021"
55

@@ -43,6 +43,7 @@ surrealdb = { version = "3.0.0", features = [
4343
"kv-rocksdb",
4444
"kv-mem",
4545
], optional = true }
46+
web-sys = { version = "0.3.77", features = ["Window", "Storage"], optional = true }
4647

4748

4849
[build-dependencies]
@@ -51,7 +52,7 @@ tonic-prost-build = "0.14"
5152

5253
[features]
5354
default = ["ssr"]
54-
hydrate = ["leptos/hydrate", "dep:console_error_panic_hook", "dep:wasm-bindgen"]
55+
hydrate = ["leptos/hydrate", "dep:console_error_panic_hook", "dep:wasm-bindgen", "dep:web-sys"]
5556
ssr = [
5657
"dep:axum",
5758
"dep:tokio",

package-lock.json

Lines changed: 8 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@
3232
"vite": "^7.3.1"
3333
},
3434
"dependencies": {
35-
"axios": "^1.13.2"
35+
"axios": "^1.13.2",
36+
"grpc-web": "^2.0.2"
3637
}
3738
}

public/css/app.css

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/main.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
#[cfg(feature = "ssr")]
2-
use new_avored::error::Result;
2+
use avored_rust_cms::error::Result;
33
#[cfg(feature = "ssr")]
4-
use new_avored::infra::app::create_app;
4+
use avored_rust_cms::infra::app::create_app;
55
#[cfg(feature = "ssr")]
6-
use new_avored::infra::setup::init_app_state;
6+
use avored_rust_cms::infra::setup::init_app_state;
77

88
#[cfg(feature = "ssr")]
99
#[tokio::main]

src/pages/app.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ use leptos_router::{
77

88
use crate::pages::home_page::HomePage;
99
use crate::pages::auth::login_page::LoginPage;
10+
use crate::pages::dashboard::dashboard_page::DashboardPage;
1011

1112

1213
#[component]
@@ -27,6 +28,7 @@ pub fn App() -> impl IntoView {
2728
<Routes fallback=|| "Page not found.".into_view()>
2829
<Route path=StaticSegment("") view=HomePage/>
2930
<Route path=StaticSegment("/login") view=LoginPage/>
31+
<Route path=StaticSegment("/dashboard") view=DashboardPage/>
3032
</Routes>
3133
</main>
3234
</Router>

src/pages/auth/login_page.rs

Lines changed: 95 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,77 @@
11
use leptos::prelude::*;
22
use rust_i18n::t;
33

4+
#[cfg(feature = "ssr")]
5+
pub mod ssr {
6+
use crate::infra::grpc::auth_user::{auth_client::AuthClient, LoginRequest};
7+
use leptos::prelude::ServerFnError;
8+
9+
pub async fn login_user_grpc(email: String, password: String) -> Result<String, ServerFnError> {
10+
let mut client = AuthClient::connect("http://127.0.0.1:3000")
11+
.await
12+
.map_err(|e| ServerFnError::new(format!("Connection failed: {}", e)))?;
13+
14+
let request = tonic::Request::new(LoginRequest { email, password });
15+
16+
let response = client
17+
.login_user(request)
18+
.await
19+
.map_err(|e| ServerFnError::new(format!("gRPC error: {}", e)))?;
20+
21+
let inner = response.into_inner();
22+
if inner.status {
23+
Ok(inner.data)
24+
} else {
25+
Err(ServerFnError::new(inner.data))
26+
}
27+
}
28+
}
29+
30+
#[server(endpoint = "login-user")]
31+
pub async fn login_user(email: String, password: String) -> Result<String, ServerFnError> {
32+
self::ssr::login_user_grpc(email, password).await
33+
}
34+
435
#[component]
536
pub fn LoginPage() -> impl IntoView {
37+
let login_action = ServerAction::<LoginUser>::new();
38+
let email = RwSignal::new(String::new());
39+
let password = RwSignal::new(String::new());
40+
41+
let on_submit = move |ev: leptos::ev::SubmitEvent| {
42+
ev.prevent_default();
43+
login_action.dispatch(LoginUser {
44+
email: email.get(),
45+
password: password.get(),
46+
});
47+
};
48+
49+
let login_result = login_action.value();
50+
51+
Effect::new(move |_| {
52+
if let Some(Ok(token)) = login_result.get() {
53+
if !token.is_empty() {
54+
#[cfg(feature = "hydrate")]
55+
let window = web_sys::window().expect("should have a window");
56+
57+
#[cfg(feature = "hydrate")]
58+
let local_storage = window
59+
.local_storage()
60+
.expect("should have local storage")
61+
.expect("local storage should be available");
662

7-
63+
#[cfg(feature = "hydrate")]
64+
local_storage
65+
.set_item("avored_admin_token", &token)
66+
.expect("should be able to set item in local storage");
867

68+
#[cfg(feature = "hydrate")]
69+
let navigate = leptos_router::hooks::use_navigate();
70+
#[cfg(feature = "hydrate")]
71+
navigate("/", Default::default());
72+
}
73+
}
74+
});
975

1076
view! {
1177
<div class="min-h-screen bg-slate-100 flex flex-col justify-center py-12 sm:px-6 lg:px-8">
@@ -18,12 +84,13 @@ pub fn LoginPage() -> impl IntoView {
1884
{t!("sign_into_your_account")}
1985
</h2>
2086
</div>
87+
/*** empty div for spacing ***/
2188
<div></div>
2289

2390

2491
<div class="mt-8 sm:mx-auto sm:w-full sm:max-w-md">
2592
<div class="bg-white py-8 px-4 shadow sm:rounded-lg sm:px-10">
26-
<form class="space-y-5">
93+
<form class="space-y-5" on:submit=on_submit>
2794
<div>
2895
<label class="block text-sm font-medium text-gray-500 mb-1" for="email">
2996
{t!("email_address")}
@@ -32,7 +99,9 @@ pub fn LoginPage() -> impl IntoView {
3299
id="email"
33100
type="text"
34101
name="email"
35-
autoFocus
102+
autofocus=true
103+
on:input=move |ev| email.set(event_target_value(&ev))
104+
prop:value=email
36105
class="appearance-none rounded-md ring-1 ring-gray-400
37106
relative border-0 block w-full px-3 py-2 placeholder-gray-500 text-gray-900
38107
active::ring-primary-500
@@ -48,6 +117,8 @@ pub fn LoginPage() -> impl IntoView {
48117
<input
49118
id="password"
50119
type="password"
120+
on:input=move |ev| password.set(event_target_value(&ev))
121+
prop:value=password
51122
class="appearance-none rounded-md ring-1 ring-gray-400
52123
relative border-0 block w-full px-3 py-2 placeholder-gray-500 text-gray-900
53124
active::ring-primary-500
@@ -70,21 +141,32 @@ pub fn LoginPage() -> impl IntoView {
70141

71142
<div>
72143
<button
144+
type="submit"
145+
disabled=login_action.pending().get()
73146
class="w-full flex justify-center py-2 px-4 border border-transparent text-sm font-medium rounded-md text-white focus:outline-none focus:ring-2 focus:ring-offset-2 bg-primary-600 hover:bg-primary-500 focus:ring-primary-500"
74147
>
75-
{t!("sign_in")}
148+
{move || if login_action.pending().get() {
149+
t!("signing_in")
150+
} else {
151+
t!("sign_in")
152+
}}
76153
</button>
77154
</div>
78155

79-
// <div class="text-gray-600 text-center text-sm">
80-
// "need_to_change_language"
81-
// <select
82-
// class="outline-none border-none appearance-none pr-8"
83-
// >
84-
// <option>"en"</option>
85-
// <option>{t('fr')}</option>
86-
// </select>
87-
// </div>
156+
<Suspense>
157+
{move || login_result.get().map(|res| match res {
158+
Ok(token) => view! {
159+
<div class="text-green-600 text-sm mt-2">
160+
{format!("Login successful! Token: {}", token)}
161+
</div>
162+
}.into_any(),
163+
Err(e) => view! {
164+
<div class="text-red-600 text-sm mt-2">
165+
{format!("Error: {}", e)}
166+
</div>
167+
}.into_any(),
168+
})}
169+
</Suspense>
88170
</form>
89171
</div>
90172
</div>

0 commit comments

Comments
 (0)