Skip to content

Instantly share code, notes, and snippets.

@bohdanszymanik
Last active July 31, 2018 08:43
Show Gist options
  • Save bohdanszymanik/b68e872d63a7b6c5ab9b970217014c53 to your computer and use it in GitHub Desktop.
Save bohdanszymanik/b68e872d63a7b6c5ab9b970217014c53 to your computer and use it in GitHub Desktop.
First should think about this: https://mangelmaxime.github.io/Fulma/#fulma/components/modal
But otherwise...
In NarrativeModal.fs
module Client.NarrativeModal
open System
open Fable.Core
open Fable.Core.JsInterop
open Fable.Import
open Fable.Helpers.React
open Fable.Helpers.React.Props
open Shared
let view (n:Narration) =
// div [] [str "here again"]
div [ClassName "modal fade"; Id "narrativeModal"; Role "dialog"] [
div [ClassName "modal-dialog"] [
div [ClassName "modal-content"] [
div [ClassName "modal-header"] [
h5 [] [str n.Title]
button [Type "button"; ClassName "close"; unbox ("data-dismiss", "modal"); unbox("aria-label", "close")] [
span [] [str "x"]
]
]
div [ClassName "modal-body"] [ str n.Text ]
div [ClassName "modal-footer"] [
button [ Type "button"; ClassName "btn btn-primary"; unbox ("data-dismiss", "modal")] [ str "Close"]
]
]
]
]
And in ProgSuggestions.fs
module Client.ProgSuggestions
open System
open Elmish
open Elmish.Toastr
open Elmish.Browser.Navigation
open Fable.Core
open Fable.Core.JsInterop
open Fable.Import
open Fable.Helpers.React
open Fable.Helpers.React.Props
open Fable.PowerPack
open Fable.PowerPack.Fetch
open Fable.PowerPack.Fetch.Fetch_types
open Shared
open Style
open Client.Utils
[<Pojo>]
type ProgSuggestion = ProgSuggestion'
type Model = {
Provider: Provider'
TECQual: TECQual'
ProgSuggestionList: ProgSuggestion[] option
Match : MatchConfidence option
NoMatch: FailedMatch option
SelectedNarrative: Narration option
ErrorMsg: string option
}
type Msg =
// | FetchProgSuggestions
| FetchedProgSuggestions of ProgSuggestion[]
| MatchedProgToConfidenceRate of MatchConfidence
| MatchedProgAssignConfidence of MatchConfidence
| MatchedProgStoreConfidence of MatchConfidence
| DisplayModalNoMatch
| PopulateFailedMatchModel of FailedMatch
| RecordFailedMatchToDb of FailedMatch
| GoHome of string // dummy value
| GoToProviders of string // ditto
| Narrative of Narration
| FetchError of exn
let registerNoMatch (fmm:FailedMatch) =
let opn =
match fmm.OverrideProgrammeNumber with
| Some opn -> opn
| None -> ""
let c =
match fmm.Comment with
| Some c -> c
| None -> ""
let url = sprintf "http://%s:8085/providers/%i/TECQuals/%s/registerNoMatch/overrideProgNum/%s/comment/%s" apiHost fmm.MoENum fmm.TECQual opn c
System.Console.WriteLine url
promise {
let! matchRecord = fetch url []
let! txt = matchRecord.text()
return txt
}
let registerMatch (mc:MatchConfidence) =
let url = sprintf "http://%s:8085/providers/%i/TECQuals/%s/programmeVersion/%s/registerMatchWithConfidence/%f/NZSCEDBroadDM/%f/NZSCEDNarrowDM/%f/NZSCEDDetailDM/%f/NZSCEDLevenstein/%f/TitleWordMatchDM/%f/AimWordMatchDM/%f/ContentWordMatchDM/%f/CreditsDM/%f/LevelDM/%f"
apiHost
mc.MoENum mc.TECQual mc.ProgrammeVersionId mc.MatchConfidence
mc.DistanceCpts.NZSCEDBroadDM mc.DistanceCpts.NZSCEDNarrowDM mc.DistanceCpts.NZSCEDDetailDM mc.DistanceCpts.NZSCEDLevenstein
mc.DistanceCpts.TitleWordMatchDM mc.DistanceCpts.AimWordMatchDM mc.DistanceCpts.ContentWordMatchDM
mc.DistanceCpts.CreditsDM mc.DistanceCpts.LevelDM
promise {
let! matchRecord = fetch url []
let! txt = matchRecord.text()
return txt
}
let fetchProgSuggestions (psf:ProgSuggestFor) =
let url = sprintf "http://%s:8085/providers/%i/TECQuals/%s/rankedProgrammeSuggestions" apiHost psf.Provider.MoENum psf.Qual.tecQualificationCode
promise {
let! progSuggestionList = fetchAs<ProgSuggestion[]> url []
return progSuggestionList
}
let init (pfs:ProgSuggestFor) =
{
Provider = pfs.Provider
TECQual = pfs.Qual
ProgSuggestionList = None
Match = None
NoMatch = None
SelectedNarrative = None
ErrorMsg = None
}
let update f (msg:Msg) model : Model*Cmd<'a> =
// System.Console.WriteLine (sprintf "ProgSuggestions.update with msg %A" msg)
match msg with
| GoHome _ -> model, Cmd.none //process back in App.update
| GoToProviders _ -> model, Cmd.none // process in App.update
| FetchedProgSuggestions progSuggestionList ->
let model = {model with ProgSuggestionList = Some progSuggestionList}
model, Toastr.removeAll
| MatchedProgToConfidenceRate mc ->
{model with Match = Some mc}, Cmd.none
| MatchedProgAssignConfidence mc ->
{model with Match = Some mc}, Cmd.none
| MatchedProgStoreConfidence mc ->
model, (Cmd.ofPromise registerMatch mc GoHome FetchError) |> Cmd.map f
| PopulateFailedMatchModel fmm ->
{model with NoMatch = Some fmm}, Cmd.none
| DisplayModalNoMatch ->
model, Cmd.none
| RecordFailedMatchToDb fmm ->
System.Console.WriteLine "in RecordFailedMatchToDb"
model, (Cmd.ofPromise registerNoMatch fmm GoHome FetchError) |> Cmd.map f
| Narrative s ->
{model with SelectedNarrative = Some s}, Cmd.none
| FetchError error ->
System.Console.WriteLine (sprintf "ProgSuggestions.update match FetchError %s" error.StackTrace)
{model with ErrorMsg = Some error.Message}, Cmd.none
// 12 step cold-hot colour scale from http://geog.uoregon.edu/datagraphics/color_scales.htm
let rowColourClass (x:double) (min:double) (max:double) =
let idx =
match x with
| x when x > max -> 11
| x when x < min -> 0
| _ -> (int)(Math.Round((x - min)/((max - min)/12.)))
[|"#002aff"; "#1965ff"; "#3299ff"; "#65ccff"; "#99edff"; "#ccffff"; "#ffffcc"; "#ffee99"; "#ffcc65"; "#ff9932"; "#ff6619"; "#ff2a00"|]
|> Array.rev
|> fun a -> a.[idx]
let handleSelection (v:string) = System.Console.WriteLine v
let viewNoMatchModal dispatch (fmm:FailedMatch) =
div [ClassName "modal fade"; Id "noMatchModal"; Role "dialog"] [
div [] [str "Testing"]
div [ClassName "modal-dialog"] [
div [ClassName "modal-content"] [
div [ClassName "modal-header"] [
h5 [] [str "Failed match commentary"]
button [Type "button"; ClassName "close"; unbox ("data-dismiss", "modal"); unbox("aria-label", "close")] [
span [] [str "x"]
]
]
div [ClassName "modal-body"] [
form [] [
div [] [
yield table [ClassName "table"] [
tbody [] [
tr [] [
td [ Style [TextAlign "left"; VerticalAlign "Middle"; Width "20%"] ] [yield str "Comment"]
td [] [
yield input [
Key ("FailedMatchComment")
HTMLAttr.Type "text"
Name "FailedMatchComment"
DefaultValue ""
ClassName "form-control"
Placeholder "Any comments on the failed match"
Required false
Pattern "\w{0,199}"
Title "No special characters please"
OnChange (fun (ev:React.FormEvent) ->
handleSelection !!ev.target?value
dispatch (PopulateFailedMatchModel {fmm with Comment = !!ev.target?value}))
]
]
]
tr [] [
td [ Style [TextAlign "left"; VerticalAlign "Middle"; Width "20%"] ] [yield str "Override Programme Number"]
td [] [
yield input [
Key ("OverrideProgrammeNumber")
HTMLAttr.Type "text"
MaxLength 8.
Name "OverrideProgrammeNumber"
DefaultValue ""
ClassName "form-control"
Placeholder "Override programme number (if known)"
Required false
OnChange (fun (ev:React.FormEvent) ->
handleSelection !!ev.target?value
dispatch (PopulateFailedMatchModel {fmm with OverrideProgrammeNumber = !!ev.target?value}))
]
]
]
]
]
]
]
]
div [ClassName "modal-footer"] [
button [
Type "button"
ClassName "btn btn-primary"
unbox ("data-dismiss", "modal")
OnClick (fun _ -> dispatch (RecordFailedMatchToDb fmm)
)
] [ str "Submit"]
]
]
]
]
let viewConfidenceModal dispatch (mc:MatchConfidence) =
div [ClassName "modal fade"; Id "confidenceModal"; Role "dialog"] [
// div [ClassName "modal-dialog"; Role "document"] [
div [ClassName "modal-dialog"] [
div [ClassName "modal-content"] [
div [ClassName "modal-header"] [
h5 [] [str "Confidence"]
button [Type "button"; ClassName "close"; unbox ("data-dismiss", "modal"); unbox("aria-label", "close")] [
span [] [str "x"]
]
]
div [ClassName "modal-body"] [
form [] [
div [ClassName "radio"] [
label [] [
input [ Type "radio"
Value "Low"
Checked (mc.MatchConfidence = 0.33)
OnChange(fun (ev:React.FormEvent) -> handleSelection !!ev.target?value
dispatch (MatchedProgAssignConfidence { mc with MatchConfidence = 0.33})
)
]
span [] [str "Low"]
]
]
div [ClassName "radio"] [
label [] [
input [ Type "radio"
Value "Medium"
Checked (mc.MatchConfidence = 0.67)
OnChange(fun (ev:React.FormEvent) -> handleSelection !!ev.target?value
let mc1 = { mc with MatchConfidence = 0.67}
System.Console.WriteLine (sprintf "%A" mc1)
dispatch (MatchedProgAssignConfidence mc1 )
)
]
span [] [str "Medium"]
]
]
div [ClassName "radio"] [
label [] [
input [ Type "radio"
Value "High"
Checked (mc.MatchConfidence = 1.0)
OnChange(fun (ev:React.FormEvent) -> handleSelection !!ev.target?value
dispatch (MatchedProgAssignConfidence { mc with MatchConfidence = 1.0})
)
]
span [] [str "High"]
]
]
]
]
div [ClassName "modal-footer"] [
button [ Type "button"
ClassName "btn btn-primary"
unbox ("data-dismiss", "modal")
OnClick (fun _ -> dispatch (MatchedProgStoreConfidence mc) )
] [ str "Submit"]
]
]
]
]
let view model dispatch =
[
// button [
// Type "button"
// ClassName ("btn btn-danger")
// // unbox("data-toggle", "modal")
// DataToggle "modal"
// unbox ("data-target", "#testModal")
// ]
// [str "Test Modal"]
// div [Id "testModal"; ClassName "modal fade"; Role "dialog"] [
// // div [ClassName "modal-dialog"; Role "document"] [
// div [ClassName "modal-dialog"] [
// div [ClassName "modal-content"] [
// div [ClassName "modal-header"] [
// button [Type "button"; ClassName "close"; unbox ("data-dismiss", "modal"); unbox("aria-label", "close")] [
// // span [unbox("aria-hidden", true)] [str "x"]
// span [] [str "x"]
// ]
// ]
// div [ClassName "modal-body"] [ words 20 "testing" ]
// div [ClassName "modal-footer"] [
// button [ Type "button"; ClassName "btn btn-primary"; unbox ("data-dismiss", "modal")] [ str "Close"]
// ]
// ]
// ]
// ]
words 40 "Suggested NZQA Programme matches"
div [ClassName "row"] [
div [ClassName "col-sm-2"
Style[TextAlign "center"]] [
words 12 "(As a last resort you can indicate no sensible match was found)"
br []
model.NoMatch
|> Option.map (fun nm -> viewNoMatchModal dispatch nm)
|> Option.defaultValue (viewNoMatchModal dispatch { MoENum = model.Provider.MoENum
TECQual = model.TECQual.tecQualificationCode
OverrideProgrammeNumber = None
Comment = None
}
)
button [
ClassName ("btn btn-danger")
DataToggle "modal"
unbox ("data-target", "#noMatchModal")
OnClick (fun _ -> dispatch DisplayModalNoMatch)]
[str "No reasonable match!"]
]
div [ClassName "col-sm-8"] [
div [Style [BackgroundColor "#fdcb21"]] [
h4 [] [yield str (sprintf "Provider: (%i) %s" model.Provider.MoENum model.Provider.Names.Head)]
h4 [] [str "TEC qualification:"]
table [ClassName "table"] [
thead [] [
tr [] [
th [] [str "Title"]
th [] [str "Qual Code"]
th [] [str "NZSCED"]
th [] [str "Qual Level"]
th [] [str "Credits"]
th [] [str "Length (years)"]
]
]
tbody [] [
tr [] [
td [] [
yield str model.TECQual.tecTitle
yield br []
yield br []
yield boldWords 14 "Created: "
yield str (dateOptionView (model.TECQual.tecCreationDate))
yield br []
yield boldWords 14 "Last valid date: "
yield str (dateOptionView (model.TECQual.tecLastDate))
]
td [] [yield str model.TECQual.tecQualificationCode]
td [] [
boldWords 14 "Code: "
span [] [yield str model.TECQual.NZSCED]
br []
boldWords 14 "Broad: "
span [] [yield str ((string)model.TECQual.NZSCEDBroad)]
br []
boldWords 14 "Narrow: "
span [] [yield str ((string)model.TECQual.NZSCEDNarrow)]
br []
boldWords 14 "Detail: "
span [] [yield str ((string)model.TECQual.NZSCEDDetail)]
]
td [] [yield str ((string)model.TECQual.tecNatQual)]
td [] [
words 12 "TEC credits:"
div [] [yield str ((string)model.TECQual.tecCreditsPoints)]
br []
words 12 "TEC's record of credits @ NZQA:"
div [] [yield str ((string)model.TECQual.tecNzqaCredits)]
]
td [] [yield str ((string)model.TECQual.tecNumYears)]
]
]
]
div [] [
table [ClassName "table table-bordered"] [
tbody [] [
tr [] [
td [Style [FontWeight "bold"]] [str "Aim"]
td [] [yield str model.TECQual.tecAim]
]
tr [] [
td [Style [FontWeight "bold"]] [str "Contents"]
td [] [yield str model.TECQual.tecContents]
]
tr [] [
td [Style [FontWeight "bold"]] [str "Entry requirements"]
td [] [yield str model.TECQual.tecEntry]
]
tr [] [
td [Style [FontWeight "bold"]] [str "Evaluation"]
td [] [yield str model.TECQual.tecEvaluation]
]
]
]
]
]
]
div [ClassName "col-sm-2"] [str ""]
]
div [] [
match model.ProgSuggestionList with
| None -> yield h4 [] [str "No programme suggestions for this combination of provider and TEC qualification code"]
| Some psl ->
yield table [ClassName "table"] [
thead [] [
tr [] [
th [] [str "NZQA Programme"]
th [] [str "Programme Version"]
th [] [str "Programme Title"]
th [] [str "MaxLevel"]
th [] [str "MaxCredits"]
th [] [str "Total Study Hours"]
th [] [str "External ID"]
th [] [str "Narratives"]
th [] [str "Distance Measure"]
]
]
tbody [] [
let psl' =
psl
|> Array.sortBy (fun i -> i.DistanceMeasure.TotalDM)
|> Array.take (match psl.Length with
| x when x < 40 -> x
| _ -> 40)
for i in psl' do
yield
tr [Style [BackgroundColor (rowColourClass i.DistanceMeasure.TotalDM 0. 10.)]] [
td [] [
// modal popup rendered for the confidence selecting modal
model.Match
|> Option.map (fun m -> viewConfidenceModal dispatch m)
|> Option.defaultValue (viewConfidenceModal dispatch { MoENum = model.Provider.MoENum
TECQual = model.TECQual.tecQualificationCode
ProgrammeVersionId = (string)i.programme_id
MatchConfidence = 1.0
DistanceCpts = {NZSCEDBroadDM = 0.; NZSCEDNarrowDM = 0.; NZSCEDDetailDM = 0.; NZSCEDLevenstein = 0.; TitleWordMatchDM = 0.; AimWordMatchDM = 0.; ContentWordMatchDM = 0.; CreditsDM = 0.; LevelDM = 0.; TotalDM = 0.}
})
button [
ClassName ("btn btn-primary")
DataToggle "modal"
unbox ("data-target", "#confidenceModal")
OnClick (fun _ -> System.Console.WriteLine i.DistanceMeasure.TotalDM
System.Console.WriteLine i.DistanceMeasure.TitleWordMatchDM
dispatch (MatchedProgToConfidenceRate { MoENum = model.Provider.MoENum;
TECQual = model.TECQual.tecQualificationCode;
ProgrammeVersionId = (string)i.programme_id;
MatchConfidence = 1.0
DistanceCpts = { NZSCEDBroadDM = i.DistanceMeasure.NZSCEDBroadDM
NZSCEDNarrowDM = i.DistanceMeasure.NZSCEDBroadDM
NZSCEDDetailDM = i.DistanceMeasure.NZSCEDDetailDM
NZSCEDLevenstein = i.DistanceMeasure.NZSCEDLevenstein
TitleWordMatchDM = i.DistanceMeasure.TitleWordMatchDM
AimWordMatchDM = i.DistanceMeasure.AimWordMatchDM
ContentWordMatchDM = i.DistanceMeasure.ContentWordMatchDM
CreditsDM = i.DistanceMeasure.CreditsDM
LevelDM = i.DistanceMeasure.LevelDM
TotalDM = i.DistanceMeasure.TotalDM}
} ))]
[str "Match"]
br []
span [] [
yield boldWords 14 "Id "
yield str ((string)i.programme_id)
]
br []
span [] [
yield boldWords 14 "Number "
yield str ((string)i.programme_number)
]
]
td [] [
table [] [
tbody [] [
tr [] [
td [Style[FontWeight "bold"]] [str "Id"]
td [] [yield str ((string)i.programme_version_id)]
]
tr [] [
td [Style[FontWeight "bold"]] [str "Number"]
td [] [yield str ((string)i.programme_version_number)]
]
tr [] [
td [Style[FontWeight "bold"]] [str "Status Code"]
td [] [yield str i.programme_version_status_code]
]
tr [] [
td [Style[FontWeight "bold"]] [str "Status Date"]
td [] [yield str (i.programme_version_status_date.ToString("dd/MM/yyyy"))]
]
]
]
]
td [] [
yield str i.programme_title
yield br []
yield boldWords 14 "match count: "
yield str ((string)i.TitleWordMatchCount)
]
td [] [yield str ((string)i.MaxLevel)]
td [] [yield str ((string)i.MaxCredits)]
td [] [yield str ((string)i.total_study_hours)]
td [] [
boldWords 14 "Code: "
span [] [yield str i.external_id]
br []
boldWords 14 "Broad: "
span [] [yield str ((string)i.NZSCEDBroad)]
br []
boldWords 14 "Narrow: "
span [] [yield str ((string)i.NZSCEDNarrow)]
br []
boldWords 14 "Detail: "
span [] [yield str ((string)i.NZSCEDDetail)]
br []
boldWords 14 "Distance:"
span [] [yield str ((string)i.NZSCEDLevCount)]
]
td [] [
table [Style[BorderSpacing "0px"]] [
thead [] [
tr [] [
th [ColSpan 2.; Style [TextAlign "right"]] [str "match count"]
]
]
tbody [] [
tr [] [
td [] [
// here's where we yield the html for a selected narrative modal popup
// - we have to instantiate it with something... so let's just use a dummy narration
let n = {Title = ""; Text = "" }
yield model.SelectedNarrative |> Option.map NarrativeModal.view |> Option.defaultValue (NarrativeModal.view n)
yield button [
ClassName ("btn btn-link btn-sm")
Style [FontSize "100%"; unbox("padding", 0);]
DataToggle "modal"
unbox ("data-target", "#narrativeModal")
OnClick (fun _ -> dispatch (Narrative {Title = "Aim"; Text = (string)i.aim} ))
] [str "Aim"]
]
td [Style [FontSize "100%"; unbox("padding", 0); unbox("verticalAlign", "middle");]] [yield str ((string)i.AimWordMatchCount)]
]
tr [] [
td [] [
button [
ClassName ("btn btn-link btn-sm")
Style [FontSize "100%"; unbox("padding", 0);]
DataToggle "modal"
unbox ("data-target", "#narrativeModal")
OnClick (fun _ -> dispatch (Narrative {Title = "Content"; Text = (string)i.content} ))
] [str "Content"]
]
td [Style [FontSize "100%"; unbox("padding", 0); unbox("verticalAlign", "middle");]] [yield str ((string)i.ContentWordMatchCount)]
]
tr [] [
td [] [
button [
ClassName ("btn btn-link btn-sm")
Style [FontSize "100%"; unbox("padding", 0);]
DataToggle "modal"
unbox ("data-target", "#narrativeModal")
OnClick (fun _ -> dispatch (Narrative {Title = "Outcome"; Text = (string)i.outcome} ))
] [str "Outcome"]
]
td [] []
]
tr [] [
td [] [
button [
ClassName ("btn btn-link btn-sm")
Style [FontSize "100%"; unbox("padding", 0);]
DataToggle "modal"
unbox ("data-target", "#narrativeModal")
OnClick (fun _ -> dispatch (Narrative {Title = "Entry requirement"; Text = (string)i.entry_requirement} ))
] [str "Entry requirement"]
]
td [] []
]
]
]
]
td [] [yield str (sprintf "%.2f" i.DistanceMeasure.TotalDM)]
]
]
]
]
]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment