From 0dce7e57620dafc18ba5cc8e347fe5a9a47dde16 Mon Sep 17 00:00:00 2001
From: Philippe Pittoli <karchnu@karchnu.fr>
Date: Thu, 21 Mar 2024 00:16:59 +0100
Subject: [PATCH] Authentication page: tabs!

---
 src/App/Page/Authentication.purs | 71 ++++++++++++++++++++++++++------
 1 file changed, 58 insertions(+), 13 deletions(-)

diff --git a/src/App/Page/Authentication.purs b/src/App/Page/Authentication.purs
index b706104..a9e6e29 100644
--- a/src/App/Page/Authentication.purs
+++ b/src/App/Page/Authentication.purs
@@ -2,12 +2,13 @@
 -- | TODO: token validation.
 module App.Page.Authentication where
 
-import Prelude (Unit, bind, discard, pure, ($), (<<<), (<>), (>), (==), map, show)
+import Prelude (Unit, bind, discard, pure, ($), (<<<), (=<<), (<>), (>), (==), map, show)
 
 import Data.Array as A
 import Data.ArrayBuffer.Types (ArrayBuffer)
-import Data.Maybe (Maybe(..), maybe)
 import Data.Either (Either(..))
+import Data.Eq (class Eq)
+import Data.Maybe (Maybe(..), maybe)
 import Data.Tuple (Tuple(..))
 import Effect.Aff.Class (class MonadAff)
 import Halogen as H
@@ -19,6 +20,10 @@ import Web.Event.Event (Event)
 
 import Bulma as Bulma
 
+import Web.HTML (window) as HTML
+import Web.HTML.Window (sessionStorage) as Window
+import Web.Storage.Storage as Storage
+
 import App.Type.LogMessage
 import App.Message.AuthenticationDaemon as AuthD
 
@@ -77,7 +82,8 @@ data NewPasswordInput
   | NEWPASS_INP_confirmation String
 
 data Action
-  = HandleAuthenticationInput AuthenticationInput
+  = Initialize
+  | HandleAuthenticationInput AuthenticationInput
   | HandlePasswordRecovery    PasswordRecoveryInput
   | HandleNewPassword         NewPasswordInput
   --
@@ -85,6 +91,14 @@ data Action
   | PasswordRecoveryAttempt Event
   | NewPasswordAttempt Event
 
+  -- | Change the displayed tab.
+  | ChangeTab Tab
+
+-- | There are different tabs in the administration page.
+-- | For example, users can be searched (`authd`) and a list is provided.
+data Tab = Auth | TabPasswordRecovery | Recovery
+derive instance eqTab :: Eq Tab
+
 type StateAuthenticationForm = { login :: String, pass :: String }
 type StatePasswordRecoveryForm = { login :: String, email :: String }
 type StateNewPasswordForm = { login :: String, token :: String, password :: String, confirmation :: String }
@@ -95,6 +109,7 @@ type State =
   , newPasswordForm        :: StateNewPasswordForm
   , errors                 :: Array Error
   , wsUp                   :: Boolean
+  , current_tab            :: Tab
   }
 
 initialState :: Input -> State
@@ -104,6 +119,7 @@ initialState _ =
   , newPasswordForm: { login: "", token: "", password: "", confirmation: "" }
   , wsUp: true
   , errors: []
+  , current_tab: Auth
   }
 
 component :: forall m. MonadAff m => H.Component Query Input Output m
