Browse Source

Incorporate Elm app.

pull/167/head
Sean Zicari 6 years ago
parent
commit
68e051edb3
14 changed files with 1689 additions and 53 deletions
  1. +2
    -0
      .gitignore
  2. +28
    -0
      elm.json
  3. +6
    -0
      package.json
  4. +156
    -0
      src/assets/scripts/elm/Main.elm
  5. +264
    -0
      src/assets/scripts/elm/Pages/Counselors.elm
  6. +5
    -0
      src/assets/scripts/elm/Pages/Login.elm
  7. +9
    -0
      src/assets/scripts/elm/Session.elm
  8. +9
    -0
      src/assets/scripts/elm/firebaseConfig.js
  9. +62
    -0
      src/assets/scripts/elm/index.js
  10. +38
    -38
      src/assets/scripts/fullcalendar/index.js
  11. +1
    -15
      src/assets/scripts/index.js
  12. +1100
    -0
      src/dashboard.html
  13. +8
    -0
      webpack/rules/elm.js
  14. +1
    -0
      webpack/rules/index.js

+ 2
- 0
.gitignore View File

@ -30,3 +30,5 @@ package-lock.json
# ----------------------------
build/
elm-stuff/

+ 28
- 0
elm.json View File

@ -0,0 +1,28 @@
{
"type": "application",
"source-directories": [
"src/assets/scripts/elm"
],
"elm-version": "0.19.0",
"dependencies": {
"direct": {
"arowM/elm-form-decoder": "1.2.0",
"elm/browser": "1.0.1",
"elm/core": "1.0.2",
"elm/html": "1.0.0",
"elm/http": "2.0.0",
"elm/json": "1.1.3",
"elm/url": "1.0.0"
},
"indirect": {
"elm/bytes": "1.0.8",
"elm/file": "1.0.5",
"elm/time": "1.0.0",
"elm/virtual-dom": "1.0.2"
}
},
"test-dependencies": {
"direct": {},
"indirect": {}
}
}

+ 6
- 0
package.json View File

@ -27,6 +27,10 @@
"copy-webpack-plugin": "^4.2.0",
"cross-env": "^5.1.0",
"css-loader": "^0.28.7",
"elm": "0.19.0-bugfix6",
"elm-hot-webpack-loader": "^1.0.2",
"elm-test": "^0.19.0-rev5",
"elm-webpack-loader": "github:halfzebra/elm-webpack-loader#updated-elm-compiler",
"eslint": "^4.9.0",
"eslint-config-airbnb-base": "^12.1.0",
"eslint-plugin-import": "2.7.0",
@ -54,6 +58,8 @@
"datatables": "^1.10.13",
"easy-pie-chart": "^2.1.7",
"file-loader": "^1.1.5",
"firebase": "^7.2.3",
"firebaseui": "^4.2.0",
"fullcalendar": "^3.6.2",
"jquery": "^3.2.1",
"jquery-sparkline": "^2.4.0",


+ 156
- 0
src/assets/scripts/elm/Main.elm View File

