diff options
author | Akshay <[email protected]> | 2020-12-24 05:21:40 +0000 |
---|---|---|
committer | Akshay <[email protected]> | 2020-12-24 05:21:40 +0000 |
commit | 9d2b6ee10ec5359cc91769d430485c8c869ba1a8 (patch) | |
tree | b2d541e575ab6e3f70cb709f156f06afca085881 /frontend/src/Product.elm | |
parent | 7c65421328552b08e64df25e224fe9d54d363e6e (diff) |
monorepo
Diffstat (limited to 'frontend/src/Product.elm')
-rw-r--r-- | frontend/src/Product.elm | 302 |
1 files changed, 302 insertions, 0 deletions
diff --git a/frontend/src/Product.elm b/frontend/src/Product.elm new file mode 100644 index 0000000..0ea0ce1 --- /dev/null +++ b/frontend/src/Product.elm | |||
@@ -0,0 +1,302 @@ | |||
1 | module Product exposing (..) | ||
2 | |||
3 | import Browser | ||
4 | import Browser.Navigation as Nav | ||
5 | import Html exposing (..) | ||
6 | import Html.Attributes exposing (..) | ||
7 | import Html.Events exposing (..) | ||
8 | import Http | ||
9 | import Json.Decode as D | ||
10 | import Json.Encode as Encode | ||
11 | import Url | ||
12 | import Url.Parser as P exposing ((</>), Parser, int, oneOf, s, string) | ||
13 | |||
14 | |||
15 | type SubmitStatus | ||
16 | = SubmitSuccess | ||
17 | | SubmitFail | ||
18 | | Submitting | ||
19 | | NotSubmitted | ||
20 | |||
21 | |||
22 | type alias Product = | ||
23 | { id : Int | ||
24 | , name : String | ||
25 | , kind : Maybe String | ||
26 | , price : Float | ||
27 | , description : Maybe String | ||
28 | } | ||
29 | |||
30 | |||
31 | emptyProduct = | ||
32 | Product -1 "" Nothing 0 Nothing | ||
33 | |||
34 | |||
35 | type alias Rating = | ||
36 | { commentDate : String | ||
37 | , commentText : Maybe String | ||
38 | , customerName : String | ||
39 | , productName : String | ||
40 | , stars : Int | ||
41 | } | ||
42 | |||
43 | |||
44 | type alias Model = | ||
45 | { pageStatus : Status | ||
46 | , listing : Product | ||
47 | , ratings : List Rating | ||
48 | , ratingStars : Int | ||
49 | , ratingText : String | ||
50 | , addRatingStatus : SubmitStatus | ||
51 | } | ||
52 | |||
53 | |||
54 | type Status | ||
55 | = Loading | ||
56 | | Loaded | ||
57 | | NotLoaded | ||
58 | |||
59 | |||
60 | type Msg | ||
61 | = ListingLoaded (Result Http.Error Product) | ||
62 | | RatingsLoaded (Result Http.Error (List Rating)) | ||
63 | | FetchProduct Int | ||
64 | | FetchRatings Int | ||
65 | | AddRatingStars Int | ||
66 | | AddRatingComment String | ||
67 | | AddRatingPressed | ||
68 | | AddRatingSuccess (Result Http.Error ()) | ||
69 | | AddRatingFail | ||
70 | | AddToCartSuccess (Result Http.Error ()) | ||
71 | | AddToCartPressed | ||
72 | |||
73 | |||
74 | init : Model | ||
75 | init = | ||
76 | Model NotLoaded emptyProduct [] 0 "" NotSubmitted | ||
77 | |||
78 | |||
79 | update : Msg -> Model -> ( Model, Cmd Msg ) | ||
80 | update msg model = | ||
81 | case msg of | ||
82 | ListingLoaded res -> | ||
83 | case res of | ||
84 | Ok s -> | ||
85 | ( { model | listing = s, pageStatus = Loaded }, Cmd.none ) | ||
86 | |||
87 | Err e -> | ||
88 | let | ||
89 | _ = | ||
90 | Debug.log "error" e | ||
91 | in | ||
92 | ( { model | pageStatus = NotLoaded }, Cmd.none ) | ||
93 | |||
94 | RatingsLoaded res -> | ||
95 | case res of | ||
96 | Ok s -> | ||
97 | ( { model | ratings = s, pageStatus = Loaded }, Cmd.none ) | ||
98 | |||
99 | Err e -> | ||
100 | let | ||
101 | _ = | ||
102 | Debug.log "error" e | ||
103 | in | ||
104 | ( { model | pageStatus = NotLoaded }, Cmd.none ) | ||
105 | |||
106 | FetchProduct id -> | ||
107 | ( { model | pageStatus = Loading }, fetchListing id ) | ||
108 | |||
109 | FetchRatings id -> | ||
110 | ( { model | pageStatus = Loading }, fetchRatings id ) | ||
111 | |||
112 | AddRatingStars i -> | ||
113 | ( { model | ratingStars = i }, Cmd.none ) | ||
114 | |||
115 | AddRatingComment s -> | ||
116 | ( { model | ratingText = s }, Cmd.none ) | ||
117 | |||
118 | AddRatingPressed -> | ||
119 | ( { model | addRatingStatus = Submitting } | ||
120 | , submitRating model | ||
121 | ) | ||
122 | |||
123 | AddRatingSuccess res -> | ||
124 | case res of | ||
125 | Ok _ -> | ||
126 | ( { model | addRatingStatus = SubmitSuccess }, fetchRatings model.listing.id ) | ||
127 | |||
128 | Err _ -> | ||
129 | ( { model | addRatingStatus = SubmitFail }, Cmd.none ) | ||
130 | |||
131 | AddRatingFail -> | ||
132 | ( { model | addRatingStatus = SubmitFail }, Cmd.none ) | ||
133 | |||
134 | AddToCartPressed -> | ||
135 | ( model, addToCart model ) | ||
136 | |||
137 | AddToCartSuccess _ -> | ||
138 | ( model, Cmd.none ) | ||
139 | |||
140 | |||
141 | decodeProduct : D.Decoder Product | ||
142 | decodeProduct = | ||
143 | D.map5 Product | ||
144 | (D.field "id" D.int) | ||
145 | (D.field "name" D.string) | ||
146 | (D.field "kind" (D.nullable D.string)) | ||
147 | (D.field "price" D.float) | ||
148 | (D.field "description" (D.nullable D.string)) | ||
149 | |||
150 | |||
151 | decodeRating : D.Decoder Rating | ||
152 | decodeRating = | ||
153 | D.map5 Rating | ||
154 | (D.field "comment_date" D.string) | ||
155 | (D.field "comment_text" (D.nullable D.string)) | ||
156 | (D.field "customer_name" D.string) | ||
157 | (D.field "product_name" D.string) | ||
158 | (D.field "stars" D.int) | ||
159 | |||
160 | |||
161 | decodeRatings : D.Decoder (List Rating) | ||
162 | decodeRatings = | ||
163 | D.list decodeRating | ||
164 | |||
165 | |||
166 | fetchListing : Int -> Cmd Msg | ||
167 | fetchListing id = | ||
168 | let | ||
169 | _ = | ||
170 | Debug.log "err" <| "fetching listing " ++ String.fromInt id | ||
171 | in | ||
172 | Http.get | ||
173 | { url = "http://127.0.0.1:7878/product/" ++ String.fromInt id | ||
174 | , expect = Http.expectJson ListingLoaded decodeProduct | ||
175 | } | ||
176 | |||
177 | |||
178 | fetchRatings : Int -> Cmd Msg | ||
179 | fetchRatings id = | ||
180 | let | ||
181 | _ = | ||
182 | Debug.log "err" <| "fetching ratings " ++ String.fromInt id | ||
183 | in | ||
184 | Http.get | ||
185 | { url = "http://127.0.0.1:7878/product/reviews/" ++ String.fromInt id | ||
186 | , expect = Http.expectJson RatingsLoaded decodeRatings | ||
187 | } | ||
188 | |||
189 | |||
190 | encodeRatingForm : Model -> Encode.Value | ||
191 | encodeRatingForm model = | ||
192 | Encode.object | ||
193 | [ ( "product_id", Encode.int model.listing.id ) | ||
194 | , ( "stars", Encode.int model.ratingStars ) | ||
195 | , ( "comment_text", Encode.string model.ratingText ) | ||
196 | ] | ||
197 | |||
198 | |||
199 | submitRating : Model -> Cmd Msg | ||
200 | submitRating model = | ||
201 | let | ||
202 | _ = | ||
203 | Debug.log "err" <| "submitting rating for" ++ String.fromInt model.listing.id | ||
204 | in | ||
205 | Http.riskyRequest | ||
206 | { method = "POST" | ||
207 | , headers = [] | ||
208 | , url = "http://127.0.0.1:7878/rating/add" | ||
209 | , body = model |> encodeRatingForm |> Http.jsonBody | ||
210 | , expect = Http.expectWhatever AddRatingSuccess | ||
211 | , timeout = Nothing | ||
212 | , tracker = Nothing | ||
213 | } | ||
214 | |||
215 | |||
216 | addToCart : Model -> Cmd Msg | ||
217 | addToCart model = | ||
218 | let | ||
219 | _ = | ||
220 | Debug.log "err" <| "adding to cart: " ++ String.fromInt model.listing.id | ||
221 | in | ||
222 | Http.riskyRequest | ||
223 | { method = "POST" | ||
224 | , headers = [] | ||
225 | , url = "http://127.0.0.1:7878/cart/add" | ||
226 | , body = Http.stringBody "applcation/json" <| String.fromInt <| model.listing.id | ||
227 | , expect = Http.expectWhatever AddToCartSuccess | ||
228 | , timeout = Nothing | ||
229 | , tracker = Nothing | ||
230 | } | ||
231 | |||
232 | |||
233 | viewStatus : Status -> String | ||
234 | viewStatus s = | ||
235 | case s of | ||
236 | Loading -> | ||
237 | "Loading" | ||
238 | |||
239 | Loaded -> | ||
240 | "Ready!" | ||
241 | |||
242 | NotLoaded -> | ||
243 | "Not loaded ..." | ||
244 | |||
245 | |||
246 | viewProduct : Product -> Html Msg | ||
247 | viewProduct p = | ||
248 | div [] | ||
249 | [ text p.name | ||
250 | , text <| Maybe.withDefault "" p.kind | ||
251 | , text <| Maybe.withDefault "" p.description | ||
252 | , text <| String.fromFloat p.price | ||
253 | ] | ||
254 | |||
255 | |||
256 | viewRating : Rating -> Html Msg | ||
257 | viewRating r = | ||
258 | div [] | ||
259 | [ text <| r.customerName ++ " posted on " | ||
260 | , text <| r.commentDate ++ " " | ||
261 | , text <| Maybe.withDefault "" r.commentText | ||
262 | , text <| " Stars: " ++ String.fromInt r.stars | ||
263 | ] | ||
264 | |||
265 | |||
266 | viewInput : String -> String -> String -> (String -> msg) -> Html msg | ||
267 | viewInput t p v toMsg = | ||
268 | input [ type_ t, placeholder p, value v, onInput toMsg ] [] | ||
269 | |||
270 | |||
271 | viewStars : Html Msg | ||
272 | viewStars = | ||
273 | ul [] | ||
274 | (List.map | ||
275 | (\i -> button [ onClick (AddRatingStars i) ] [ text <| String.fromInt i ]) | ||
276 | [ 0, 1, 2, 3, 4, 5 ] | ||
277 | ) | ||
278 | |||
279 | |||
280 | view : Model -> Html Msg | ||
281 | view model = | ||
282 | case model.pageStatus of | ||
283 | Loading -> | ||
284 | div [] [ text <| viewStatus Loading ] | ||
285 | |||
286 | _ -> | ||
287 | div [] | ||
288 | [ div [] [ viewProduct model.listing ] | ||
289 | , ul [] (List.map viewRating model.ratings) | ||
290 | , div [] [ text "Add Rating: " ] | ||
291 | , div [] | ||
292 | [ viewStars | ||
293 | , viewInput "text" "Enter Comment Text" model.ratingText AddRatingComment | ||
294 | , button [ onClick AddRatingPressed ] [ text "Submit Rating" ] | ||
295 | ] | ||
296 | , div [] | ||
297 | [ button [ onClick AddToCartPressed ] [ text "Add To Cart" ] | ||
298 | ] | ||
299 | , div [] | ||
300 | [ a [ href "/catalog" ] [ text "Back to catalog" ] | ||
301 | ] | ||
302 | ] | ||