@@ -112,26 +128,35 @@ component =
     { initialState
     , render
     , eval: H.mkEval $ H.defaultEval
-        { handleAction = handleAction
+        { initialize   = Just Initialize
+        , handleAction = handleAction
         , handleQuery  = handleQuery
         }
     }
 
 render :: forall m. State -> H.ComponentHTML Action () m
-render { wsUp, authenticationForm, passwordRecoveryForm, newPasswordForm, errors } =
+render { wsUp, current_tab, authenticationForm, passwordRecoveryForm, newPasswordForm, errors } =
   Bulma.section_small
-    [ case wsUp of
+    [ fancy_tab_bar
+    , if A.length errors > 0
+      then HH.div_ [ Bulma.box [ HH.text (A.fold $ map show_error errors) ] ]
+      else HH.div_ []
+    , case wsUp of
         false -> Bulma.p "You are disconnected."
-        true  ->
-          if A.length errors > 0
-          then HH.div_ [ Bulma.box [ HH.text (A.fold $ map show_error errors) ]
-                       , Bulma.columns_ [ b auth_form, b passrecovery_form, b newpass_form ]
-                       ]
-          else Bulma.columns_ [ b auth_form, b passrecovery_form, b newpass_form ]
+        true  -> case current_tab of
+          Auth                -> Bulma.box auth_form
+          TabPasswordRecovery -> Bulma.box passrecovery_form
+          Recovery            -> Bulma.box newpass_form
     ]
 
   where
-    b e = Bulma.column_ [ Bulma.box e ]
+    fancy_tab_bar =
+      Bulma.fancy_tabs
+        [ Bulma.tab_entry (is_tab_active Auth) "Authentication" (ChangeTab Auth)
+        , Bulma.tab_entry (is_tab_active TabPasswordRecovery) "Ask for Password Recovery" (ChangeTab TabPasswordRecovery)
+        , Bulma.tab_entry (is_tab_active Recovery) "Recover with a token" (ChangeTab Recovery)
+        ]
+    is_tab_active tab = current_tab == tab
 
     show_error :: Error -> String
     show_error = case _ of
@@ -248,6 +273,17 @@ render { wsUp, authenticationForm, passwordRecoveryForm, newPasswordForm, errors
 
 handleAction :: forall m. MonadAff m => Action -> H.HalogenM State Action () Output m Unit
 handleAction = case _ of
+  Initialize -> do
+    sessionstorage <- H.liftEffect $ Window.sessionStorage =<< HTML.window
+    old_tab <- H.liftEffect $ Storage.getItem "current-auth-tab" sessionstorage
+    case old_tab of
+      Nothing -> H.raise $ Log $ ErrorLog "We hadn't changed tab before reload apparently."
+      Just current_tab -> case current_tab of
+        "Auth"                -> handleAction $ ChangeTab Auth
+        "TabPasswordRecovery" -> handleAction $ ChangeTab TabPasswordRecovery
+        "Recovery"            -> handleAction $ ChangeTab Recovery
+        _  -> H.raise $ Log $ ErrorLog $ "Reload but cannot understand old current_tab: " <> current_tab
+
   HandleAuthenticationInput authinp -> do
     case authinp of
       AUTH_INP_login v -> H.modify_ _ { authenticationForm { login = v } }
@@ -323,6 +359,15 @@ handleAction = case _ of
                                   H.raise $ PasswordRecovery login token password
          else H.raise $ Log $ UnableToSend "Confirmation differs from password!"
 
+  ChangeTab current_tab -> do
+    -- Store the current tab we are on and restore it when we reload.
+    sessionstorage <- H.liftEffect $ Window.sessionStorage =<< HTML.window
+    _ <- case current_tab of
+      Auth                -> H.liftEffect $ Storage.setItem "current-auth-tab" "Auth"                sessionstorage
+      TabPasswordRecovery -> H.liftEffect $ Storage.setItem "current-auth-tab" "TabPasswordRecovery" sessionstorage
+      Recovery            -> H.liftEffect $ Storage.setItem "current-auth-tab" "Recovery"            sessionstorage
+    H.modify_ _ { current_tab = current_tab }
+
 handleQuery :: forall a m. MonadAff m => Query a -> H.HalogenM State Action () Output m (Maybe a)
 handleQuery = case _ of
   -- For now, no message actually needs to be handled here.