@ -0,0 +1,156 @@
port module Main exposing (Model, Msg(..), init, main, update, view)
import Browser exposing (Document, UrlRequest(..))
import Browser.Navigation as Nav exposing (Key)
import Html exposing (Html, a, br, div, h1, img, text)
import Html.Attributes exposing (class, href, id, src)
import Html.Events exposing (onClick)
import Pages.Counselors as Counselors
import Session exposing (User)
import Url exposing (Url)
import Url.Parser as Parser exposing (Parser, map, oneOf, s, top)
---- MODEL ----
type alias Model =
{ navKey : Key, page : Page, user : Maybe User }
type Page
= Dashboard
| Counselors Counselors.Model
init : Maybe User -> Url -> Key -> ( Model, Cmd Msg )
init user url key =
( { navKey = key
, page = whichPage url
, user = user
}
, Cmd.none
)
routes : Parser (Page -> Page) Page
routes =
oneOf
[ map Dashboard top
, map (Counselors Counselors.init) (s "counselors")
]
whichPage : Url -> Page
whichPage url =
Maybe.withDefault Dashboard (Parser.parse routes url)
---- UPDATE ----
type Msg
= ClickedLink UrlRequest
| ChangedUrl Url
| CounselorsMsg Counselors.Msg
| Logout
| NoOp
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case ( msg, model.page ) of
( ClickedLink req, _ ) ->
case req of
Internal url ->
( model
, Nav.pushUrl model.navKey (Url.toString url)
)
External href ->
( model, Nav.load href )
( ChangedUrl url, _ ) ->
( { model | page = whichPage url }, Cmd.none )
( CounselorsMsg cMsg, Counselors cModel ) ->
let
( cModel_, cCmd ) =
Counselors.update cMsg cModel
in
( { model
| page = Counselors cModel_
}
, Cmd.map CounselorsMsg cCmd
)
( Logout, _ ) ->
( { model | user = Nothing }, logout () )
_ ->
( model, Cmd.none )
---- VIEW ----
view : Model -> Document Msg
view model =
let
content =
case model.page of
Counselors cModel ->
Html.map CounselorsMsg Counselors.view
Dashboard ->
mainHtml
in
{ title = "InteroCare Admin"
, body =
[ div []
[ img [ src "/ALFBLogo.png" ] []
, div [ id "firebaseui-auth-container", class "hidden" ] []
, content
]
]
}
mainHtml : Html Msg
mainHtml =
div
[]
[ div [ id "firebaseui-auth-container" ] []
, h1
[]
[ text "Dashboard" ]
, a [ href "/counselors" ] [ text "Counselors" ]
, br [] []
, a [ href "/", onClick Logout ] [ text "Logout" ]
]
---- PROGRAM ----
main : Program (Maybe User) Model Msg
main =
Browser.application
{ view = view
, init = init
, update = update
, subscriptions = always Sub.none
, onUrlRequest = ClickedLink
, onUrlChange = ChangedUrl
}
-- Ports
port logout : () -> Cmd msg

+ 264
- 0
src/assets/scripts/elm/Pages/Counselors.elm View File

@ -0,0 +1,264 @@
module Pages.Counselors exposing (Model, Msg, init, update, view)
import Debug exposing (log)
import Form.Decoder as Decoder exposing (Decoder)
import Html
exposing
( Html
, a
, br
, button
, div
, fieldset
, form
, h1
, input
, label
, legend
, p
, span
, text
)
import Html.Attributes
exposing
( action
, class
, for
, href
, method
, name
, placeholder
, type_
, value
)
import Html.Events exposing (onClick, onInput, onSubmit)
import Http
import Json.Encode as E
-- MODEL
type alias Model =
{ form : NewCounselorForm, counselor : Maybe NewCounselor }
type alias NewCounselorForm =
{ firstName : String
, lastName : String
, email : String
}
type Msg
= NewCounselorSubmitted (Result Http.Error ())
| NewCounselorFormSubmitted
| EmailCharEntered String
| FirstNameCharEntered String
| LastNameCharEntered String
type alias NewCounselor =
{ firstName : String
, lastName : String
, email : String
}
-- UPDATE & INIT
update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
case msg of
NewCounselorSubmitted _ ->
( model, Cmd.none )
NewCounselorFormSubmitted ->
let
( newCounselor, _ ) =
case Decoder.run form model.form of
Ok newCounselor_ ->
( Just newCounselor_, Nothing )
Err _ ->
( Nothing, Nothing )
in
( { model | counselor = newCounselor }, Cmd.none )
FirstNameCharEntered char ->
let
cForm : NewCounselorForm
cForm =
model.form
form_ : NewCounselorForm
form_ =
{ cForm | firstName = String.trim char }
in
( { model | form = form_ }, Cmd.none )
LastNameCharEntered char ->
let
cForm : NewCounselorForm
cForm =
model.form
form_ : NewCounselorForm
form_ =
{ cForm | lastName = String.trim char }
in
( { model | form = form_ }, Cmd.none )
EmailCharEntered char ->
let
cForm : NewCounselorForm
cForm =
model.form
form_ : NewCounselorForm
form_ =
{ cForm | email = String.trim char }
in
( { model | form = form_ }, Cmd.none )
init : Model
init =
{ form = NewCounselorForm "" "" "", counselor = Nothing }
-- VIEW
view : Html Msg
view =
div
[]
[ h1 [] [ text "Counselors" ]
, a [ href "/" ] [ text "Dashboard" ]
, br [] []
, Html.form
[ class "pure-form pure-form-aligned"
, onSubmit NewCounselorFormSubmitted
]
[ legend
[]
[ text "Invite a Counselor"
, fieldset []
[ div
[ class "pure-control-group" ]
[ label [ for "first_name" ] [ text "First Name" ]
, input
[ type_ "text", placeholder "First Name", name "first_name", onInput FirstNameCharEntered ]
[]
]
, div
[ class "pure-control-group" ]
[ label [ for "last_name" ] [ text "Last Name" ]
, input
[ type_ "text", name "last_name", placeholder "Last Name", onInput LastNameCharEntered ]
[]
]
, div
[ class "pure-control-group" ]
[ label [ for "email" ] [ text "Email" ]
, input
[ type_ "text", name "email", placeholder "Email", onInput EmailCharEntered ]
[]
, div
[ class "pure-controls" ]
[ input
[ type_ "submit"
, class "pure-button pure-button-primary"
, value "Submit"
]
[]
]
]
]
]
]
]
-- Form Handling
type FormError
= FirstNameRequired
| LastNameRequired
| EmailRequired
| InvalidEmail
firstName : Decoder String FormError String
firstName =
Decoder.identity
|> Decoder.assert (Decoder.minLength FirstNameRequired 1)
lastName : Decoder String FormError String
lastName =
Decoder.identity
|> Decoder.assert (Decoder.minLength LastNameRequired 1)
email : Decoder String FormError String
email =
Decoder.identity
|> Decoder.assert (Decoder.minLength EmailRequired 1)
firstName_ : Decoder NewCounselorForm FormError String
firstName_ =
Decoder.lift .firstName firstName
lastName_ : Decoder NewCounselorForm FormError String
lastName_ =
Decoder.lift .lastName lastName
email_ : Decoder NewCounselorForm FormError String
email_ =
Decoder.lift .email email
form : Decoder NewCounselorForm FormError NewCounselor
form =
Decoder.top NewCounselor
|> Decoder.field firstName_
|> Decoder.field lastName_
|> Decoder.field email_
toNewCounselor : NewCounselorForm -> Result (List FormError) NewCounselor
toNewCounselor theForm =
Ok <| NewCounselor "" "" ""
-- API
encodeNewCounselor : NewCounselor -> E.Value
encodeNewCounselor counselor =
E.object
[ ( "firstName", E.string counselor.firstName )
, ( "lastName", E.string counselor.lastName )
, ( "email", E.string counselor.email )
]
submitNewCounselor : NewCounselor -> Cmd Msg
submitNewCounselor counselor =
Http.post
{ url = "/counselors"
, body = Http.jsonBody (encodeNewCounselor counselor)
, expect = Http.expectWhatever NewCounselorSubmitted
}

