Skip to content

Commit 3aa056e

Browse files
authored
Merge pull request #12 from jchavarri/styles
Improve styles
2 parents dc66179 + dc46957 commit 3aa056e

24 files changed

+394
-200
lines changed

client/src/App.re

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,14 @@ module Handlers = {
5858
{(authors => <PageAuthorExcerpts authors />)}
5959
</Client.FetchRender>
6060
};
61+
62+
let counter = _req => <PageCounter />;
6163
};
6264
module Api = {
6365
// Api routes should never be loaded from React app, show the backing page in case it happens
6466
let excerpts_by_author = Pages.excerpts_by_author;
6567
let authors_with_excerpts = Pages.authors_with_excerpts;
68+
let add_excerpt = _ => <PageNotFound />;
6669
};
6770
};
6871

client/src/Bridge.re

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,16 @@ module React = {
44
};
55

66
module Dom = {
7+
module Div = {
8+
[@react.component]
9+
let make = (~cls as className=?, ~children) => {
10+
<div ?className> children </div>;
11+
};
12+
};
713
module Ul = {
814
[@react.component]
9-
let make = (~children) => {
10-
<ul> children </ul>;
15+
let make = (~cls as className=?, ~children) => {
16+
<ul ?className> children </ul>;
1117
};
1218
};
1319
module Form = {
@@ -19,8 +25,13 @@ module Dom = {
1925
| `Get => "GET"
2026
| `Post => "POST";
2127
[@react.component]
22-
let make = (~action, ~form_method, ~children) => {
23-
<form action method={stringOfMethod(form_method)}> children </form>;
28+
let make = (~action=?, ~form_method=?, ~onSubmit=?, ~children) => {
29+
<form
30+
?action
31+
method=?{form_method->Belt.Option.map(stringOfMethod)}
32+
?onSubmit>
33+
children
34+
</form>;
2435
};
2536
};
2637
module Input = {
@@ -54,8 +65,15 @@ module Dom = {
5465
| `Url => "url"
5566
| `Week => "week";
5667
[@react.component]
57-
let make = (~input_type, ~name=?, ~value=?) => {
58-
<input type_={stringOfInputType(input_type)} ?name ?value />;
68+
let make =
69+
(~cls as className=?, ~input_type, ~name=?, ~value=?, ~onChange=?) => {
70+
<input
71+
?className
72+
type_={stringOfInputType(input_type)}
73+
?name
74+
?value
75+
?onChange
76+
/>;
5977
};
6078
};
6179
module P = {
@@ -66,8 +84,8 @@ module Dom = {
6684
};
6785
module Textarea = {
6886
[@react.component]
69-
let make = (~name, ~onChange, ~value) => {
70-
<textarea name onChange value />;
87+
let make = (~cls as className=?, ~name, ~onChange=?, ~value) => {
88+
<textarea ?className name ?onChange value />;
7189
};
7290
};
7391
};

client/src/Client.re

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,25 @@ type data('a, 'b) =
44
| Loading
55
| Finished(Result.t('a, 'b));
66

7-
let get = (url, decoder) => {
7+
type error = [
8+
| `DecodingError(option(string))
9+
| `NetworkError(Js.Promise.error)
10+
];
11+
12+
let request = (~method_=Fetch.Get, ~input=?, url, decoder) => {
813
Js.Promise.(
9-
Fetch.fetchWithInit(url, Fetch.RequestInit.make(~method_=Get, ()))
14+
Fetch.fetchWithInit(
15+
url,
16+
Fetch.RequestInit.make(
17+
~method_,
18+
~body=?{
19+
input->Belt.Option.map(input =>
20+
input->Js.Json.stringify->Fetch.BodyInit.make
21+
);
22+
},
23+
(),
24+
),
25+
)
1026
|> then_(res => {
1127
res->Fetch.Response.status >= 400
1228
? Js.log(
@@ -32,6 +48,12 @@ let get = (url, decoder) => {
3248
);
3349
};
3450

51+
let errorToString: error => string =
52+
fun
53+
| `DecodingError(Some(msg)) => Printf.sprintf("Decoding error: %s", msg)
54+
| `DecodingError(None) => Printf.sprintf("Decoding error")
55+
| `NetworkError(_err) => "Network error";
56+
3557
let usePrevious = value => {
3658
let valueRef = React.useRef(value);
3759
React.useEffect(() => {
@@ -51,7 +73,7 @@ module FetchRender = {
5173
React.useEffect2(
5274
() => {
5375
setData(_ => Loading);
54-
get(url, decoder)
76+
request(url, decoder)
5577
|> Js.Promise.then_(res =>
5678
setData(_ => Finished(res))->Js.Promise.resolve
5779
)

client/src/Index.re

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
[%bs.raw {|require("./index.css")|}];
2-
31
switch (Bindings.getElementById("root")) {
42
| Some(el) => ReactDOMRe.hydrate(<App />, el)
53
| None => ()

client/src/index.css

Lines changed: 0 additions & 14 deletions
This file was deleted.

client/src/index.html

Lines changed: 0 additions & 11 deletions
This file was deleted.

server/lib/handlers.ml

Lines changed: 23 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
open Core
22
open Tyxml
33
open Opium.Std
4+
open Lwt.Syntax
45

56
(** A <head> component shared by all pages *)
67
let default_head =
@@ -9,10 +10,15 @@ let default_head =
910
(title (txt "OCaml Webapp Tutorial"))
1011
[
1112
meta ~a:[ a_charset "UTF-8" ] ();
13+
meta
14+
~a:
15+
[ a_name "viewport"; a_content "width=device-width, initial-scale=1" ]
16+
();
1217
link ~rel:[ `Icon ]
1318
~a:[ a_mime_type "image/x-icon" ]
1419
~href:"/static/favicon.ico" ();
15-
link ~rel:[ `Stylesheet ] ~href:"/static/style.css" ();
20+
link ~rel:[ `Stylesheet ]
21+
~href:"https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css" ();
1622
]
1723

1824
(** The basic page layout, emitted as an [`Html string] which Opium can use as a
@@ -54,22 +60,6 @@ let respond_or_err resp = function
5460
Html.
5561
[ p [ txt (Printf.sprintf "Oh no! Something went wrong: %s" err) ] ]
5662

57-
let excerpt_of_form_data data =
58-
let find data key =
59-
let open Core in
60-
(* NOTE Should handle error in case of missing fields *)
61-
List.Assoc.find_exn ~equal:String.equal data key |> String.concat
62-
in
63-
let author = find data "author"
64-
and excerpt = find data "excerpt"
65-
and source = find data "source"
66-
and page =
67-
match find data "page" with
68-
| "" -> None
69-
| p -> Some p
70-
in
71-
Lwt.return Shared.Excerpt_t.{ author; excerpt; source; page }
72-
7363
(** The route handlers for our app *)
7464
module Handlers = struct
7565
type request = Request.t
@@ -78,33 +68,35 @@ module Handlers = struct
7868

7969
module Pages = struct
8070
(** Defines a handler that replies to requests at the root endpoint *)
81-
let root _req = respond' @@ basic_page (Shared.PageWelcome.make ())
71+
let root _req = respond' @@ basic_page [ Shared.PageWelcome.make () ]
8272

8373
(** Defines a handler that takes a path parameter from the route *)
84-
let hello lang _req = respond' @@ basic_page (Shared.PageHello.make ~lang)
74+
let hello lang _req = respond' @@ basic_page [ Shared.PageHello.make ~lang ]
8575

8676
(** Fallback handler in case the endpoint is called without a language parameter *)
8777
let hello_fallback _req =
88-
respond' @@ basic_page (Shared.PageHelloFallback.make ())
78+
respond' @@ basic_page [ Shared.PageHelloFallback.make () ]
8979

9080
let excerpts_add _req =
91-
respond' @@ basic_page (Shared.PageAddExcerpt.make ())
81+
respond' @@ basic_page [ Shared.PageAddExcerpt.make () ]
9282

9383
let excerpts_by_author name req =
9484
let open Lwt in
9585
Db.Get.excerpts_by_author name req
9686
>>= respond_or_err @@ fun excerpts ->
9787
page_with_payload
9888
(Shared.PageExcerpts_j.string_of_payload excerpts)
99-
(Shared.PageExcerpts.make ~excerpts)
89+
[ Shared.PageExcerpts.make ~excerpts ]
10090

10191
let authors_with_excerpts req =
10292
let open Lwt in
10393
Db.Get.authors req
10494
>>= respond_or_err @@ fun authors ->
10595
page_with_payload
10696
(Shared.PageAuthorExcerpts_j.string_of_payload authors)
107-
(Shared.PageAuthorExcerpts.make ~authors)
97+
[ Shared.PageAuthorExcerpts.make ~authors ]
98+
99+
let counter _req = respond' @@ basic_page [ Shared.PageCounter.make () ]
108100
end
109101

110102
module Api = struct
@@ -119,18 +111,14 @@ module Handlers = struct
119111
Db.Get.excerpts_by_author name req
120112
>>= respond_or_err (fun excerpts ->
121113
json (Shared.PageExcerpts_j.string_of_payload excerpts))
122-
end
123-
end
124114

125-
(** The POST route handlers for our app *)
126-
module Post = struct
127-
let excerpts_add req =
128-
let open Lwt in
129-
(* NOTE Should handle possible error arising from invalid data *)
130-
App.urlencoded_pairs_of_body req >>= excerpt_of_form_data >>= fun excerpt ->
131-
Db.Update.add_excerpt excerpt req
132-
>>= respond_or_err (fun () ->
133-
basic_page (Shared.PageExcerptAdded.make ~excerpt))
115+
let add_excerpt req =
116+
let open Lwt in
117+
let* str = App.string_of_body_exn req in
118+
let excerpt = Shared.Excerpt_j.t_of_string str in
119+
Db.Update.add_excerpt excerpt req
120+
>>= respond_or_err (fun () -> json (Shared.Excerpt_j.string_of_t excerpt))
121+
end
134122
end
135123

136124
module Router = Shared.Router.Make (Handlers)
@@ -148,4 +136,4 @@ let create_middleware ~router =
148136
let m = create_middleware ~router:(Shared.Method_routes.one_of Router.routes)
149137

150138
let four_o_four =
151-
not_found (fun _req -> respond' @@ basic_page (Shared.PageNotFound.make ()))
139+
not_found (fun _req -> respond' @@ basic_page [ Shared.PageNotFound.make () ])

server/static/index.html

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22
<html lang="en">
33
<head>
44
<meta charset="UTF-8" />
5-
<title>Reason react starter</title>
5+
<meta name="viewport" content="width=device-width, initial-scale=1">
6+
<title>OCaml webapp</title>
7+
<link href="https://unpkg.com/tailwindcss@^1.0/dist/tailwind.min.css" rel="stylesheet">
68
</head>
79
<body>
810
<div id="root"></div>

server/static/style.css

Lines changed: 0 additions & 35 deletions
This file was deleted.

0 commit comments

Comments
 (0)