11use leptos:: prelude:: * ;
22use 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]
536pub 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