Skip to content

Commit 7de7826

Browse files
committed
Add modal result + download ✨
1 parent 0bb4b3c commit 7de7826

11 files changed

+177
-33
lines changed

package-lock.json

+32-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

+3-1
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,11 @@
1010
"test:live": "webpack-dev-server --mode development --env test"
1111
},
1212
"dependencies": {
13+
"bulma": "^0.9.3",
1314
"react": "^17.0.2",
1415
"react-dom": "^17.0.2",
15-
"bulma": "^0.9.3"
16+
"sweetalert2": "^10.16.11",
17+
"sweetalert2-react-content": "^3.3.3"
1618
},
1719
"devDependencies": {
1820
"copy-webpack-plugin": "^10.0.0",

paket.dependencies

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ source https://api.nuget.org/v3/index.json
22
framework: net6.0
33
storage: none
44

5+
nuget Feliz.SweetAlert
56
nuget Feliz.UseElmish 1.6.0
67
nuget Fsharp.Core ~> 6
78
nuget Fable.Remoting.Giraffe

paket.lock

+9
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@ NUGET
6161
Fable.Parsimmon (4.1)
6262
Fable.Core (>= 3.0)
6363
FSharp.Core (>= 4.6.2)
64+
Fable.Promise (2.2.2)
65+
Fable.Core (>= 3.1.5)
66+
FSharp.Core (>= 4.7.2)
6467
Fable.React (7.4.3)
6568
Fable.Browser.Dom (>= 2.4.4)
6669
Fable.Core (>= 3.2.7)
@@ -141,6 +144,12 @@ NUGET
141144
Feliz.CompilerPlugins (1.8)
142145
Fable.AST (>= 3.0)
143146
FSharp.Core (>= 4.7.2)
147+
Feliz.SweetAlert (2.8)
148+
Fable.Core (>= 3.1.5 < 4.0)
149+
Fable.Elmish.React (>= 3.0.1 < 4.0)
150+
Fable.Promise (>= 2.1 < 3.0)
151+
Feliz (>= 1.5 < 2.0)
152+
FSharp.Core (>= 4.7.2)
144153
Feliz.UseElmish (1.6)
145154
Fable.Elmish (>= 3.0.6)
146155
Feliz (>= 1.55)

src/Client/Client.fsproj

+1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
<Compile Include="Api.fs" />
1212
<Compile Include="State.fs" />
1313
<Compile Include="ModalLogic.fs" />
14+
<Compile Include="View\Components\SweetAlertModals.fs" />
1415
<Compile Include="View\Components\ResultModal.fs" />
1516
<Compile Include="View\Components\Footer.fs" />
1617
<Compile Include="View\Components\Navbar.fs" />

src/Client/Index.fs

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ let view (model: Model) (dispatch: Msg -> unit) =
1717
Client.Components.Navbar.Navbar model dispatch
1818
Client.View.MainView.hero model dispatch
1919
ModalLogic.modalContainer
20-
Client.View.InputView.View model.HasJobRunning dispatch
20+
Client.View.InputView.View model.Version model.HasJobRunning dispatch
2121
Client.Components.Footer.footer model.Version
2222
]
2323
]

src/Client/State.fs