+ 5
- 0
src/assets/scripts/elm/Pages/Login.elm View File

@ -0,0 +1,5 @@
module Pages.Login exposing (Model)
type alias Model =
{}

+ 9
- 0
src/assets/scripts/elm/Session.elm View File

@ -0,0 +1,9 @@
module Session exposing (User)
type alias User =
{ email : String
, photoUrl : String
, orgId : Maybe String
, accessToken : String
}

+ 9
- 0
src/assets/scripts/elm/firebaseConfig.js View File

@ -0,0 +1,9 @@
module.exports = {
apiKey: "AIzaSyDaihAI-PZdy0lNSsIhzciKWWy2tYOIUVE",
authDomain: "dev-interocare.firebaseapp.com",
databaseURL: "https://dev-interocare.firebaseio.com",
projectId: "dev-interocare",
storageBucket: "dev-interocare.appspot.com",
messagingSenderId: "820163062176",
appId: "1:820163062176:web:33ea3278ea9166900c1cc5"
};

+ 62
- 0
src/assets/scripts/elm/index.js View File

@ -0,0 +1,62 @@
import { Elm } from './Main.elm';
export default (function () {
var firebaseConfig = require('./firebaseConfig');
const firebase = require('firebase/app');
// Initialize Firebase
firebase.initializeApp(firebaseConfig);
const firebaseui = require('firebaseui');
const ui = new firebaseui.auth.AuthUI(firebase.auth());
firebase.auth().onAuthStateChanged(function (user) {
if (user) {
Promise.all([user.getIdToken(), user.getIdTokenResult()]).then(([accessToken, idToken]) => {
const app = Elm.Main.init({
flags: {
email: user.email,
photoUrl: user.photoURL,
orgId: idToken.claims.orgId || null,
accessToken: accessToken
}
});
app.ports.logout.subscribe(() => {
firebase.auth().signOut().then(() => {
window.location.reload();
});
});
});
} else {
initLoginForm();
}
});
const initLoginForm = () => {
const uiConfig = {
callbacks: {
signInSuccessWithAuthResult: function (authResult, redirectUrl) {
// User successfully signed in.
// Return type determines whether we continue the redirect automatically
// or whether we leave that to developer to handle.
return true;
}
},
// Will use popup for IDP Providers sign-in flow instead of the default, redirect.
// signInFlow: 'popup',
signInSuccessUrl: window.location.href,
signInOptions: [
// Leave the lines as is for the providers you want to offer your users.
firebase.auth.GoogleAuthProvider.PROVIDER_ID
],
// Terms of service url.
tosUrl: '<your-tos-url>',
// Privacy policy url.
privacyPolicyUrl: '<your-privacy-policy-url>'
};
ui.start('#firebaseui-auth-container', uiConfig);
}
}());

