Simple form working end-to-end

This commit is contained in:
Ozzie Gooen 2020-02-09 20:44:59 +00:00
parent edaa7a7ca5
commit 54e645f252
10 changed files with 245 additions and 258 deletions

View File

@ -1,46 +1 @@
'use strict';
var $$Array = require("bs-platform/lib/js/array.js");
var Curry = require("bs-platform/lib/js/curry.js");
var React = require("react");
var Belt_Option = require("bs-platform/lib/js/belt_Option.js");
var Belt_MapString = require("bs-platform/lib/js/belt_MapString.js");
var Model$ProbExample = require("../Model.bs.js");
var EAFunds_Model$ProbExample = require("./EAFunds_Model.bs.js");
function handleChange(handleChange$1, $$event) {
return Curry._1(handleChange$1, $$event.target.value);
}
var model = EAFunds_Model$ProbExample.Interface.model;
var initialMap = Model$ProbExample.toMaps(model);
function EAFunds_Form(Props) {
var match = React.useState((function () {
return Model$ProbExample.toMaps(model);
}));
var params = match[0];
return $$Array.map((function (parameter) {
var __x = Belt_MapString.get(params[/* inputs */1], parameter[/* id */0]);
var value = Belt_Option.flatMap(__x, (function (param) {
return param[1];
}));
return React.createElement(React.Fragment, undefined, parameter[/* name */1], parameter[/* id */0], React.createElement(Model$ProbExample.Input.Form.make, {
parameter: parameter,
value: value,
onChange: (function (r) {
console.log(r);
return /* () */0;
})
}));
}), $$Array.of_list(model[/* inputs */3]));
}
var make = EAFunds_Form;
exports.handleChange = handleChange;
exports.model = model;
exports.initialMap = initialMap;
exports.make = make;
/* initialMap Not a pure module */
/* This output is empty. Its source's type definitions, externals and/or unused code got optimized away. */

View File

