diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..36acd22 --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +# elm-package generated files +elm-stuff +# elm-repl generated files +repl-temp-* + +elm.js diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d22e54f --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 John Whiles + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/elm-package.json b/elm-package.json new file mode 100644 index 0000000..8a65872 --- /dev/null +++ b/elm-package.json @@ -0,0 +1,19 @@ +{ + "version": "1.0.0", + "summary": "an elm boilerplate", + "repository": "https://github.com/Jwhiles/elm-boiler-plate.git", + "license": "BSD3", + "source-directories": [ + "src" + ], + "exposed-modules": [], + "dependencies": { + "NoRedInk/elm-decode-pipeline": "3.0.0 <= v < 4.0.0", + "andrewMacmurray/elm-delay": "2.0.2 <= v < 3.0.0", + "elm-lang/core": "5.1.1 <= v < 6.0.0", + "elm-lang/dom": "1.1.1 <= v < 2.0.0", + "elm-lang/html": "2.0.0 <= v < 3.0.0", + "elm-lang/http": "1.0.0 <= v < 2.0.0" + }, + "elm-version": "0.18.0 <= v < 0.19.0" +} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..fd25c47 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,5 @@ +{ + "name": "elm-boiler-plate", + "version": "1.0.0", + "lockfileVersion": 1 +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..2dd9d2f --- /dev/null +++ b/package.json @@ -0,0 +1,21 @@ +{ + "name": "elm-boiler-plate", + "version": "1.0.0", + "description": "a boiler plate for elm projects", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1", + "dev": "elm-live src/Main.elm --output=public/elm.js --dir=public --open --debug --pushstate", + "build": "elm-make src/Main.elm --output=public/elm.js" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/Jwhiles/elm-boiler-plate.git" + }, + "author": "John Whiles", + "license": "ISC", + "bugs": { + "url": "https://github.com/Jwhiles/elm-boiler-plate/issues" + }, + "homepage": "https://github.com/Jwhiles/elm-boiler-plate#readme" +} diff --git a/public/assets/cross.svg b/public/assets/cross.svg new file mode 100644 index 0000000..91d3bcd --- /dev/null +++ b/public/assets/cross.svg @@ -0,0 +1,16 @@ + + + + Created with Sketch. + + + + + + + + + + + + diff --git a/public/assets/favicon.ico b/public/assets/favicon.ico new file mode 100644 index 0000000..fde9a1a Binary files /dev/null and b/public/assets/favicon.ico differ diff --git a/public/assets/infoHub.png b/public/assets/infoHub.png new file mode 100644 index 0000000..2b74413 Binary files /dev/null and b/public/assets/infoHub.png differ diff --git a/public/assets/lucy.png b/public/assets/lucy.png new file mode 100644 index 0000000..0cc4781 Binary files /dev/null and b/public/assets/lucy.png differ diff --git a/public/assets/paul.svg b/public/assets/paul.svg new file mode 100644 index 0000000..e69de29 diff --git a/public/assets/speechBubble.svg b/public/assets/speechBubble.svg new file mode 100644 index 0000000..21586a2 --- /dev/null +++ b/public/assets/speechBubble.svg @@ -0,0 +1,15 @@ + + + + Created with Sketch. + + + + + + + + + + + diff --git a/public/assets/unlock-screenshot.png b/public/assets/unlock-screenshot.png new file mode 100644 index 0000000..45304b0 Binary files /dev/null and b/public/assets/unlock-screenshot.png differ diff --git a/public/assets/woman.jpg b/public/assets/woman.jpg new file mode 100644 index 0000000..1fe125d Binary files /dev/null and b/public/assets/woman.jpg differ diff --git a/public/index.html b/public/index.html new file mode 100644 index 0000000..1df529f --- /dev/null +++ b/public/index.html @@ -0,0 +1,89 @@ + + + + Elm Boilerplate + + + + + + + + + + + +
+
+ + +
+
+
+

