diff options
Diffstat (limited to 'frontend/src/Main.elm')
-rw-r--r-- | frontend/src/Main.elm | 339 |
1 files changed, 339 insertions, 0 deletions
diff --git a/frontend/src/Main.elm b/frontend/src/Main.elm new file mode 100644 index 0000000..bf1583c --- /dev/null +++ b/frontend/src/Main.elm | |||
@@ -0,0 +1,339 @@ | |||
1 | module Main exposing (Model, Msg(..), init, main, subscriptions, update, view, viewLink) | ||
2 | |||
3 | import Browser | ||
4 | import Browser.Navigation as Nav | ||
5 | import Cart | ||
6 | import Catalog | ||
7 | import Html exposing (..) | ||
8 | import Html.Attributes exposing (..) | ||
9 | import Html.Events exposing (..) | ||
10 | import Http | ||
11 | import Json.Encode as Encode | ||
12 | import Login | ||
13 | import Product | ||
14 | import Signup | ||
15 | import Url | ||
16 | import Url.Parser as P exposing ((</>), Parser, int, oneOf, s, string) | ||
17 | |||
18 | |||
19 | |||
20 | -- MAIN | ||
21 | |||
22 | |||
23 | main : Program () Model Msg | ||
24 | main = | ||
25 | Browser.application | ||
26 | { init = init | ||
27 | , view = view | ||
28 | , update = update | ||
29 | , subscriptions = subscriptions | ||
30 | , onUrlChange = UrlChanged | ||
31 | , onUrlRequest = LinkClicked | ||
32 | } | ||
33 | |||
34 | |||
35 | |||
36 | -- MODEL | ||
37 | |||
38 | |||
39 | type Route | ||
40 | = LoginPage | ||
41 | | SignupPage | ||
42 | | HomePage | ||
43 | | CatalogPage | ||
44 | | CartPage | ||
45 | | ProductPage Int | ||
46 | | NotFoundPage | ||
47 | |||
48 | |||
49 | parseRoute : Parser (Route -> a) a | ||
50 | parseRoute = | ||
51 | oneOf | ||
52 | [ P.map LoginPage (P.s "login") | ||
53 | , P.map HomePage P.top | ||
54 | , P.map CatalogPage (P.s "catalog") | ||
55 | , P.map CartPage (P.s "cart") | ||
56 | , P.map SignupPage (P.s "signup") | ||
57 | , P.map ProductPage (P.s "product" </> P.int) | ||
58 | |||
59 | --, P.map ProductPage (P.s "product" </> int) | ||
60 | ] | ||
61 | |||
62 | |||
63 | type alias Model = | ||
64 | { key : Nav.Key | ||
65 | , url : Url.Url | ||
66 | , location : Route | ||
67 | , loginModel : Login.Model | ||
68 | , catalogModel : Catalog.Model | ||
69 | , productModel : Product.Model | ||
70 | , signupModel : Signup.Model | ||
71 | , cartModel : Cart.Model | ||
72 | } | ||
73 | |||
74 | |||
75 | init : () -> Url.Url -> Nav.Key -> ( Model, Cmd Msg ) | ||
76 | init flags url key = | ||
77 | let | ||
78 | start = | ||
79 | HomePage | ||
80 | |||
81 | login = | ||
82 | Login.init | ||
83 | |||
84 | catalog = | ||
85 | Catalog.init | ||
86 | |||
87 | product = | ||
88 | Product.init | ||
89 | |||
90 | signup = | ||
91 | Signup.init | ||
92 | |||
93 | cart = | ||
94 | Cart.init | ||
95 | in | ||
96 | ( Model key url start login catalog product signup cart, Cmd.none ) | ||
97 | |||
98 | |||
99 | |||
100 | -- UPDATE | ||
101 | |||
102 | |||
103 | type Msg | ||
104 | = LinkClicked Browser.UrlRequest | ||
105 | | UrlChanged Url.Url | ||
106 | | LoginMessage Login.Msg | ||
107 | | CatalogMessage Catalog.Msg | ||
108 | | ProductMessage Product.Msg | ||
109 | | SignupMessage Signup.Msg | ||
110 | | CartMessage Cart.Msg | ||
111 | | LogoutPressed | ||
112 | | LogoutSuccess (Result Http.Error ()) | ||
113 | |||
114 | |||
115 | update : Msg -> Model -> ( Model, Cmd Msg ) | ||
116 | update msg model = | ||
117 | case msg of | ||
118 | LinkClicked urlRequest -> | ||
119 | case urlRequest of | ||
120 | Browser.Internal url -> | ||
121 | ( model, Nav.pushUrl model.key (Url.toString url) ) | ||
122 | |||
123 | Browser.External href -> | ||
124 | ( model, Nav.load href ) | ||
125 | |||
126 | LogoutPressed -> | ||
127 | ( model, tryLogout ) | ||
128 | |||
129 | LogoutSuccess _ -> | ||
130 | ( model, Nav.replaceUrl model.key "/login" ) | ||
131 | |||
132 | UrlChanged url -> | ||
133 | let | ||
134 | parsedUrl = | ||
135 | P.parse parseRoute url | ||
136 | in | ||
137 | case parsedUrl of | ||
138 | Just CatalogPage -> | ||
139 | ( { model | location = CatalogPage }, Cmd.map CatalogMessage Catalog.fetchProducts ) | ||
140 | |||
141 | Just (ProductPage id) -> | ||
142 | let | ||
143 | cmds = | ||
144 | List.map (Cmd.map ProductMessage) | ||
145 | [ Product.fetchListing id | ||
146 | , Product.fetchRatings id | ||
147 | ] | ||
148 | in | ||
149 | ( { model | location = ProductPage id }, Cmd.batch cmds ) | ||
150 | |||
151 | Just CartPage -> | ||
152 | let | ||
153 | cmd = | ||
154 | Cmd.map CartMessage Cart.fetchCartItems | ||
155 | in | ||
156 | ( { model | location = CartPage }, cmd ) | ||
157 | |||
158 | Just p -> | ||
159 | ( { model | location = p }, Cmd.none ) | ||
160 | |||
161 | Nothing -> | ||
162 | ( { model | location = NotFoundPage }, Cmd.none ) | ||
163 | |||
164 | LoginMessage lm -> | ||
165 | let | ||
166 | ( lmn, cmd ) = | ||
167 | Login.update lm model.loginModel | ||
168 | |||
169 | redir = | ||
170 | case lmn.loginStatus of | ||
171 | Login.LoggedIn -> | ||
172 | Nav.replaceUrl model.key "/catalog" | ||
173 | |||
174 | _ -> | ||
175 | Cmd.none | ||
176 | in | ||
177 | ( { model | loginModel = lmn }, Cmd.batch [ Cmd.map LoginMessage cmd, redir ] ) | ||
178 | |||
179 | SignupMessage sm -> | ||
180 | let | ||
181 | ( smn, cmd ) = | ||
182 | Signup.update sm model.signupModel | ||
183 | |||
184 | redir = | ||
185 | case smn.status of | ||
186 | Signup.CreatedSuccessfully -> | ||
187 | Nav.replaceUrl model.key "/login" | ||
188 | |||
189 | _ -> | ||
190 | Cmd.none | ||
191 | in | ||
192 | ( { model | signupModel = smn }, Cmd.batch [ Cmd.map SignupMessage cmd, redir ] ) | ||
193 | |||
194 | CatalogMessage cm -> | ||
195 | let | ||
196 | ( cmn, cmd ) = | ||
197 | Catalog.update cm model.catalogModel | ||
198 | in | ||
199 | ( { model | catalogModel = cmn }, Cmd.map CatalogMessage cmd ) | ||
200 | |||
201 | CartMessage cm -> | ||
202 | let | ||
203 | ( cmn, cmd ) = | ||
204 | Cart.update cm model.cartModel | ||
205 | in | ||
206 | ( { model | cartModel = cmn }, Cmd.map CartMessage cmd ) | ||
207 | |||
208 | ProductMessage pm -> | ||
209 | let | ||
210 | ( pmn, cmd ) = | ||
211 | Product.update pm model.productModel | ||
212 | |||
213 | redir = | ||
214 | case pm of | ||
215 | Product.AddToCartSuccess _ -> | ||
216 | Nav.replaceUrl model.key "/cart" | ||
217 | |||
218 | _ -> | ||
219 | Cmd.none | ||
220 | in | ||
221 | ( { model | productModel = pmn }, Cmd.batch [ Cmd.map ProductMessage cmd, redir ] ) | ||
222 | |||
223 | |||
224 | tryLogout : Cmd Msg | ||
225 | tryLogout = | ||
226 | Http.riskyRequest | ||
227 | { method = "POST" | ||
228 | , headers = [] | ||
229 | , url = "http://127.0.0.1:7878/user/logout" | ||
230 | , body = Http.emptyBody | ||
231 | , expect = Http.expectWhatever LogoutSuccess | ||
232 | , timeout = Nothing | ||
233 | , tracker = Nothing | ||
234 | } | ||
235 | |||
236 | |||
237 | |||
238 | -- SUBSCRIPTIONS | ||
239 | |||
240 | |||
241 | subscriptions : Model -> Sub Msg | ||
242 | subscriptions _ = | ||
243 | Sub.none | ||
244 | |||
245 | |||
246 | |||
247 | -- VIEW | ||
248 | |||
249 | |||
250 | view : Model -> Browser.Document Msg | ||
251 | view model = | ||
252 | case model.location of | ||
253 | LoginPage -> | ||
254 | { title = "Login" | ||
255 | , body = [ Html.map LoginMessage (Login.view model.loginModel) ] | ||
256 | } | ||
257 | |||
258 | SignupPage -> | ||
259 | { title = "Signup" | ||
260 | , body = [ Html.map SignupMessage (Signup.view model.signupModel) ] | ||
261 | } | ||
262 | |||
263 | HomePage -> | ||
264 | { title = "URL Interceptor" | ||
265 | , body = | ||
266 | [ text "The current URL is: " | ||
267 | , b [] [ text (Url.toString model.url) ] | ||
268 | , ul [] | ||
269 | [ viewLink "/login" | ||
270 | , viewLink "/catalog" | ||
271 | , viewLink "/cart" | ||
272 | , viewLink "/signup" | ||
273 | ] | ||
274 | ] | ||
275 | } | ||
276 | |||
277 | NotFoundPage -> | ||
278 | { title = "404 - Not Found" | ||
279 | , body = | ||
280 | [ text "404 - Not Found" | ||
281 | , a [ href "/" ] [ text "Go back >" ] | ||
282 | ] | ||
283 | } | ||
284 | |||
285 | CatalogPage -> | ||
286 | { title = "Catalog" | ||
287 | , body = pageWrap model (Html.map CatalogMessage (Catalog.view model.catalogModel)) | ||
288 | } | ||
289 | |||
290 | CartPage -> | ||
291 | { title = "Cart" | ||
292 | , body = pageWrap model (Html.map CartMessage (Cart.view model.cartModel)) | ||
293 | } | ||
294 | |||
295 | ProductPage item -> | ||
296 | { title = "Product " ++ String.fromInt item | ||
297 | , body = pageWrap model (Html.map ProductMessage (Product.view model.productModel)) | ||
298 | } | ||
299 | |||
300 | |||
301 | viewHeader : Model -> Html Msg | ||
302 | viewHeader model = | ||
303 | let | ||
304 | links = | ||
305 | [ ( "Home", "/" ) | ||
306 | , ( "Catalog", "/catalog" ) | ||
307 | , ( "Cart", "/cart" ) | ||
308 | ] | ||
309 | in | ||
310 | div [] | ||
311 | [ List.map | ||
312 | (\( name, loc ) -> | ||
313 | li [] | ||
314 | [ a [ href loc ] [ text name ] | ||
315 | ] | ||
316 | ) | ||
317 | links | ||
318 | ++ [ if model.loginModel.loginStatus /= Login.LoggedIn then | ||
319 | li [] [ a [ href "/login" ] [ text "Login" ] ] | ||
320 | |||
321 | else | ||
322 | button [ onClick LogoutPressed ] [ text "Logout" ] | ||
323 | ] | ||
324 | |> ul [] | ||
325 | ] | ||
326 | |||
327 | |||
328 | pageWrap : Model -> Html Msg -> List (Html Msg) | ||
329 | pageWrap model page = | ||
330 | [ div [] | ||
331 | [ viewHeader model | ||
332 | , page | ||
333 | ] | ||
334 | ] | ||
335 | |||
336 | |||
337 | viewLink : String -> Html msg | ||
338 | viewLink path = | ||
339 | li [] [ a [ href path ] [ text path ] ] | ||