module Main exposing (..) import Browser import Browser.Navigation as Nav import Html exposing (..) import Html.Attributes exposing (..) import Html.Events exposing (onInput) import Http import Json.Decode as D import Url -- MAIN main : Program () Model Msg main = Browser.application { init = init , view = view , update = update , subscriptions = subscriptions , onUrlChange = UrlChanged , onUrlRequest = LinkClicked } -- MODEL type alias Model = { key : Nav.Key , url : Url.Url , token: Maybe String , token_data: Maybe TokenData , switch_data : Maybe ( List Switch ) } init : () -> Url.Url -> Nav.Key -> ( Model, Cmd Msg ) init flags url key = ( Model key url Nothing Nothing Nothing, Cmd.none ) -- UPDATE type Msg = LinkClicked Browser.UrlRequest | UrlChanged Url.Url | TokenInput String | TokenValidate ( Result Http.Error TokenData ) | Logout | NoSwitchData | GotSwitchData ( List Switch ) type alias Switch = { id : String , who : String , started_at : String , ended_at : Maybe String , duration : Int } type alias TokenData = { sub : String , jti : String } tokenDecoder : D.Decoder TokenData tokenDecoder = D.map2 TokenData (D.field "sub" D.string) (D.field "jti" D.string) request method token path body expect = Http.request { method = method , body = body , headers = [ Http.header "Authorization" token ] , url = path , expect = expect , timeout = Nothing , tracker = Nothing } update : Msg -> Model -> ( Model, Cmd Msg ) update msg model = case msg of LinkClicked urlRequest -> case urlRequest of Browser.Internal url -> ( model, Nav.pushUrl model.key (Url.toString url) ) Browser.External href -> ( model, Nav.load href ) UrlChanged url -> case url.path of "/logout" -> ( model, Nav.load "/" ) default -> ( { model | url = url } , Cmd.none ) TokenInput token -> ( { model | token = Just token } , request "GET" token "/.within/tokeninfo" Http.emptyBody (expectJson TokenValidate tokenDecoder) ) TokenValidate result -> case result of Ok data -> ( { model | token_data = Just data } , Cmd.none ) Err _ -> ( { model | token = Nothing } , Cmd.none ) Logout -> ( { model | token = Nothing, token_data = Nothing } , Nav.load "/" ) _ -> ( model, Cmd.none ) -- SUBSCRIPTIONS subscriptions : Model -> Sub Msg subscriptions _ = Sub.none -- VIEW view : Model -> Browser.Document Msg view model = case model.token of Nothing -> template "Login" [ h1 [] [ text "Login" ] , viewInput "password" "API Token" "" TokenInput ] Just token -> case model.url.path of "/" -> template "Mi" [ navBar , h1 [] [ text "Mi" ] , p [] [ text "TODO: everything" ] , p [] [ text "Token sub: " , text (Maybe.withDefault (TokenData "" "") model.token_data).sub , text " ID: " , text (Maybe.withDefault (TokenData "" "") model.token_data).jti ] ] other -> template "Not found" [ navBar , h1 [] [ text "Not found" ] , p [] [ text "The requested URL " , b [] [ text other ] , text " was not found." ] ] viewInput : String -> String -> String -> (String -> msg) -> Html msg viewInput t p v toMsg = input [ type_ t, placeholder p, value v, onInput toMsg ] [] viewLink : String -> String -> Html msg viewLink path title = a [ href path ] [ text title ] template : String -> List (Html msg) -> Browser.Document msg template title body = { title = title , body = [ node "main" [] body ] } navBar : Html msg navBar = node "nav" [] [ p [] [ viewLink "/" "Mi" , text " - " , viewLink "/switch" "Switch tracker" , text " - " , viewLink "/logout" "Logout" ] ] expectJson : (Result Http.Error a -> msg) -> D.Decoder a -> Http.Expect msg expectJson toMsg decoder = Http.expectStringResponse toMsg <| \response -> case response of Http.BadUrl_ url -> Err (Http.BadUrl url) Http.Timeout_ -> Err Http.Timeout Http.NetworkError_ -> Err Http.NetworkError Http.BadStatus_ metadata body -> Err (Http.BadStatus metadata.statusCode) Http.GoodStatus_ metadata body -> case D.decodeString decoder body of Ok value -> Ok value Err err -> Err (Http.BadBody (D.errorToString err))