@ -1,24 +1,21 @@
let model = EAFunds_Model.Interface.model;
let handleChange = (handleChange, event) =>
handleChange(ReactEvent.Form.target(event)##value);
let model = EAFunds_Model.Interface.model;
let initialMap = Model.toMaps(model);
[@react.component]
let make = () => {
let (params, changeParams) = React.useState(() => Model.toMaps(model));
model.inputs
|> Array.of_list
|> Array.map((parameter: Model.Input.parameter) => {
let value =
params.inputs->Model.MS.get(parameter.id)
|> Belt.Option.flatMap(_, ((_, b)) => b);
<>
{parameter.name |> ReasonReact.string}
{parameter.id |> ReasonReact.string}
<Model.Input.Form parameter value onChange={r => Js.log(r)} />
</>;
});
};
// let model = EAFunds_Model.Interface.model;
// let handleChange = (handleChange, event) =>
// handleChange(ReactEvent.Form.target(event)##value);
// let model = EAFunds_Model.Interface.model;
// let initialMap = Model.toMaps(model);
// [@react.component]
// let make = () => {
// let (params, changeParams) = React.useState(() => Model.toMaps(model));
// model.inputs
// |> Array.of_list
// |> Array.map((parameter: Model.Input.parameter) => {
// let value =
// params.inputs->Model.MS.get(parameter.id)
// |> Belt.Option.flatMap(_, ((_, b)) => b);
// <>
// {parameter.name |> ReasonReact.string}
// {parameter.id |> ReasonReact.string}
// <Model.Input.Form parameter value onChange={r => Js.log(r)} />
// </>;
// });
/* }*/

View File

@ -1,52 +1,50 @@
open EAFunds_Data;
let handleChange = (handleChange, event) =>
handleChange(ReactEvent.Form.target(event)##value);
[@react.component]
let make = () => {
let (year, setYear) = React.useState(() => 2021.);
<>
<h1> {"EA Funds Forecasting Model 0.1" |> ReasonReact.string} </h1>
<input
type_="number"
value={year |> Js.Float.toString}
onChange={handleChange(r =>
switch (Js.Float.fromString(r)) {
| r when r >= 2020.0 && r <= 2050.0 => setYear(_ => r)
| _ => ()
}
)}
/>
<table className="table-auto">
<thead>
<tr>
<th className="px-4 py-2"> {"Fund Name" |> ReasonReact.string} </th>
<th className="px-4 py-2"> {"Donations" |> ReasonReact.string} </th>
<th className="px-4 py-2"> {"Payouts" |> ReasonReact.string} </th>
</tr>
</thead>
<tbody>
{funds
|> Belt.Array.map(_, r =>
<tr>
<th className="px-4 py-2 border ">
{r.name |> ReasonReact.string}
</th>
<th className="px-4 py-2 border font-normal">
{EAFunds_Model.go(r.group, year, DONATIONS)
|> Model.InputTypes.to_string
|> ReasonReact.string}
</th>
<th className="px-4 py-2 border font-normal">
{EAFunds_Model.go(r.group, year, PAYOUTS)
|> Model.InputTypes.to_string
|> ReasonReact.string}
</th>
</tr>
)
|> ReasonReact.array}
</tbody>
</table>
</>;
};
// open EAFunds_Data;
// let handleChange = (handleChange, event) =>
// handleChange(ReactEvent.Form.target(event)##value);
// [@react.component]
// let make = () => {
// let (year, setYear) = React.useState(() => 2021.);
// <>
// <h1> {"EA Funds Forecasting Model 0.1" |> ReasonReact.string} </h1>
// <input
// type_="number"
// value={year |> Js.Float.toString}
// onChange={handleChange(r =>
// switch (Js.Float.fromString(r)) {
// | r when r >= 2020.0 && r <= 2050.0 => setYear(_ => r)
// | _ => ()
// }
// )}
// />
// <table className="table-auto">
// <thead>
// <tr>
// <th className="px-4 py-2"> {"Fund Name" |> ReasonReact.string} </th>
// <th className="px-4 py-2"> {"Donations" |> ReasonReact.string} </th>
// <th className="px-4 py-2"> {"Payouts" |> ReasonReact.string} </th>
// </tr>
// </thead>
// <tbody>
// {funds
// |> Belt.Array.map(_, r =>
// <tr>
// <th className="px-4 py-2 border ">
// {r.name |> ReasonReact.string}
// </th>
// <th className="px-4 py-2 border font-normal">
// {EAFunds_Model.go(r.group, year, DONATIONS)
// |> Model.InputTypes.to_string
// |> ReasonReact.string}
// </th>
// <th className="px-4 py-2 border font-normal">
// {EAFunds_Model.go(r.group, year, PAYOUTS)
// |> Model.InputTypes.to_string
// |> ReasonReact.string}
// </th>
// </tr>
// )
// |> ReasonReact.array}
// </tbody>
// </table>
// </>;
/* }*/

View File

@ -1,8 +1,10 @@
'use strict';
var Block = require("bs-platform/lib/js/block.js");
var Curry = require("bs-platform/lib/js/curry.js");
var React = require("react");
var Math$ProbExample = require("../Math.bs.js");
var Model$ProbExample = require("../Model.bs.js");
var Prop$ProbExample = require("../Prop.bs.js");
function yearDiff(year) {
return year - 2020.0;
@ -66,46 +68,38 @@ var PayoutsIfAround = {
};
function go(group, year, output) {
return /* FloatCdf */Block.__(3, [calculateDifference(currentValue(group, output), year, /* record */[
return /* FloatCdf */Block.__(2, [calculateDifference(currentValue(group, output), year, /* record */[
/* meanDiff */1.1,
/* stdDiff */1.1
])]);
}
var model_002 = /* assumptions : :: */[
Model$ProbExample.Input.make("Yearly Growth Rate", /* FloatPoint */0, undefined, /* () */0),
/* :: */[
Model$ProbExample.Input.currentYear,
/* [] */0
]
];
var model_003 = /* inputs : :: */[
Model$ProbExample.Input.make("Fund", /* SingleChoice */Block.__(1, [/* record */[
var model_002 = /* inputTypes : array */[
Prop$ProbExample.TypeWithMetadata.make("Fund", /* SelectSingle */Block.__(0, [/* record */[
/* options : :: */[
/* tuple */[
"Animal Welfare Fund",
"animal"
/* record */[
/* id */"animal",
/* name */"Animal Welfare Fund"
],
/* :: */[
/* tuple */[
"Global Health Fund",
"globalHealth"
/* record */[
/* id */"globalHealth",
/* name */"Global Health Fund"
],
/* :: */[
/* tuple */[
"Long Term Future Fund",
"longTerm"
/* record */[
/* id */"longTerm",
/* name */"Long Term Future Fund"
],
/* :: */[
/* tuple */[
"Meta Fund",
"meta"
/* record */[
/* id */"longterm",
/* name */"Meta Fund"
],
/* :: */[
/* tuple */[
"All",
"all"
/* record */[
/* id */"all",
/* name */"All"
],
/* [] */0
]
@ -114,32 +108,21 @@ var model_003 = /* inputs : :: */[
]
],
/* default */"total"
]]), undefined, /* () */0),
/* :: */[
Model$ProbExample.Input.make("Year", /* Year */Block.__(0, [/* record */[
]]), undefined, undefined, undefined, /* () */0),
Prop$ProbExample.TypeWithMetadata.make("Year", /* Year */Block.__(2, [/* record */[
/* default */2030.0,
/* min */2020.0,
/* max */2050.0
]]), undefined, /* () */0),
/* [] */0
]
]]), undefined, undefined, undefined, /* () */0)
];
var model_004 = /* outputs : :: */[
Model$ProbExample.Output.make("Payments", /* FloatCdf */2, undefined, /* () */0),
/* :: */[
Model$ProbExample.Output.make("Payouts", /* FloatCdf */2, undefined, /* () */0),
/* [] */0
]
];
var model_003 = /* outputTypes : array */[];
var model = /* record */[
/* name */"Calculate the payments and payouts of EA Funds based on existing data.",
/* author */"George Harrison",
model_002,
model_003,
model_004,
/* outputConfig : Single */0
model_003
];
function convertChoice(s) {
@ -158,48 +141,54 @@ function convertChoice(s) {
}
function run(p) {
var match = p[/* assumptions */0];
var match$1 = p[/* inputs */1];
if (match) {
var match$2 = match[0];
if (match$2 !== undefined) {
var match$3 = match$2;
if (match$3.tag) {
return ;
} else {
var match$4 = match[1];
if (match$4) {
var match$5 = match$4[0];
if (match$5 !== undefined && !(match$5.tag || match$4[1] || !match$1)) {
var match$6 = match$1[0];
if (match$6 !== undefined) {
var match$7 = match$6;
if (match$7.tag === /* SingleChoice */1 && !match$1[1]) {
return go(convertChoice(match$7[0]), match$3[0], /* DONATIONS */0);
} else {
var partial_arg = p[/* inputValues */1];
var partial_arg$1 = Prop$ProbExample.ValueMap.get;
var get = function (param) {
return partial_arg$1(partial_arg, param);
};
var match = Curry._1(get, "Fund");
var match$1 = Curry._1(get, "Year");
if (match !== undefined) {
var match$2 = match;
switch (match$2.tag | 0) {
case /* SelectSingle */0 :
if (match$1 !== undefined) {
var match$3 = match$1;
switch (match$3.tag | 0) {
case /* FloatPoint */1 :
return go(convertChoice(match$2[0]), match$3[0], /* DONATIONS */0);
case /* SelectSingle */0 :
case /* FloatCdf */2 :
return ;
}
} else {
return ;
}
} else {
return ;
}
} else {
return ;
}
}
} else {
case /* FloatPoint */1 :
case /* FloatCdf */2 :
return ;
}
}
}
function EAFunds_Model$Interface$Form(Props) {
return React.createElement(Prop$ProbExample.ModelForm.make, {
combo: Prop$ProbExample.Combo.fromModel(model)
});
}
var Form = {
make: EAFunds_Model$Interface$Form
};
var Interface = {
model: model,
convertChoice: convertChoice,
run: run
run: run,
Form: Form
};
exports.PayoutsIfAround = PayoutsIfAround;

View File

@ -44,7 +44,7 @@ module PayoutsIfAround = {
let go = (group: group, year: float, output: output) => {
PayoutsIfAround.(
Model.InputTypes.FloatCdf(
Prop.Value.FloatCdf(
calculateDifference(
currentValue(group, output),
year,
@ -55,34 +55,30 @@ let go = (group: group, year: float, output: output) => {
};
module Interface = {
open Model;
open Prop;
let model = {
let model: Model.t = {
name: "Calculate the payments and payouts of EA Funds based on existing data.",
author: "George Harrison",
assumptions: [
Input.make(~name="Yearly Growth Rate", ~parameterType=FloatPoint, ()),
Input.currentYear,
],
inputs: [
Input.make(
inputTypes: [|
TypeWithMetadata.make(
~name="Fund",
~parameterType=
SingleChoice({
~type_=
SelectSingle({
default: Some("total"),
options: [
("Animal Welfare Fund", "animal"),
("Global Health Fund", "globalHealth"),
("Long Term Future Fund", "longTerm"),
("Meta Fund", "meta"),
("All", "all"),
{name: "Animal Welfare Fund", id: "animal"},
{name: "Global Health Fund", id: "globalHealth"},
{name: "Long Term Future Fund", id: "longTerm"},
{name: "Meta Fund", id: "longterm"},
{name: "All", id: "all"},
],
}),
(),
),
Input.make(
TypeWithMetadata.make(
~name="Year",
~parameterType=
~type_=
Year({
default: Some(2030.0),
min: Some(2020.0),
@ -90,12 +86,8 @@ module Interface = {
}),
(),
),
],
outputs: [
Output.make(~name="Payments", ~parameterType=FloatCdf, ()),
Output.make(~name="Payouts", ~parameterType=FloatCdf, ()),
],
outputConfig: Single,
|],
outputTypes: [||],
};
let convertChoice = (s: string) =>
@ -107,14 +99,17 @@ module Interface = {
| _ => All
};
let run = (p: Model.modelParams) => {
switch (p.assumptions, p.inputs) {
| (
[Some(Year(intendedYear)), Some(Year(currentYear))],
[Some(SingleChoice(fund))],
) =>
let run = (p: Combo.t) => {
let get = Prop.ValueMap.get(p.inputValues);
switch (get("Fund"), get("Year")) {
| (Some(SelectSingle(fund)), Some(FloatPoint(intendedYear))) =>
Some(go(convertChoice(fund), intendedYear, DONATIONS))
| _ => None
};
};
module Form = {
[@react.component]
let make = () => <Prop.ModelForm combo={Prop.Combo.fromModel(model)} />;
};
};

View File

@ -2,10 +2,10 @@
var React = require("react");
var ReactDOMRe = require("reason-react/src/ReactDOMRe.js");
var EAFunds_Form2$ProbExample = require("./EAFunds/EAFunds_Form2.bs.js");
var EAFunds_Model$ProbExample = require("./EAFunds/EAFunds_Model.bs.js");
((import('./styles/index.css')));
ReactDOMRe.renderToElementWithId(React.createElement(EAFunds_Form2$ProbExample.make, { }), "app");
ReactDOMRe.renderToElementWithId(React.createElement(EAFunds_Model$ProbExample.Interface.Form.make, { }), "app");
/* Not a pure module */

View File

@ -1,2 +1,2 @@
[%bs.raw {|import('./styles/index.css')|}];
ReactDOMRe.renderToElementWithId(<EAFunds_Form2 />, "app");
ReactDOMRe.renderToElementWithId(<EAFunds_Model.Interface.Form />, "app");

View File

@ -1,17 +0,0 @@
type yearAsFloat = {
min: option(float),
max: option(float),
};
type namedValue('a) = {
name: string,
value: 'a,
};
type choice('a) = list(namedValue('a));
type output =
| A
| B;
let nOutput: choice(output) = [{name: "sdfsdf", value: A}];

View File

@ -51,12 +51,12 @@ module Type = {
module ValueMap = {
module MS = Belt.Map.String;
type t = MS.t(Value.t);
let get = MS.get;
let get = (t: t, s) => MS.get(t, s);
let keys = MS.keysToArray;
let map = MS.map;
let fromArray = (r): t => MS.fromArray(r);
let values = (t: t) => t |> MS.valuesToArray;
let update = MS.update;
let update = (t, k, v) => MS.update(t, k, _ => v);
let toArray = MS.toArray;
let fromOptionalMap = (t: MS.t(option(Value.t))): t =>
MS.keep(t, (_, d) => E.O.isSome(d))
@ -121,7 +121,7 @@ module Model = {
};
module Combo = {
type combo = {
type t = {
model: Model.t,
inputValues: ValueMap.t,
outputValues: ValueMap.t,
@ -135,20 +135,90 @@ module Combo = {
)
|> ValueMap.fromOptionalArray;
let isValid = (t: combo) =>
let isValid = t =>
t.model
|> Model.InputTypes.keys
|> E.A.fmap(ValueMap.get(t.inputValues))
|> Belt.Array.some(_, E.O.isNone);
let update =
(
t: combo,
key: string,
onUpdate: option(Value.t) => option(Value.t),
) =>
let update = (t, key: string, onUpdate: option(Value.t)) =>
ValueMap.update(t.inputValues, key, onUpdate);
};
let run = (t: combo, f): ValueMap.t => f(t.inputValues);
let updateInputValue = (t, k, u) => {
...t,
inputValues: InputValues.update(t, k, u),
};
let inputTypeValuePairs = (t: t) =>
t.model.inputTypes
|> E.A.fmap((i: TypeWithMetadata.t) =>
(i, ValueMap.get(t.inputValues, i.id))
);
let fromModel = (t: Model.t) => {
model: t,
inputValues: InputValues.defaults(t),
outputValues: InputValues.defaults(t),
};
let run = (t: t, f): ValueMap.t => f(t.inputValues);
};
module ValueForm = {
let handleChange = (handleChange, event) =>
handleChange(ReactEvent.Form.target(event)##value);
type onChange = option(Value.t) => unit;
[@react.component]
let make =
(
~type_: TypeWithMetadata.t,
~value: option(Value.t),
~onChange: onChange,
) => {
switch (type_.type_, value) {
| (Year(_), Some(FloatPoint(r))) =>
<input
type_="number"
value={r |> Js.Float.toString}
onChange={handleChange(r =>
switch (Js.Float.fromString(r)) {
| r => onChange(Some(Value.FloatPoint(r)))
}
)}
/>
| (FloatPoint(_), Some(FloatPoint(r))) =>
<input type_="number" value={r |> Js.Float.toString} />
| (Year(_), _)
| (FloatPoint(_), _) => <input type_="number" value="" />
| (SelectSingle(_), _) =>
<div> {"Single Choice" |> ReasonReact.string} </div>
};
};
};
module ModelForm = {
let handleChange = (handleChange, event) =>
handleChange(ReactEvent.Form.target(event)##value);
[@react.component]
let make = (~combo: Combo.t) => {
let (combo, setCombo) = React.useState(() => combo);
<div>
{Combo.inputTypeValuePairs(combo)
|> E.A.fmap(((type_, value)) =>
<ValueForm
type_
value
onChange={newValue =>
setCombo(_ =>
Combo.updateInputValue(combo, type_.id, newValue)
)
}
/>
)
|> ReasonReact.array}
</div>;
};
};