+3-1
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,13 @@ type Versions = {
1717

1818
type Model = {
1919
Version : Versions
20+
Result : DeepStabP.Types.PredictorResponse []
2021
HasJobRunning : bool
2122
} with
2223
static member init = {
23-
HasJobRunning = false
2424
Version = Versions.init
25+
Result = Array.empty
26+
HasJobRunning = false
2527
}
2628

2729
type Msg =

src/Client/Update.fs

+6-5
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@ let init () : Model * Cmd<Msg> =
1414

1515
model, cmd
1616

17+
open Feliz.SweetAlert
18+
1719
let update (msg: Msg) (model: Model) : Model * Cmd<Msg> =
1820
match msg with
1921
| GetVersionUIRequest ->
@@ -48,10 +50,9 @@ let update (msg: Msg) (model: Model) : Model * Cmd<Msg> =
4850
(Error >> PredictionResponse)
4951
nextModel, cmd
5052
| PredictionResponse (Ok response) ->
51-
let nextModel = { model with HasJobRunning = false }
52-
ModalLogic.renderModal(Client.Components.ResultModal.Results())
53-
nextModel, Cmd.none
53+
let nextModel = { model with HasJobRunning = false; Result = response }
54+
let modal = Client.Components.SweetAlertModals.resultModal_success(nextModel)
55+
nextModel, modal
5456
| PredictionResponse (Error e) ->
5557
let nextModel = { model with HasJobRunning = false }
56-
Browser.Dom.window.alert (e)
57-
nextModel, Cmd.none
58+
nextModel, Cmd.Swal.Simple.error(e.Message, e.InnerException.ToString())
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
module Client.Components.SweetAlertModals
2+
3+
open Elmish
4+
open Feliz
5+
open Feliz.Bulma
6+
open Feliz.SweetAlert
7+
8+
module private ResultModal_success =
9+
10+
open Fable.Core.JsInterop
11+
12+
let resultsToCsv (results: DeepStabP.Types.PredictorResponse []) =
13+
results
14+
|> Array.map (fun x ->
15+
$"{x.Protein},{x.MeltingTemp}{System.Environment.NewLine}"
16+
)
17+
|> String.concat ""
18+
19+
let downloadResults (filename: string) (filedata: string) =
20+
let element = Browser.Dom.document.createElement("a");
21+
element.setAttribute("href", "data:text/plain;charset=utf-8," + Fable.Core.JS.encodeURIComponent(filedata));
22+
element.setAttribute("download", filename);
23+
24+
element?style?display <- "None";
25+
let _ = Browser.Dom.document.body.appendChild(element);
26+
27+
element.click();
28+
29+
Browser.Dom.document.body.removeChild(element) |> ignore
30+
()
31+
32+
let body (model:State.Model) =
33+
Html.div [
34+
prop.style [
35+
style.maxHeight (length.vh 40);
36+
style.overflow.auto
37+
]
38+
prop.children [
39+
Bulma.table [
40+
Bulma.table.isFullWidth
41+
Bulma.table.isBordered
42+
prop.children [
43+
Html.thead [Html.tr [
44+
Html.th "index"
45+
Html.th "Protein"
46+
Html.th "mt"
47+
]]
48+
Html.tbody [
49+
for i in 0 .. model.Result.Length-1 do
50+
yield
51+
Html.tr [
52+
let r = model.Result.[i]
53+
Html.td i
54+
Html.td r.Protein
55+
Html.td r.MeltingTemp
56+
]
57+
]
58+
]
59+
]
60+
]
61+
]
62+
63+
open ResultModal_success
64+
65+
66+
/// if more customization is needed one can fallback to ModalLogic
67+
let resultModal_success (model:State.Model)=
68+
Cmd.Swal.fire([
69+
swal.icon.success
70+
swal.title "Success"
71+
swal.html (body model)
72+
swal.showCloseButton true
73+
swal.showCancelButton true
74+
swal.cancelButtonText "Close"
75+
swal.cancelButtonColor "#E31B4C"
76+
swal.showConfirmButton true
77+
swal.confirmButtonText "Download as .csv"
78+
swal.reverseButtons true
79+
swal.width (length.perc 70)
80+
swal.preConfirm (fun _ ->
81+
let fileName =
82+
[
83+
System.DateTime.UtcNow.ToString("yyyyMMdd_hhmmss")
84+
"DeepStabP"
85+
] |> String.concat "_"
86+
model.Result
87+
|> resultsToCsv
88+
|> downloadResults fileName
89+
)
90+
])

src/Client/View/InputView.fs

+29-23
Original file line numberDiff line numberDiff line change
@@ -124,27 +124,33 @@ module private Update =
124124

125125
open State
126126

127-
let private validateInputState (state:InputState) =
128-
match state.SeqMode with
129-
| Some Single ->
130-
match state.SingleSequence with
131-
| "" -> false, "No data provided"
132-
| _ ->
133-
match state.HasValidFasta with
134-
| false -> false, "Fasta is invalid"
135-
| _ -> true, "Start computation"
136-
| Some Fasta ->
137-
match state.FastaFileData with
138-
| "" -> false, "No data provided"
139-
| x when x.Split([|'>'|],System.StringSplitOptions.RemoveEmptyEntries).Length > 1000 ->
140-
false, "Too many sequences (>1000)."
141-
| _ ->
142-
match state.HasValidFasta with
143-
| false -> false, "Fasta is invalid"
144-
| _ -> true, "Start computation"
145-
| None ->
146-
false, "No data provided"
147-
127+
let private validateInputState (versions: State.Versions) (state:InputState) =
128+
let validateInput() =
129+
match state.SeqMode with
130+
| Some Single ->
131+
match state.SingleSequence with
132+
| "" -> false, "No data provided"
133+
| _ ->
134+
match state.HasValidFasta with
135+
| false -> false, "Fasta is invalid"
136+
| _ -> true, "Start computation"
137+
| Some Fasta ->
138+
match state.FastaFileData with
139+
| "" -> false, "No data provided"
140+
| x when x.Split([|'>'|],System.StringSplitOptions.RemoveEmptyEntries).Length > 1000 ->
141+
false, "Too many sequences (>1000)."
142+
| _ ->
143+
match state.HasValidFasta with
144+
| false -> false, "Fasta is invalid"
145+
| _ -> true, "Start computation"
146+
| None ->
147+
false, "No data provided"
148+
match versions with
149+
| noServerConnection when versions.UI = "" ->
150+
false, "No connection to server"
151+
| noApiConnection when versions.Api = "" ->
152+
false, "No connection to predictor service"
153+
| _ -> validateInput()
148154
module private UploadHandler =
149155
open Fable.Core.JsInterop
150156

@@ -327,10 +333,10 @@ let private startPredictionRight (hasJobRunning:bool) (isValidState:bool) (butto
327333
open Update
328334

329335
[<ReactComponent>]
330-
let View (hasJobRunning: bool) (dispatch : Msg -> unit) =
336+
let View (versions: State.Versions) (hasJobRunning: bool) (dispatch : Msg -> unit) =
331337
let state, setState = React.useElmish(init, update, [||])
332338

333-
let isValidState, buttonMsg = validateInputState state
339+
let isValidState, buttonMsg = validateInputState versions state
334340

335341
div [Style [FlexGrow 1; Display DisplayOptions.Flex; FlexDirection "column"]] [
336342
Columns.columns [Columns.CustomClass "ProcessDecision"; Columns.Props [Style [FlexGrow 1]]] [

src/Client/paket.references

+2-1
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,5 @@ Feliz
99
Feliz.Bulma
1010
Fsharp.Core
1111
Fulma
12-
Feliz.UseElmish
12+
Feliz.UseElmish
13+
Feliz.SweetAlert

0 commit comments

Comments
 (0)