module Main exposing (main) import Browser import Html exposing (..) import Html.Attributes as Attr import Html.Events exposing (onInput) import String -- MODEL type alias Model = { participants : String , salary : String , minutes : String , errors : List String } init : Model init = { participants = "" , salary = "" , minutes = "" , errors = [] } -- UPDATE type Msg = UpdateParticipants String | UpdateSalary String | UpdateMinutes String update : Msg -> Model -> Model update msg model = case msg of UpdateParticipants value -> { model | participants = value, errors = validateInputs { model | participants = value } } UpdateSalary value -> { model | salary = value, errors = validateInputs { model | salary = value } } UpdateMinutes value -> { model | minutes = value, errors = validateInputs { model | minutes = value } } -- VALIDATION validateInputs : Model -> List String validateInputs model = let validateParticipants value = case String.toFloat (String.replace "," "" value) of Just num -> if num <= 0 then [ "Number of participants must be greater than 0" ] else if num > 1000 then [ "Number of participants seems unusually high" ] else if num /= toFloat (round num) then [ "Number of participants must be a whole number" ] else [] Nothing -> if String.isEmpty value then [] else [ "Number of participants must be a valid number" ] validateSalary value = case String.toFloat (String.replace "," "" value) of Just num -> if num <= 0 then [ "Salary must be greater than 0" ] else if num < 10 then [ "Salary seems unusually low" ] else if num > 1000000 then [ "Salary seems unusually high" ] else [] Nothing -> if String.isEmpty value then [] else [ "Salary must be a valid number" ] validateMinutes value = case String.toFloat (String.replace "," "" value) of Just num -> if num <= 0 then [ "Meeting length must be greater than 0" ] else if num > 480 then [ "Meeting length seems unusually long (over 8 hours)" ] else if num /= toFloat (round num) then [ "Meeting length must be a whole number" ] else [] Nothing -> if String.isEmpty value then [] else [ "Meeting length must be a valid number" ] in validateParticipants model.participants ++ validateSalary model.salary ++ validateMinutes model.minutes -- VIEW view : Model -> Html Msg view model = div [ Attr.class "container" ] [ h1 [] [ text "Meeting Cost Calculator" ] , div [ Attr.class "input-group" ] [ label [] [ text "Number of Participants" ] , input [ Attr.type_ "number" , Attr.value model.participants , onInput UpdateParticipants , Attr.placeholder "Enter number of participants" , Attr.min "1" , Attr.max "1000" ] [] ] , div [ Attr.class "input-group" ] [ label [] [ text "Average Annual Salary ($)" ] , input [ Attr.type_ "text" , Attr.value model.salary , onInput UpdateSalary , Attr.placeholder "Enter average annual salary" ] [] ] , div [ Attr.class "input-group" ] [ label [] [ text "Meeting Length (minutes)" ] , input [ Attr.type_ "number" , Attr.value model.minutes , onInput UpdateMinutes , Attr.placeholder "Enter meeting length in minutes" , Attr.min "1" , Attr.max "480" ] [] ] , viewErrors model.errors , div [ Attr.class "results" ] [ viewResults model ] ] viewErrors : List String -> Html msg viewErrors errors = if List.isEmpty errors then div [] [] else div [ Attr.class "errors" ] (List.map (\error -> p [] [ text error ]) errors) viewResults : Model -> Html msg viewResults model = let participants = String.toFloat (String.replace "," "" model.participants) |> Maybe.withDefault 0 salary = String.toFloat (String.replace "," "" model.salary) |> Maybe.withDefault 0 minutes = String.toFloat (String.replace "," "" model.minutes) |> Maybe.withDefault 0 -- Calculate working hours per year (50 weeks * 5 days * 8 hours) workingHoursPerYear = 50 * 5 * 8 -- Calculate hourly rate hourlyRate = salary / workingHoursPerYear -- Calculate cost per minute costPerMinute = hourlyRate / 60 -- Calculate total meeting cost totalCost = costPerMinute * minutes * participants in if participants > 0 && salary > 0 && minutes > 0 && List.isEmpty model.errors then div [] [ h2 [] [ text "Approximate Meeting Cost" ] , p [] [ span [] [ text "Total Meeting Cost: " ] , strong [] [ text ("$" ++ String.fromFloat (toFloat (round (totalCost * 100)) / 100)) ] ] , p [] [ span [] [ text "Cost per Minute: " ] , strong [] [ text ("$" ++ String.fromFloat (toFloat (round (costPerMinute * 100)) / 100)) ] ] ] else div [] [] -- MAIN main : Program () Model Msg main = Browser.sandbox { init = init , update = update , view = view }