aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--frontend/src/Cart.elm83
-rw-r--r--frontend/src/Catalog.elm2
-rw-r--r--frontend/src/Checkout.elm31
-rw-r--r--frontend/src/Main.elm62
-rw-r--r--frontend/src/Product.elm2
-rw-r--r--frontend/src/Profile.elm211
-rw-r--r--frontend/src/Styles.elm14
7 files changed, 361 insertions, 44 deletions
diff --git a/frontend/src/Cart.elm b/frontend/src/Cart.elm
index 44d5a0d..008d0bc 100644
--- a/frontend/src/Cart.elm
+++ b/frontend/src/Cart.elm
@@ -2,6 +2,7 @@ module Cart 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 (..)
@@ -9,8 +10,8 @@ import Html.Styled.Events exposing (..)
9import Http 10import Http
10import Json.Decode as D 11import Json.Decode as D
11import Json.Encode as Encode 12import Json.Encode as Encode
12import Url 13import Styles exposing (..)
13import Url.Parser as P exposing ((</>), Parser, int, oneOf, s, string) 14import Utils exposing (..)
14 15
15 16
16type alias Product = 17type alias Product =
@@ -181,15 +182,24 @@ calculateTotal model =
181 182
182viewCartItemListing : CartListing -> Html Msg 183viewCartItemListing : CartListing -> Html Msg
183viewCartItemListing listing = 184viewCartItemListing listing =
184 div [] 185 -- div []
185 [ text listing.productItem.name 186 -- [ text listing.productItem.name
186 , div [] [ text <| Maybe.withDefault "" listing.productItem.kind ] 187 -- , div [] [ text <| Maybe.withDefault "" listing.productItem.kind ]
187 , div [] [ text <| Maybe.withDefault "" listing.productItem.description ] 188 -- , div [] [ text <| Maybe.withDefault "" listing.productItem.description ]
188 , div [] [ text <| String.fromFloat listing.productItem.price ] 189 -- , div [] [ text <| String.fromFloat listing.productItem.price ]
189 , div [] [ text <| String.fromInt listing.quantity ] 190 -- , div [] [ text <| String.fromInt listing.quantity ]
190 , div [] [ button [ onClick (AddToCartPressed listing.productItem.id) ] [ text "Add" ] ] 191 -- , div [] [ button [ onClick (AddToCartPressed listing.productItem.id) ] [ text "Add" ] ]
191 , div [] [ button [ onClick (RemoveFromCart listing.productItem.id) ] [ text "Remove" ] ] 192 -- , div [] [ button [ onClick (RemoveFromCart listing.productItem.id) ] [ text "Remove" ] ]
192 , div [] [ a [ href ("/product/" ++ String.fromInt listing.productItem.id) ] [ text "View Product" ] ] 193 -- , div [] [ a [ href ("/product/" ++ String.fromInt listing.productItem.id) ] [ text "View Product" ] ]
194 -- ]
195 tr []
196 [ td [] [ a [ href ("/product/" ++ String.fromInt listing.productItem.id) ] [ text listing.productItem.name ] ]
197 , td [] [ text <| String.fromFloat listing.productItem.price ]
198 , td []
199 [ furbyButton [ onClick (RemoveFromCart listing.productItem.id) ] [ div [ style "font-family" "monospace" ] [ text "-" ] ]
200 , text <| String.fromInt listing.quantity
201 , furbyButton [ onClick (AddToCartPressed listing.productItem.id) ] [ div [ style "font-family" "monospace" ] [ text "+" ] ]
202 ]
193 ] 203 ]
194 204
195 205
@@ -200,16 +210,41 @@ view model =
200 div [] [ text <| viewStatus Loading ] 210 div [] [ text <| viewStatus Loading ]
201 211
202 _ -> 212 _ ->
203 div [] 213 let
204 [ let 214 cart =
205 cart = 215 List.map viewCartItemListing model.products
206 List.map viewCartItemListing model.products 216
207 in 217 headings =
208 if List.isEmpty cart then 218 [ "Product Name", "Price (₹)", "Quantity" ]
209 text "No items in cart" 219 |> List.map (th [] << List.singleton << text)
210 220 in
211 else 221 if List.isEmpty cart then
212 ul [] cart 222 text "No items in cart"
213 , calculateTotal model |> String.fromFloat |> text 223
214 , a [ href "/checkout" ] [ text "Checkout" ] 224 else
215 ] 225 div
226 [ css
227 [ margin auto
228 , marginTop (pct 5)
229 , Css.width (pct 40)
230 ]
231 ]
232 [ div [ css [ bigHeading, marginBottom (px 20) ] ] [ text "Cart" ]
233 , Html.Styled.table
234 [ css
235 [ Css.width (pct 100)
236 , maxWidth (px 650)
237 , textAlign right
238 ]
239 ]
240 (tr [] headings
241 :: cart
242 ++ [ tr [ style "padding-top" "20px" ]
243 [ td [ style "border-top" "1px solid black" ] []
244 , td [ style "border-top" "1px solid black" ] [ div [] [ text "Cart total: " ] ]
245 , td [ style "border-top" "1px solid black" ] [ calculateTotal model |> String.fromFloat |> text ]
246 ]
247 ]
248 )
249 , a [ href "/checkout" ] [ text "Checkout" ]
250 ]
diff --git a/frontend/src/Catalog.elm b/frontend/src/Catalog.elm
index 7e9bde7..d4cbf96 100644
--- a/frontend/src/Catalog.elm
+++ b/frontend/src/Catalog.elm
@@ -210,7 +210,7 @@ viewProduct p =
210 [ css 210 [ css
211 [ cardSecondaryText 211 [ cardSecondaryText
212 , paddingBottom (px 3) 212 , paddingBottom (px 3)
213 , fontVariant smallCaps 213 , textTransform uppercase
214 ] 214 ]
215 ] 215 ]
216 [ text <| Maybe.withDefault "" p.kind ] 216 [ text <| Maybe.withDefault "" p.kind ]
diff --git a/frontend/src/Checkout.elm b/frontend/src/Checkout.elm
index 4df20d8..216b90d 100644
--- a/frontend/src/Checkout.elm
+++ b/frontend/src/Checkout.elm
@@ -2,6 +2,7 @@ module Checkout 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 (..)
@@ -9,9 +10,8 @@ import Html.Styled.Events exposing (..)
9import Http 10import Http
10import Json.Decode as D 11import Json.Decode as D
11import Json.Encode as Encode 12import Json.Encode as Encode
13import Styles exposing (..)
12import Tuple exposing (..) 14import Tuple exposing (..)
13import Url
14import Url.Parser as P exposing ((</>), Parser, int, oneOf, s, string)
15import Utils exposing (..) 15import Utils exposing (..)
16 16
17 17
@@ -26,6 +26,7 @@ type Status
26 = Loading 26 = Loading
27 | Loaded 27 | Loaded
28 | NotLoaded 28 | NotLoaded
29 | CheckedOut
29 30
30 31
31type Msg 32type Msg
@@ -48,7 +49,7 @@ update msg model =
48 ( model, tryCheckout model.paymentMode ) 49 ( model, tryCheckout model.paymentMode )
49 50
50 CheckoutSuccessful _ -> 51 CheckoutSuccessful _ ->
51 ( model, Cmd.none ) 52 ( { model | pageStatus = CheckedOut }, Cmd.none )
52 53
53 AmountLoaded res -> 54 AmountLoaded res ->
54 case res of 55 case res of
@@ -107,6 +108,9 @@ viewStatus s =
107 NotLoaded -> 108 NotLoaded ->
108 "Not loaded ..." 109 "Not loaded ..."
109 110
111 CheckedOut ->
112 "Checked out!"
113
110 114
111view : Model -> Html Msg 115view : Model -> Html Msg
112view model = 116view model =
@@ -115,13 +119,22 @@ view model =
115 div [] [ text <| viewStatus Loading ] 119 div [] [ text <| viewStatus Loading ]
116 120
117 _ -> 121 _ ->
118 div [] 122 div
119 [ div [] [ text <| String.fromFloat <| model.cartTotal ] 123 [ css
120 , select [] 124 [ margin auto
121 [ option [ onInput PaymentModeSelected ] [ text "Cash" ] 125 , marginTop (pct 5)
122 , option [ onInput PaymentModeSelected ] [ text "Debit Card" ] 126 , Css.width (pct 40)
123 , option [ onInput PaymentModeSelected ] [ text "Credit Card" ]
124 ] 127 ]
128 ]
129 [ div [ css [ bigHeading, marginBottom (px 20) ] ] [ text "Checkout" ]
130 , div [ css [ cardSupportingText ] ] [ text "Your total is" ]
131 , div
132 [ css [ bigHeading, fontWeight bold, marginBottom (px 20) ] ]
133 [ text <| (++) "₹ " <| String.fromFloat <| model.cartTotal ]
134 , div [ css [ cardSupportingText ] ] [ text "Select a payment mode" ]
135 , div [] [ furbyRadio "Cash" (PaymentModeSelected "Cash") ]
136 , div [] [ furbyRadio "Debit Card" (PaymentModeSelected "Debit Card") ]
137 , div [] [ furbyRadio "Credit Card" (PaymentModeSelected "Credit Card") ]
125 , div [] [ a [ href "/cart" ] [ text "Cancel" ] ] 138 , div [] [ a [ href "/cart" ] [ text "Cancel" ] ]
126 , div [] [ button [ onClick CheckoutPressed ] [ text "Confirm and Pay" ] ] 139 , div [] [ button [ onClick CheckoutPressed ] [ text "Confirm and Pay" ] ]
127 ] 140 ]
diff --git a/frontend/src/Main.elm b/frontend/src/Main.elm
index c1489bf..09a851d 100644
--- a/frontend/src/Main.elm
+++ b/frontend/src/Main.elm
@@ -14,6 +14,7 @@ import Http
14import Json.Encode as Encode 14import Json.Encode as Encode
15import Login 15import Login
16import Product 16import Product
17import Profile
17import Signup 18import Signup
18import Styles exposing (..) 19import Styles exposing (..)
19import Url 20import Url
@@ -48,6 +49,7 @@ type Route
48 | CartPage 49 | CartPage
49 | ProductPage Int 50 | ProductPage Int
50 | CheckoutPage 51 | CheckoutPage
52 | ProfilePage
51 | NotFoundPage 53 | NotFoundPage
52 54
53 55
@@ -61,6 +63,7 @@ parseRoute =
61 , P.map SignupPage (P.s "signup") 63 , P.map SignupPage (P.s "signup")
62 , P.map CheckoutPage (P.s "checkout") 64 , P.map CheckoutPage (P.s "checkout")
63 , P.map ProductPage (P.s "product" </> P.int) 65 , P.map ProductPage (P.s "product" </> P.int)
66 , P.map ProfilePage (P.s "profile")
64 ] 67 ]
65 68
66 69
@@ -74,6 +77,7 @@ type alias Model =
74 , signupModel : Signup.Model 77 , signupModel : Signup.Model
75 , cartModel : Cart.Model 78 , cartModel : Cart.Model
76 , checkoutModel : Checkout.Model 79 , checkoutModel : Checkout.Model
80 , profileModel : Profile.Model
77 } 81 }
78 82
79 83
@@ -100,8 +104,11 @@ init flags url key =
100 104
101 checkout = 105 checkout =
102 Checkout.init 106 Checkout.init
107
108 profile =
109 Profile.init
103 in 110 in
104 ( Model key url start login catalog product signup cart checkout, Cmd.none ) 111 ( Model key url start login catalog product signup cart checkout profile, Cmd.none )
105 112
106 113
107 114
@@ -117,6 +124,7 @@ type Msg
117 | SignupMessage Signup.Msg 124 | SignupMessage Signup.Msg
118 | CartMessage Cart.Msg 125 | CartMessage Cart.Msg
119 | CheckoutMessage Checkout.Msg 126 | CheckoutMessage Checkout.Msg
127 | ProfileMessage Profile.Msg
120 | LogoutPressed 128 | LogoutPressed
121 | LogoutSuccess (Result Http.Error ()) 129 | LogoutSuccess (Result Http.Error ())
122 130
@@ -174,6 +182,13 @@ update msg model =
174 in 182 in
175 ( { model | location = CheckoutPage }, cmd ) 183 ( { model | location = CheckoutPage }, cmd )
176 184
185 Just ProfilePage ->
186 let
187 cmd =
188 Cmd.map ProfileMessage Profile.tryFetchProfile
189 in
190 ( { model | location = ProfilePage }, cmd )
191
177 Just p -> 192 Just p ->
178 ( { model | location = p }, Cmd.none ) 193 ( { model | location = p }, Cmd.none )
179 194
@@ -229,10 +244,28 @@ update msg model =
229 ( cmn, cmd ) = 244 ( cmn, cmd ) =
230 Checkout.update cm model.checkoutModel 245 Checkout.update cm model.checkoutModel
231 246
247 redir =
248 case cmn.pageStatus of
249 Checkout.CheckedOut ->
250 Nav.replaceUrl model.key "/profile"
251
252 _ ->
253 Cmd.none
254
232 _ = 255 _ =
233 Debug.log "err" "received checkout message ..." 256 Debug.log "err" "received checkout message ..."
234 in 257 in
235 ( { model | checkoutModel = cmn }, Cmd.map CheckoutMessage cmd ) 258 ( { model | checkoutModel = cmn }, Cmd.batch [ Cmd.map CheckoutMessage cmd, redir ] )
259
260 ProfileMessage pm ->
261 let
262 ( pmn, cmd ) =
263 Profile.update pm model.profileModel
264
265 _ =
266 Debug.log "err" "recieved profile message"
267 in
268 ( { model | profileModel = pmn }, Cmd.map ProfileMessage cmd )
236 269
237 ProductMessage pm -> 270 ProductMessage pm ->
238 let 271 let
@@ -359,6 +392,15 @@ view model =
359 |> pageWrap model 392 |> pageWrap model
360 } 393 }
361 394
395 ProfilePage ->
396 { title = "Profile"
397 , body =
398 model.profileModel
399 |> Profile.view
400 |> Html.Styled.map ProfileMessage
401 |> pageWrap model
402 }
403
362 ProductPage item -> 404 ProductPage item ->
363 { title = "Product " ++ String.fromInt item 405 { title = "Product " ++ String.fromInt item
364 , body = 406 , body =
@@ -379,7 +421,7 @@ viewHeader model =
379 in 421 in
380 div 422 div
381 [ css 423 [ css
382 [ padding (px 40) 424 [ padding (px 30)
383 , paddingTop (px 3) 425 , paddingTop (px 3)
384 , paddingBottom (px 3) 426 , paddingBottom (px 3)
385 , textAlign left 427 , textAlign left
@@ -393,17 +435,19 @@ viewHeader model =
393 ] 435 ]
394 ) 436 )
395 links 437 links
396 ++ [ if model.loginModel.loginStatus /= Login.LoggedIn then 438 ++ (if model.loginModel.loginStatus /= Login.LoggedIn then
397 li [ css [ display inline ] ] [ headerLink [ href "/login" ] [ text "Login" ] ] 439 [ furbyButton [] [ headerLink [ href "/login" ] [ text "Login" ] ] ]
398 440
399 else 441 else
400 furbyButton [ onClick LogoutPressed ] [ text "Logout" ] 442 [ headerLink [ href "/profile" ] [ text "Profile" ]
401 ] 443 , furbyButton [ onClick LogoutPressed ] [ text "Logout" ]
444 ]
445 )
402 |> ul 446 |> ul
403 [ css 447 [ css
404 [ listStyle Css.none 448 [ listStyle Css.none
405 , padding (px 0) 449 , padding (px 0)
406 , margin (px 24) 450 , margin (px 12)
407 ] 451 ]
408 ] 452 ]
409 ] 453 ]
diff --git a/frontend/src/Product.elm b/frontend/src/Product.elm
index 79256cc..0975c8d 100644
--- a/frontend/src/Product.elm
+++ b/frontend/src/Product.elm
@@ -287,7 +287,7 @@ viewProduct p =
287 [ css 287 [ css
288 [ cardSecondaryText 288 [ cardSecondaryText
289 , paddingBottom (px 3) 289 , paddingBottom (px 3)
290 , fontVariant smallCaps 290 , textTransform uppercase
291 ] 291 ]
292 ] 292 ]
293 [ text <| Maybe.withDefault "" p.kind ] 293 [ text <| Maybe.withDefault "" p.kind ]
diff --git a/frontend/src/Profile.elm b/frontend/src/Profile.elm
new file mode 100644
index 0000000..6575d41
--- /dev/null
+++ b/frontend/src/Profile.elm
@@ -0,0 +1,211 @@
1module Profile exposing (..)
2
3import Browser
4import Browser.Navigation as Nav
5import Css exposing (..)
6import Html
7import Html.Styled exposing (..)
8import Html.Styled.Attributes exposing (..)
9import Html.Styled.Events exposing (..)
10import Http
11import Icons exposing (..)
12import Json.Decode as D
13import Json.Encode as Encode
14import Styles exposing (..)
15import Utils exposing (..)
16
17
18emptyProfile =
19 UserProfile "" "" "" Nothing 0 []
20
21
22type alias Transaction =
23 { amount : Float
24 , transactionId : Int
25 , orderDate : String
26 , paymentMode : String
27 }
28
29
30type alias UserProfile =
31 { username : String
32 , phoneNumber : String
33 , emailId : String
34 , address : Maybe String
35 , ratingsGiven : Int
36 , transactions : List Transaction
37 }
38
39
40type alias Model =
41 { profile : UserProfile
42 , status : Status
43 }
44
45
46type Status
47 = Loading
48 | Loaded
49 | NotLoaded
50
51
52type Msg
53 = ProfileLoaded (Result Http.Error UserProfile)
54 | FetchProfile
55
56
57init : Model
58init =
59 Model emptyProfile NotLoaded
60
61
62update : Msg -> Model -> ( Model, Cmd Msg )
63update msg model =
64 case msg of
65 ProfileLoaded res ->
66 case res of
67 Ok p ->
68 ( { model | profile = p }, Cmd.none )
69
70 Err _ ->
71 ( { model | status = NotLoaded }, Cmd.none )
72
73 FetchProfile ->
74 ( { model | status = Loading }, tryFetchProfile )
75
76
77decodeProfile : D.Decoder UserProfile
78decodeProfile =
79 D.map6 UserProfile
80 (D.field "username" D.string)
81 (D.field "phone_number" D.string)
82 (D.field "email_id" D.string)
83 (D.field "address" (D.nullable D.string))
84 (D.field "ratings_given" D.int)
85 (D.field "transactions" (D.list decodeTransaction))
86
87
88decodeTransaction : D.Decoder Transaction
89decodeTransaction =
90 D.map4 Transaction
91 (D.field "amount" D.float)
92 (D.field "id" D.int)
93 (D.field "order_date" D.string)
94 (D.field "payment_type" D.string)
95
96
97tryFetchProfile : Cmd Msg
98tryFetchProfile =
99 let
100 _ =
101 Debug.log "err" <| "fetching user profile"
102 in
103 Http.riskyRequest
104 { method = "GET"
105 , headers = []
106 , url = "http://127.0.0.1:7878/user/profile"
107 , body = Http.emptyBody
108 , expect = Http.expectJson ProfileLoaded decodeProfile
109 , timeout = Nothing
110 , tracker = Nothing
111 }
112
113
114viewStatus : Status -> String
115viewStatus s =
116 case s of
117 Loading ->
118 "Loading"
119
120 Loaded ->
121 "Ready!"
122
123 NotLoaded ->
124 "Not loaded ..."
125
126
127viewTransactions : List Transaction -> Html Msg
128viewTransactions ts =
129 let
130 headings =
131 [ "Order ID", "Date", "Amount (₹)", "Payment Mode" ]
132 |> List.map (th [] << List.singleton << text)
133
134 transactionRow t =
135 List.map (td [] << List.singleton)
136 [ text <| String.fromInt t.transactionId
137 , text t.orderDate
138 , text <| String.fromFloat t.amount
139 , text t.paymentMode
140 ]
141 in
142 div []
143 [ div
144 [ css [ bigHeading, marginTop (px 20), marginBottom (px 12) ] ]
145 [ text "Transactions" ]
146 , Html.Styled.table
147 [ css
148 [ Css.width (pct 100)
149 , maxWidth (px 650)
150 ]
151 ]
152 ([ tr [ style "text-align" "right" ] headings
153 ]
154 ++ List.map (tr [ style "text-align" "right" ] << transactionRow) ts
155 )
156 ]
157
158
159profileField : String -> String -> Html Msg
160profileField fieldName entry =
161 div []
162 [ div
163 [ css
164 [ cardSecondaryText
165 , textTransform uppercase
166 , paddingBottom (px 3)
167 ]
168 ]
169 [ text fieldName ]
170 , div
171 [ css
172 [ cardPrimaryText
173 , paddingBottom (px 12)
174 ]
175 ]
176 [ text entry ]
177 ]
178
179
180viewProfile : UserProfile -> Html Msg
181viewProfile u =
182 div
183 []
184 [ div
185 [ css [ bigHeading, marginTop (px 20), marginBottom (px 12) ] ]
186 [ text "Profile" ]
187 , profileField "name" u.username
188 , profileField "email" u.emailId
189 , profileField "contact number" u.phoneNumber
190 , profileField "address" <| Maybe.withDefault "No address provided" u.address
191 , profileField "Total Reviews" <| String.fromInt u.ratingsGiven
192 , hr [] []
193 , viewTransactions u.transactions
194 ]
195
196
197view : Model -> Html Msg
198view model =
199 case model.status of
200 Loading ->
201 div [] [ text <| viewStatus Loading ]
202
203 _ ->
204 div
205 [ css
206 [ margin auto
207 , marginTop (pct 5)
208 , Css.width (pct 40)
209 ]
210 ]
211 [ viewProfile model.profile ]
diff --git a/frontend/src/Styles.elm b/frontend/src/Styles.elm
index fbef6e1..4222024 100644
--- a/frontend/src/Styles.elm
+++ b/frontend/src/Styles.elm
@@ -71,6 +71,20 @@ furbyButton =
71 ] 71 ]
72 72
73 73
74furbyRadio : String -> msg -> Html msg
75furbyRadio value msg =
76 label
77 []
78 [ input
79 [ type_ "radio"
80 , onClick msg
81 , name "radio"
82 ]
83 []
84 , text value
85 ]
86
87
74furbySelect : List (Attribute msg) -> List (Html msg) -> Html msg 88furbySelect : List (Attribute msg) -> List (Html msg) -> Html msg
75furbySelect = 89furbySelect =
76 styled select 90 styled select