Last active
July 31, 2018 08:43
-
-
Save bohdanszymanik/b68e872d63a7b6c5ab9b970217014c53 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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