Unlock Support Centre

+
+ Image of Unlock assistant +

Lucy

+

“ Hi I’m Lucy! I received a caution for possession of a controlled substance (Class A) in 2009. By mid-2010, I had applied for and been granted a visa which was valid for 10 years.”

+
+

How can I help?

+
+
+
+ + + + + + + +
+ +
+ +
+ +
+
+ + + +
+ + + + + +
+ + + + + + diff --git a/public/style.css b/public/style.css new file mode 100644 index 0000000..58049d7 --- /dev/null +++ b/public/style.css @@ -0,0 +1,112 @@ +* { + font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif; +} + +::-webkit-scrollbar { + display: none; +} + +.grey-font { + color: #4a4a4a; +} + +.light-grey-background { + background-color: #f3efef; +} + +.purple-background { + background-color: #8f5b9c; +} + +.blue-background { + background-color: #2396d2; +} + +.br-50 { + border-radius: 50px; +} + +.margin-0-auto { + margin: 0 auto; +} + +.w480 { + width: 480px; +} + +.unlock-image { + background-image: url('./assets/infoHub.png'); +} + +.h-60 { + height: 60%; +} + +.h-10 { + height: 9rem; +} + +.w416 { + min-width: 416px; +} + +.wrap { + word-wrap: break-word; +} + +@-webkit-keyframes thinking { + 50% { + color: white; + } +} + +@-webkit-keyframes growBot { + 0% { + transform: scale(0, 0); + /*translate(-16rem);*/ + } + 100% { + transform: scale(1, 1); + /*translate(0rem);*/ + } +} + +@-webkit-keyframes growUser { + 0% { + transform: scale(0, 0); + /*translate(16rem);*/ + } + 100% { + transform: scale(1, 1); + /*translate(0rem);*/ + } +} + +.thinking { + -webkit-animation: thinking 0.8s infinite; +} + +.grow_bot_speech { + -webkit-animation: growBot 0.4s; +} + +.grow_user_speech { + -webkit-animation: growUser 0.4s; +} + +.ball { + -webkit-animation: bounce 0.3s infinite alternate; +} + +@-webkit-keyframes bounce { + from { + transform: translateY(0px); + } + to { + transform: translateY(-30px); + } +} + +.min-height{ + min-height: 443.5px; +} diff --git a/src/Main.elm b/src/Main.elm new file mode 100644 index 0000000..82e9878 --- /dev/null +++ b/src/Main.elm @@ -0,0 +1,24 @@ +module Main exposing (..) + +import Delay +import Html exposing (..) +import Models exposing (..) +import Task +import Time +import Update exposing (..) +import View exposing (..) + + +main : Program Never Model Msg +main = + Html.program + { init = init + , view = view + , update = update + , subscriptions = subscriptions + } + + +init : ( Model, Cmd Msg ) +init = + ( model, Delay.after 500 Time.millisecond (ChangeState model.currentStateNumber) ) diff --git a/src/Models.elm b/src/Models.elm new file mode 100644 index 0000000..f14e56f --- /dev/null +++ b/src/Models.elm @@ -0,0 +1,271 @@ +module Models exposing (..) + + +type alias Model = + { input : String + , conversation : List Line + , placeHolder : String + , options : List Option + , currentStateNumber : String + , previousStateNumber : String + , states : List State + , defState : State + } + + +type alias State = + { id : String + , response : Line + , options : List Option + } + + +type alias Option = + { newState : String + , line : Line + } + + +type Msg + = ChangeInput String + | SendText + | SendOption Option + | ChangeState String + | NoOp + | AddThinking Option + + +type alias Line = + { text : String + , sideClass : String + , links : List String + } + + +model : Model +model = + { input = "" + , conversation = [] + , placeHolder = "Enter your questions here" + , options = [] + , currentStateNumber = "greeting" + , previousStateNumber = "greeting" + , states = states + , defState = state_greeting + } + + +states = + [ state_greeting + , state_travelling_where + , state_not_travelling + , state_aus + , state_can + , state_other_travelling + , state_america + , state_america_live + , state_america_travel + , state_america_travel2 + , state_prop_examples + , state_drug + , state_terror + , state_fraud + , state_esta + , state_visa + , state_more_than_6 + , state_less_than_6 + , state_visa_help + , state_lying + , state_thanks + ] + + +botStyle : String +botStyle = + "grow_bot_speech bg-white fl br4 pr2 mw5 w5 pa3" + + +userStyle : String +userStyle = + "grow_user_speech blue-background white fr br4 pr2 mw5 w5 mb3 pa3 w-100 mt3" + + +state_greeting = + State + "greeting" + (Line + "Hi! Are you looking for advice on travelling abroad?" + botStyle + [] + ) + [ { line = { text = "Yes", sideClass = userStyle, links = [] }, newState = "travelling-where" } + , { line = { text = "No", sideClass = userStyle, links = [] }, newState = "not-travelling" } + ] + + +state_travelling_where = + State + "travelling-where" + (Line "Where are you travelling to?" botStyle []) + [ { line = Line "America" userStyle [], newState = "america" } + , { line = Line "Australia" userStyle [], newState = "australia" } + , { line = Line "Canada" userStyle [], newState = "canada" } + , { line = Line "Other" userStyle [], newState = "other" } + ] + + +state_not_travelling = + State + "not-travelling" + (Line "OK! I'm afraid I can't be much help with that! Try having a look at our website for more information:" botStyle [ "https://hub.unlock.org.uk" ]) + [] + + +state_aus = + State + "australia" + (Line "Great, you can find more information about travelling to Australia here:" botStyle [ "https://hub.unlock.org.uk/knowledgebase/travelling-australia" ]) + [] + + +state_can = + State + "canada" + (Line "Great, you can find more information about travelling to Canada here:" botStyle [ "https://hub.unlock.org.uk/knowledgebase/travelling-canada" ]) + [] + + +state_other_travelling = + State + "other" + (Line "OK! Find out more information about travelling abroad here:" + botStyle + [ "https://hub.unlock.org.uk/information/travelling-abroad" ] + ) + [] + + +state_america = + State + "america" + (Line "Are you travelling to live, work or just visit" botStyle []) + [ { line = Line "Live/Work" userStyle [], newState = "america-live" } + , { line = Line "Just Visit" userStyle [], newState = "america-travel" } + ] + + +state_america_live = + State + "america-live" + (Line "Cool! You can find more information about that here:" + botStyle + [ "https://uk.usembassy.gov/visas/visa-information-services/" ] + ) + [ { line = Line "Thanks" userStyle [], newState = "thanks" } ] + + +state_america_travel = + State + "america-travel" + (Line "OK, great! Have you ever been arrested or convicted for a crime that resulted in serious damage to property or serious harm to another person or government authority?" botStyle []) + [ { line = Line "Yes" userStyle [], newState = "visa" } + , { line = Line "No" userStyle [], newState = "drug-crimes" } + , { line = Line "Can you give me some examples?" userStyle [], newState = "prop-examples" } + ] + + +state_america_travel2 = + State + "america-travel-2" + (Line "No problem. So, have you ever been arrested or convicted for a crime that resulted in serious damage to property or serious harm to another person or government authority?" botStyle []) + [ { line = Line "Yes" userStyle [], newState = "visa" } + , { line = Line "No" userStyle [], newState = "drug-crimes" } + , { line = Line "Sorry, can I see the examples again?" userStyle [], newState = "prop-examples" } + ] + + +state_prop_examples = + State + "prop-examples" + (Line "Some examples are: \n Blackmail Burglary Arson Counterfieting, Tax Evasion Adultery Gross Indecency and Mayhem \n A comprehensive list can be found here:" botStyle [ "https://hub.unlock.org.uk/wp-content/uploads/Annex-A-Crimes-involving-moral-turpitude.pdf" ]) + [ { line = Line "Thanks" userStyle [], newState = "america-travel-2" } + ] + + +state_drug = + State + "drug-crimes" + (Line "Have you ever violated any law relating to possesing, using or distributing illegal drugs?" botStyle []) + [ { line = Line "Yes" userStyle [], newState = "visa" } + , { line = Line "No" userStyle [], newState = "terrorist-crimes" } + ] + + +state_terror = + State + "terrorist-crimes" + (Line "Do you seek to engage in or have you ever engaged in terrorist activities, espionage, sabotage or genocide?" botStyle []) + [ { line = Line "Yes" userStyle [], newState = "visa" } + , { line = Line "No" userStyle [], newState = "fraud-crimes" } + ] + + +state_fraud = + State + "fraud-crimes" + (Line "Have you ever committed fraud or misrepresented yourself to obtain, or assisted others to obtain, a visa or entry into the USA" botStyle []) + [ { line = Line "Yes" userStyle [], newState = "visa" } + , { line = Line "No" userStyle [], newState = "esta" } + ] + + +state_esta = + State + "esta" + (Line "Great news, you can travel on an ESTA! Learn more about how to apply for one here: " botStyle [ "http://hub.unlock.org.uk/knowledgebase/travelling-to-the-usa-the-esta-form-and-moral-turpitude/" ]) + [ { line = Line "Thanks" userStyle [], newState = "thanks" } + ] + + +state_visa = + State + "visa" + (Line "OK, you'll probably need to apply for a visa - this can take some time, when are you planning to travel?" botStyle []) + [ { line = Line "In less than 6 months time" userStyle [], newState = "less-than-6" } + , { line = Line "In more than 6 months time" userStyle [], newState = "more-than-6" } + ] + + +state_more_than_6 = + State + "more-than-6" + (Line "OK, that should be fine, you should apply to the US Embassy for a visa. You can find out more about how to do that here: " botStyle [ "http://hub.unlock.org.uk/knowledgebase/travelling-us-need-visa/" ]) + [] + + +state_less_than_6 = + State + "less-than-6" + (Line "OK, applying for a visa to the US can take any time between 12 weeks and 6 months, and sometimes even longer than that" botStyle [ "http://hub.unlock.org.uk/knowledgebase/travelling-us-need-visa/" ]) + [ { line = Line "So what can I do?" userStyle [], newState = "visa-help" } ] + + +state_visa_help = + State + "visa-help" + (Line "We would strongly recommended not booking a holiday or postponing a current holiday until you are certain you have your visa." botStyle []) + [ { line = Line "If I don’t declare my conviction, is there any way they can find out?" userStyle [], newState = "lying" } ] + + +state_lying = + State + "lying" + (Line "The answer is probably 'no' but there are obviously some risks involved. You can see more information about what happens if you lie on the ESTA form here on this page: " botStyle [ "http://hub.unlock.org.uk/knowledgebase/travelling-to-the-usa-the-esta-form-and-moral-turpitude/#vwp" ]) + [ { line = Line "Thanks" userStyle [], newState = "thanks" } ] + + +state_thanks = + State + "thanks" + (Line "No problem! Thanks for chatting with me. If you still require more information on travelling with a conviction, have a look here: " botStyle [ "https://hub.unlock.org.uk/information/travelling-abroad" ]) + [] diff --git a/src/Update.elm b/src/Update.elm new file mode 100644 index 0000000..a3a63c2 --- /dev/null +++ b/src/Update.elm @@ -0,0 +1,80 @@ +-- UPDATE + + +module Update exposing (..) + +import Delay +import Dom.Scroll exposing (toBottom) +import Models exposing (..) +import Task +import Time + + +update : Msg -> Model -> ( Model, Cmd Msg ) +update msg model = + case msg of + ChangeInput newInput -> + ( { model | input = newInput }, Cmd.none ) + + SendText -> + ( { model + | conversation = model.conversation ++ [ Line model.input userStyle [] ] + , input = "" + } + , Cmd.none + ) + + SendOption option -> + ( { model + | conversation = model.conversation ++ [ option.line ] + , options = [] + } + , Delay.after 450 Time.millisecond (AddThinking option) + ) + + AddThinking option -> + ( { model + | conversation = model.conversation ++ [ Line "..." "bg-white fl br4 pr2 mw5 w5 pa3 thinking b f3" [] ] + , options = [] + } + , Cmd.batch + [ Task.attempt (always NoOp) (toBottom "chat") + , Delay.after 1500 Time.millisecond (ChangeState option.newState) + ] + ) + + ChangeState stateId -> + let + newState = + case stateToChangeTo stateId model of + Just state -> + state + + Nothing -> + model.defState + + newListLength = + List.length model.conversation - 1 + + newConversations = + List.take newListLength model.conversation ++ [ newState.response ] + in + ( { model + | input = "" + , conversation = newConversations + , options = newState.options + , currentStateNumber = newState.id + , previousStateNumber = model.currentStateNumber + } + , Task.attempt (always NoOp) (toBottom "chat") + ) + + NoOp -> + model ! [] + + +stateToChangeTo : String -> Model -> Maybe State +stateToChangeTo newStateId model = + model.states + |> List.filter (\state -> state.id == newStateId) + |> List.head diff --git a/src/View.elm b/src/View.elm new file mode 100644 index 0000000..c30523f --- /dev/null +++ b/src/View.elm @@ -0,0 +1,81 @@ +-- VIEW + + +module View exposing (..) + +import Html exposing (..) +import Html.Attributes exposing (..) +import Html.Events exposing (..) +import Json.Decode as Json +import Models exposing (..) + + +view : Model -> Html Msg +view model = + div [ class "br3 light-grey-background vh-50 shadow-1-ns overflow-auto-ns min-height", id "chat" ] + [ div [ class "fixed w416 tc gray bg-white pa3 br3 br--top-m grey-font b f4" ] [ text "Unlock Chat" ] + , div [ class "mt5 mb2" ] [] + , div [] + [ ul [ class "list pa3 ma0" ] + (model.conversation + |> List.map eachLine + ) + ] + , options model + ] + + +options : Model -> Html Msg +options model = + div [ class "w5" ] + [ div [ class "br2 fl mw5 ml3 w-auto flex justify-around" ] [ ul [ class "pa0" ] (model.options |> List.map eachOption) ] + ] + + +eachOption : Option -> Html Msg +eachOption option = + div [ class "grow grow_bot_speech inline-flex flex-wrap" ] + [ button [ class "purple-background white bn br-50 pa3 ma1 fw1", onClick (SendOption option) ] [ text option.line.text ] ] + + +eachLine : Line -> Html Msg +eachLine line = + div [ class "" ] + [ div [ class "" ] + [ li [ class ("" ++ line.sideClass) ] [ text line.text ] + ] + , if List.length line.links > 0 then + div [ class ("wrap mt2 " ++ line.sideClass) ] + (line.links + |> List.map linksList + ) + else + div [] [] + ] + + +linksList : String -> Html Msg +linksList link = + a [ class "grow_bot_speech", href link, target "_blank" ] [ text link ] + + +textInput : Model -> Html Msg +textInput model = + Html.form + [ class + "" + , onWithOptions "submit" { stopPropagation = False, preventDefault = True } (Json.succeed SendText) + ] + [ fieldset [ class "" ] + [ div [ class "" ] + [ label [ class "", for "user-input" ] [] + , input [ class "w-30", placeholder model.placeHolder, onInput ChangeInput, value model.input ] [] + , input [ class "ma2", type_ "submit", value "Ask away!" ] [] + ] + ] + ] + + +subscriptions : Model -> Sub Msg +subscriptions model = + Sub.none