+ 38
- 38
src/assets/scripts/fullcalendar/index.js View File

@ -4,56 +4,56 @@ import 'fullcalendar/dist/fullcalendar.min.css';
export default (function () {
const date = new Date();
const d = date.getDate();
const m = date.getMonth();
const y = date.getFullYear();
const d = date.getDate();
const m = date.getMonth();
const y = date.getFullYear();
const events = [{
title : 'All Day Event',
start : new Date(y, m, 1),
desc : 'Meetings',
bullet : 'success',
title: 'All Day Event',
start: new Date(y, m, 1),
desc: 'Meetings',
bullet: 'success',
}, {
title : 'Long Event',
start : new Date(y, m, d - 5),
end : new Date(y, m, d - 2),
desc : 'Hangouts',
bullet : 'success',
title: 'Long Event',
start: new Date(y, m, d - 5),
end: new Date(y, m, d - 2),
desc: 'Hangouts',
bullet: 'success',
}, {
title : 'Repeating Event',
start : new Date(y, m, d - 3, 16, 0),
allDay : false,
desc : 'Product Checkup',
bullet : 'warning',
title: 'Repeating Event',
start: new Date(y, m, d - 3, 16, 0),
allDay: false,
desc: 'Product Checkup',
bullet: 'warning',
}, {
title : 'Repeating Event',
start : new Date(y, m, d + 4, 16, 0),
allDay : false,
desc : 'Conference',
bullet : 'danger',
title: 'Repeating Event',
start: new Date(y, m, d + 4, 16, 0),
allDay: false,
desc: 'Conference',
bullet: 'danger',
}, {
title : 'Birthday Party',
start : new Date(y, m, d + 1, 19, 0),
end : new Date(y, m, d + 1, 22, 30),
allDay : false,
desc : 'Gathering',
title: 'Birthday Party',
start: new Date(y, m, d + 1, 19, 0),
end: new Date(y, m, d + 1, 22, 30),
allDay: false,
desc: 'Gathering',
}, {
title : 'Click for Google',
start : new Date(y, m, 28),
end : new Date(y, m, 29),
url : 'http ://google.com/',
desc : 'Google',
bullet : 'success',
title: 'Click for Google',
start: new Date(y, m, 28),
end: new Date(y, m, 29),
url: 'http ://google.com/',
desc: 'Google',
bullet: 'success',
}];
$('#full-calendar').fullCalendar({
events,
height : 800,
editable : true,
height: 800,
editable: true,
header: {
left : 'month,agendaWeek,agendaDay',
center : 'title',
right : 'today prev,next',
left: 'month,agendaWeek,agendaDay',
center: 'title',
right: 'today prev,next',
},
});
}())

+ 1
- 15
src/assets/scripts/index.js View File

@ -1,17 +1,3 @@
import '../styles/index.scss';
import './masonry';
import './charts';
import './popover';
import './scrollbar';
import './search';
import './sidebar';
import './skycons';
import './vectorMaps';
import './chat';
import './datatable';
import './datepicker';
import './email';
import './fullcalendar';
import './googleMaps';
import './utils';
import './elm';

+ 1100
- 0
src/dashboard.html
File diff suppressed because it is too large
View File


+ 8
- 0
webpack/rules/elm.js View File

@ -0,0 +1,8 @@
module.exports = {
test: /\.elm$/,
exclude: [/elm-stuff/, /node_modules/],
use: {
loader: 'elm-webpack-loader',
options: {}
}
};

+ 1
- 0
webpack/rules/index.js View File

@ -2,6 +2,7 @@ module.exports = [
require('./js'),
require('./images'),
require('./css'),
require('./elm'),
require('./sass'),
require('./fonts'),
];

Loading…
Cancel
Save