aboutsummaryrefslogtreecommitdiff
path: root/frontend/src
diff options
context:
space:
mode:
Diffstat (limited to 'frontend/src')
-rw-r--r--frontend/src/Catalog.elm100
-rw-r--r--frontend/src/Icons.elm8
-rw-r--r--frontend/src/Main.elm1
-rw-r--r--frontend/src/Product.elm144
-rw-r--r--frontend/src/Styles.elm21
5 files changed, 240 insertions, 34 deletions
diff --git a/frontend/src/Catalog.elm b/frontend/src/Catalog.elm
index 5b233f2..7e9bde7 100644
--- a/frontend/src/Catalog.elm
+++ b/frontend/src/Catalog.elm
@@ -27,6 +27,8 @@ type alias Product =
27 , price : Float 27 , price : Float
28 , description : Maybe String 28 , description : Maybe String
29 , averageRating : Maybe Float 29 , averageRating : Maybe Float
30 , src : String
31 , iosSrc : String
30 } 32 }
31 33
32 34
@@ -129,13 +131,15 @@ update msg model =
129 131
130decodeProduct : D.Decoder Product 132decodeProduct : D.Decoder Product
131decodeProduct = 133decodeProduct =
132 D.map6 Product 134 D.map8 Product
133 (D.field "id" D.int) 135 (D.field "id" D.int)
134 (D.field "name" D.string) 136 (D.field "name" D.string)
135 (D.field "kind" (D.nullable D.string)) 137 (D.field "kind" (D.nullable D.string))
136 (D.field "price" D.float) 138 (D.field "price" D.float)
137 (D.field "description" (D.nullable D.string)) 139 (D.field "description" (D.nullable D.string))
138 (D.field "average_rating" (D.nullable D.float)) 140 (D.field "average_rating" (D.nullable D.float))
141 (D.field "src" D.string)
142 (D.field "ios_src" D.string)
139 143
140 144
141decodeResponse : D.Decoder (List Product) 145decodeResponse : D.Decoder (List Product)
@@ -170,18 +174,83 @@ viewStatus s =
170 174
171viewProduct : Product -> Html Msg 175viewProduct : Product -> Html Msg
172viewProduct p = 176viewProduct p =
173 div [] 177 div
174 [ div [] [ text p.name ] 178 [ css
175 , div [] [ text <| Maybe.withDefault "" p.kind ] 179 [ marginBottom (px 20)
176 , div [] [ text <| Maybe.withDefault "" p.description ] 180 , border3 (px 1) solid theme.primary
177 , div [] [ text <| String.fromFloat p.price ] 181 , borderRadius (px 4)
178 , case p.averageRating of 182 , padding (px 20)
179 Just v -> 183 , Css.width (pct 100)
180 text <| "Avg Rating: " ++ String.fromFloat v 184 , maxWidth (px 650)
181 185 ]
182 Nothing -> 186 ]
183 text "No Ratings" 187 [ div
184 , div [] [ a [ href ("/product/" ++ String.fromInt p.id) ] [ text "View Product" ] ] 188 [ css
189 [ float left
190 , Css.width (pct 50)
191 ]
192 ]
193 [ modelViewer
194 [ cameraControls
195 , autoRotate
196 , arSrc p.src
197 , arIosSrc p.iosSrc
198 , loading "eager"
199 , arModes "webxr"
200 ]
201 []
202 ]
203 , div
204 [ css
205 [ float left
206 , Css.width (pct 50)
207 ]
208 ]
209 [ div
210 [ css
211 [ cardSecondaryText
212 , paddingBottom (px 3)
213 , fontVariant smallCaps
214 ]
215 ]
216 [ text <| Maybe.withDefault "" p.kind ]
217 , div
218 [ css
219 [ cardPrimaryText
220 , paddingBottom (px 3)
221 ]
222 ]
223 [ a [ href ("/product/" ++ String.fromInt p.id) ] [ text p.name ] ]
224 , div
225 [ css
226 [ cardSecondaryText
227 , paddingBottom (px 12)
228 ]
229 ]
230 [ case p.averageRating of
231 Just v ->
232 text <| "Avg Rating: " ++ String.fromFloat v
233
234 Nothing ->
235 text "No Ratings"
236 ]
237 , div
238 [ css
239 [ cardSupportingText
240 , paddingBottom (px 6)
241 ]
242 ]
243 [ text <| Maybe.withDefault "No description provided" p.description ]
244 , div
245 [ css
246 [ fontWeight bold
247 , fontSize (px 14)
248 , money
249 ]
250 ]
251 [ text <| String.fromFloat p.price ]
252 ]
253 , div [ style "clear" "both" ] []
185 ] 254 ]
186 255
187 256
@@ -260,7 +329,10 @@ view model =
260 ] 329 ]
261 [ div [ css [ bigHeading ] ] [ text "Products" ] 330 [ div [ css [ bigHeading ] ] [ text "Products" ]
262 , ul 331 , ul
263 [ css [ padding (px 0) ] 332 [ css
333 [ padding (px 0)
334 , listStyle Css.none
335 ]
264 ] 336 ]
265 (filterProducts model |> List.map viewProduct) 337 (filterProducts model |> List.map viewProduct)
266 ] 338 ]
diff --git a/frontend/src/Icons.elm b/frontend/src/Icons.elm
index d3b862f..96fa6ff 100644
--- a/frontend/src/Icons.elm
+++ b/frontend/src/Icons.elm
@@ -1,6 +1,6 @@
1module Icons exposing (..) 1module Icons exposing (..)
2 2
3import FeatherIcons exposing (toHtml) 3import FeatherIcons exposing (toHtml, withSize)
4import Html 4import Html
5import Html.Styled exposing (..) 5import Html.Styled exposing (..)
6import Html.Styled.Attributes exposing (..) 6import Html.Styled.Attributes exposing (..)
@@ -8,8 +8,12 @@ import Html.Styled.Events exposing (..)
8 8
9 9
10convert = 10convert =
11 Html.Styled.fromUnstyled << toHtml [] 11 Html.Styled.fromUnstyled << toHtml [] << withSize 14
12 12
13 13
14loginIcon = 14loginIcon =
15 convert FeatherIcons.logIn 15 convert FeatherIcons.logIn
16
17
18starIcon =
19 convert FeatherIcons.star
diff --git a/frontend/src/Main.elm b/frontend/src/Main.elm
index ea80921..c1489bf 100644
--- a/frontend/src/Main.elm
+++ b/frontend/src/Main.elm
@@ -403,6 +403,7 @@ viewHeader model =
403 [ css 403 [ css
404 [ listStyle Css.none 404 [ listStyle Css.none
405 , padding (px 0) 405 , padding (px 0)
406 , margin (px 24)
406 ] 407 ]
407 ] 408 ]
408 ] 409 ]
diff --git a/frontend/src/Product.elm b/frontend/src/Product.elm
index b97a847..79256cc 100644
--- a/frontend/src/Product.elm
+++ b/frontend/src/Product.elm
@@ -2,13 +2,16 @@ module Product exposing (..)
2 2
3import Browser 3import Browser
4import Browser.Navigation as Nav 4import Browser.Navigation as Nav
5import Css exposing (..)
5import Html 6import Html
6import Html.Styled exposing (..) 7import Html.Styled exposing (..)
7import Html.Styled.Attributes exposing (..) 8import Html.Styled.Attributes exposing (..)
8import Html.Styled.Events exposing (..) 9import Html.Styled.Events exposing (..)
9import Http 10import Http
11import Icons exposing (..)
10import Json.Decode as D 12import Json.Decode as D
11import Json.Encode as Encode 13import Json.Encode as Encode
14import Styles exposing (..)
12import Url 15import Url
13import Url.Parser as P exposing ((</>), Parser, int, oneOf, s, string) 16import Url.Parser as P exposing ((</>), Parser, int, oneOf, s, string)
14import Utils exposing (..) 17import Utils exposing (..)
@@ -77,7 +80,7 @@ type Msg
77 80
78init : Model 81init : Model
79init = 82init =
80 Model NotLoaded emptyProduct [] 0 "" NotSubmitted 83 Model NotLoaded emptyProduct [] 5 "" NotSubmitted
81 84
82 85
83update : Msg -> Model -> ( Model, Cmd Msg ) 86update : Msg -> Model -> ( Model, Cmd Msg )
@@ -251,12 +254,19 @@ viewStatus s =
251 254
252viewProduct : Product -> Html Msg 255viewProduct : Product -> Html Msg
253viewProduct p = 256viewProduct p =
254 div [] 257 div
255 [ div [] [ text p.name ] 258 [ css
256 , div [] [ text <| Maybe.withDefault "" p.kind ] 259 [ marginBottom (px 20)
257 , div [] [ text <| Maybe.withDefault "" p.description ] 260 , padding (px 20)
258 , div [] [ text <| String.fromFloat p.price ] 261 , Css.width (pct 100)
259 , div [] 262 ]
263 ]
264 [ div
265 [ css
266 [ float left
267 , Css.width (pct 50)
268 ]
269 ]
260 [ modelViewer 270 [ modelViewer
261 [ cameraControls 271 [ cameraControls
262 , autoRotate 272 , autoRotate
@@ -267,16 +277,96 @@ viewProduct p =
267 ] 277 ]
268 [] 278 []
269 ] 279 ]
280 , div
281 [ css
282 [ float left
283 , Css.width (pct 50)
284 ]
285 ]
286 [ div
287 [ css
288 [ cardSecondaryText
289 , paddingBottom (px 3)
290 , fontVariant smallCaps
291 ]
292 ]
293 [ text <| Maybe.withDefault "" p.kind ]
294 , div
295 [ css
296 [ cardPrimaryText
297 , paddingBottom (px 12)
298 ]
299 ]
300 [ text p.name ]
301 , div
302 [ css
303 [ cardSupportingText
304 , paddingBottom (px 6)
305 ]
306 ]
307 [ text <| Maybe.withDefault "No description provided" p.description ]
308 , div
309 [ css
310 [ fontWeight bold
311 , fontSize (px 14)
312 , money
313 ]
314 ]
315 [ text <| String.fromFloat p.price
316 , div []
317 [ furbyButton [ onClick AddToCartPressed ] [ text "Add To Cart" ]
318 ]
319 ]
320 ]
321 , div [ style "clear" "both" ] []
270 ] 322 ]
271 323
272 324
325viewStarRating : Int -> Html Msg
326viewStarRating i =
327 div []
328 (List.repeat i starIcon)
329
330
273viewRating : Rating -> Html Msg 331viewRating : Rating -> Html Msg
274viewRating r = 332viewRating r =
275 div [] 333 -- div []
276 [ text <| r.customerName ++ " posted on " 334 -- [ text <| r.customerName ++ " posted on "
277 , text <| r.commentDate ++ " " 335 -- , text <| r.commentDate ++ " "
278 , text <| Maybe.withDefault "" r.commentText 336 -- , text <| Maybe.withDefault "" r.commentText
279 , text <| " Stars: " ++ String.fromInt r.stars 337 -- , text <| " Stars: " ++ String.fromInt r.stars
338 -- ]
339 div
340 [ css
341 [ border3 (px 1) solid theme.primary
342 , borderRadius (px 4)
343 , marginBottom (px 20)
344 , padding (px 20)
345 ]
346 ]
347 [ div
348 [ css
349 [ fontSize (px 16)
350 , fontWeight bold
351 , paddingBottom (px 3)
352 ]
353 ]
354 [ text r.customerName ]
355 , viewStarRating r.stars
356 , div
357 [ css
358 [ cardSecondaryText
359 , paddingBottom (px 12)
360 ]
361 ]
362 [ text <| "Reviewed on " ++ r.commentDate ]
363 , if r.commentText /= Nothing then
364 div
365 [ css [ cardSupportingText ] ]
366 [ text <| Maybe.withDefault "" <| r.commentText ]
367
368 else
369 text ""
280 ] 370 ]
281 371
282 372
@@ -290,7 +380,7 @@ viewStars =
290 ul [] 380 ul []
291 (List.map 381 (List.map
292 (\i -> button [ onClick (AddRatingStars i) ] [ text <| String.fromInt i ]) 382 (\i -> button [ onClick (AddRatingStars i) ] [ text <| String.fromInt i ])
293 [ 0, 1, 2, 3, 4, 5 ] 383 [ 1, 2, 3, 4, 5 ]
294 ) 384 )
295 385
296 386
@@ -301,9 +391,30 @@ view model =
301 div [] [ text <| viewStatus Loading ] 391 div [] [ text <| viewStatus Loading ]
302 392
303 _ -> 393 _ ->
304 div [] 394 div
395 [ css
396 [ Css.width (pct 60)
397 , margin auto
398 ]
399 ]
305 [ div [] [ viewProduct model.listing ] 400 [ div [] [ viewProduct model.listing ]
306 , ul [] (List.map viewRating model.ratings) 401 , div
402 [ css
403 [ cardPrimaryText
404 ]
405 ]
406 [ text "User Reviews" ]
407 , if model.ratings == [] then
408 text "Be the first to add a review."
409
410 else
411 ul
412 [ css
413 [ padding (px 0)
414 , listStyle Css.none
415 ]
416 ]
417 (List.map viewRating model.ratings)
307 , div [] [ text "Add Rating: " ] 418 , div [] [ text "Add Rating: " ]
308 , div [] 419 , div []
309 [ viewStars 420 [ viewStars
@@ -311,9 +422,6 @@ view model =
311 , button [ onClick AddRatingPressed ] [ text "Submit Rating" ] 422 , button [ onClick AddRatingPressed ] [ text "Submit Rating" ]
312 ] 423 ]
313 , div [] 424 , div []
314 [ button [ onClick AddToCartPressed ] [ text "Add To Cart" ]
315 ]
316 , div []
317 [ a [ href "/catalog" ] [ text "Back to catalog" ] 425 [ a [ href "/catalog" ] [ text "Back to catalog" ]
318 ] 426 ]
319 ] 427 ]
diff --git a/frontend/src/Styles.elm b/frontend/src/Styles.elm
index 36f2a81..fbef6e1 100644
--- a/frontend/src/Styles.elm
+++ b/frontend/src/Styles.elm
@@ -60,6 +60,7 @@ furbyButton =
60 , color theme.fg 60 , color theme.fg
61 , Css.height (px 40) 61 , Css.height (px 40)
62 , border (px 0) 62 , border (px 0)
63 , borderRadius (px 2)
63 , padding2 (px 6) (px 12) 64 , padding2 (px 6) (px 12)
64 , backgroundColor theme.primary 65 , backgroundColor theme.primary
65 , hover 66 , hover
@@ -102,3 +103,23 @@ loginInputField =
102bigHeading : Style 103bigHeading : Style
103bigHeading = 104bigHeading =
104 fontSize (px 24) 105 fontSize (px 24)
106
107
108
109-- card styles
110
111
112cardPrimaryText =
113 fontSize (px 18)
114
115
116cardSecondaryText =
117 Css.batch [ color theme.fgLight, fontSize (px 12) ]
118
119
120cardSupportingText =
121 fontSize (px 16)
122
123
124money =
125 before [ Css.property "content" "\"₹ \"" ]