From c8b58f6080190d3af55370d8c0d44d4258be7d04 Mon Sep 17 00:00:00 2001 From: Avinal Kumar Date: Fri, 27 Jan 2023 21:07:57 +0530 Subject: [PATCH] add category and minor improvements Signed-off-by: Avinal Kumar --- Makefile | 2 +- elm.json | 4 +- src/Pages/Pages/AboutMe.elm | 9 + src/Pages/Pages/Projects.elm | 68 +++++++ src/Pages/Posts.elm | 35 ++-- src/Pages/Posts/Category_.elm | 180 ++++++++++++++++++ .../Posts/{ALL_.elm => Category_/Post_.elm} | 11 +- src/Utils/Constants.elm | 18 ++ src/Utils/Utils.elm | 54 +++++- src/View.elm | 4 +- 10 files changed, 356 insertions(+), 29 deletions(-) create mode 100644 src/Pages/Pages/AboutMe.elm create mode 100644 src/Pages/Pages/Projects.elm create mode 100644 src/Pages/Posts/Category_.elm rename src/Pages/Posts/{ALL_.elm => Category_/Post_.elm} (96%) diff --git a/Makefile b/Makefile index fe2c3b5..f82a2a5 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,7 @@ build: # compile and watch for dev elm-serve: - npx elm-land serve + npx elm-land server # build css and watch css-serve: diff --git a/elm.json b/elm.json index c9ffabb..0f54795 100644 --- a/elm.json +++ b/elm.json @@ -13,6 +13,7 @@ "elm/html": "1.0.0", "elm/http": "2.0.0", "elm/json": "1.1.3", + "elm/parser": "1.1.0", "elm/svg": "1.0.1", "elm/time": "1.0.0", "elm/url": "1.0.0" @@ -20,7 +21,6 @@ "indirect": { "elm/bytes": "1.0.8", "elm/file": "1.0.5", - "elm/parser": "1.1.0", "elm/regex": "1.0.0", "elm/virtual-dom": "1.0.3" } @@ -29,4 +29,4 @@ "direct": {}, "indirect": {} } -} \ No newline at end of file +} diff --git a/src/Pages/Pages/AboutMe.elm b/src/Pages/Pages/AboutMe.elm new file mode 100644 index 0000000..b7d8282 --- /dev/null +++ b/src/Pages/Pages/AboutMe.elm @@ -0,0 +1,9 @@ +module Pages.Pages.AboutMe exposing (page) + +import Html exposing (Html) +import View exposing (View) + + +page : View msg +page = + View.fromString "Pages.Pages.AboutMe" diff --git a/src/Pages/Pages/Projects.elm b/src/Pages/Pages/Projects.elm new file mode 100644 index 0000000..bfa896d --- /dev/null +++ b/src/Pages/Pages/Projects.elm @@ -0,0 +1,68 @@ +module Pages.Pages.Projects exposing (Model, Msg, page) + +import Effect exposing (Effect) +import Route exposing (Route) +import Html +import Page exposing (Page) +import Shared +import View exposing (View) + + +page : Shared.Model -> Route () -> Page Model Msg +page shared route = + Page.new + { init = init + , update = update + , subscriptions = subscriptions + , view = view + } + + + +-- INIT + + +type alias Model = + {} + + +init : () -> ( Model, Effect Msg ) +init () = + ( {} + , Effect.none + ) + + + +-- UPDATE + + +type Msg + = ExampleMsgReplaceMe + + +update : Msg -> Model -> ( Model, Effect Msg ) +update msg model = + case msg of + ExampleMsgReplaceMe -> + ( model + , Effect.none + ) + + + +-- SUBSCRIPTIONS + + +subscriptions : Model -> Sub Msg +subscriptions model = + Sub.none + + + +-- VIEW + + +view : Model -> View Msg +view model = + View.fromString "Pages.Pages.Projects" diff --git a/src/Pages/Posts.elm b/src/Pages/Posts.elm index b474743..26cf750 100644 --- a/src/Pages/Posts.elm +++ b/src/Pages/Posts.elm @@ -2,7 +2,7 @@ module Pages.Posts exposing (Model, Msg, page) import Effect exposing (Effect) import Html exposing (Html) -import Html.Attributes exposing (class, href, src) +import Html.Attributes exposing (class, href, src, target) import Http import Json.Decode as Json import Page exposing (Page) @@ -102,14 +102,16 @@ view model = case bloglist of first :: rest -> Html.div [ class "max-w-6xl p-6 mx-auto space-y-6 sm:space-y-12 mb-16" ] - [ Html.a [ class "block max-w-sm gap-3 mx-auto sm:max-w-full group hover:no-underline focus:no-underline lg:grid lg:grid-cols-12 bg-neutral-900", href ("/posts/" ++ first.category ++ "/" ++ first.slug) ] - [ Html.img [ class "object-cover w-full h-64 rounded sm:h-96 lg:col-span-7", src first.image ] [] + [ Html.div [ class "block max-w-sm gap-3 mx-auto sm:max-w-full group hover:no-underline focus:no-underline lg:grid lg:grid-cols-12 bg-neutral-900" ] + [ Html.a [ class "lg:col-span-7", href <| "/posts/" ++ first.category ++ "/" ++ first.slug ] [ Html.img [ class "object-cover w-full h-64 rounded sm:h-96 lg:col-span-7", src first.image ] [] ] , Html.div [ class "p-6 space-y-2 lg:col-span-5" ] - [ Html.h3 [ class "text-2xl font-semibold sm:text-4xl group-hover:underline group-focus:underline" ] - [ Html.text first.title ] - , Html.span [ class "text-gray-400" ] [ Html.text first.date ] - , Html.p [] [ Html.text <| String.left 200 first.description ] - , UU.categoryNtags first.category [] + [ Html.a [ href <| "/posts/" ++ first.category ++ "/" ++ first.slug ] + [ Html.h3 [ class "text-2xl font-semibold sm:text-4xl group-hover:underline group-focus:underline" ] + [ Html.text first.title ] + , Html.span [ class "text-gray-400" ] [ Html.text first.date ] + , Html.p [] [ Html.text <| String.left 200 first.description ] + ] + , Html.a [ href <| "/posts/" ++ first.category, target "_blank" ] [ UU.categoryNtags first.category [] ] ] ] , Html.div [ class "grid justify-center grid-cols-1 gap-6 sm:grid-cols-2 lg:grid-cols-3" ] <| List.map card rest @@ -120,13 +122,15 @@ view model = card : JsonMeta -> Html msg card blog = - Html.a [ class "max-w-sm mx-auto group hover:no-underline focus:no-underline bg-neutral-900", href ("/posts/" ++ blog.category ++ "/" ++ blog.slug) ] - [ Html.img [ class "object-cover w-full h-44 rounded", src blog.image ] [] + Html.div [ class "max-w-sm mx-auto group hover:no-underline focus:no-underline bg-neutral-900", href ("/posts/" ++ blog.category ++ "/" ++ blog.slug) ] + [ Html.a [ href <| "/posts/" ++ blog.category ++ "/" ++ blog.slug ] [ Html.img [ class "object-cover w-full h-44 rounded", src blog.image ] [] ] , Html.div [ class "p-6 space-y-2" ] - [ Html.h3 [ class "text-2xl font-semibold group-hover:underline group-focus:underline" ] [ Html.text blog.title ] - , Html.span [ class " text-gray-400" ] [ Html.text blog.date ] - , Html.p [] [ Html.text <| String.left 200 blog.description ] - , UU.categoryNtags blog.category [] + [ Html.a [ href <| "/posts/" ++ blog.category ++ "/" ++ blog.slug ] + [ Html.h3 [ class "text-2xl font-semibold group-hover:underline group-focus:underline" ] [ Html.text blog.title ] + , Html.span [ class " text-gray-400" ] [ Html.text <| UU.getFormattedDate blog.date ] + , Html.p [] [ Html.text <| String.left 200 blog.description ] + ] + , Html.a [ href <| "/posts/" ++ blog.category, target "_blank" ] [ UU.categoryNtags blog.category [] ] ] ] @@ -145,7 +149,8 @@ view model = { title = "Blog by Avinal" , body = [ Html.section [ class "text-gray-100" ] - [ maincard blogList + [ Html.h1 [ class "text-5xl font-bold mb-6 mt-12 text-center text-white" ] [ Html.text <| "Welcome to my blog" ] + , maincard blogList , Html.div [ class "fixed bottom-0 left-0 bg-neutral-900 z-20 p-4 w-full md:flex md:items-center md:justify-between md:p-4" ] [ Html.ul [ class "flex flex-wrap items-center mt-3 text-xl text-neutral-500 sm:mt-0" ] diff --git a/src/Pages/Posts/Category_.elm b/src/Pages/Posts/Category_.elm new file mode 100644 index 0000000..52fadde --- /dev/null +++ b/src/Pages/Posts/Category_.elm @@ -0,0 +1,180 @@ +module Pages.Posts.Category_ exposing (Model, Msg, page) + +import Effect exposing (Effect) +import Html exposing (Html) +import Html.Attributes exposing (class, href, src) +import Http +import Json.Decode as Json +import Page exposing (Page) +import Route exposing (Route) +import Shared +import Utils.Constants exposing (..) +import Utils.Utils as UU +import View exposing (View) + + +page : Shared.Model -> Route { category : String } -> Page Model Msg +page shared route = + Page.new + { init = init route + , update = update + , subscriptions = subscriptions + , view = view + } + + + +-- INIT + + +type alias JsonMeta = + { title : String + , date : String + , description : String + , category : String + , slug : String + , image : String + } + + +type alias Model = + { error : Maybe String + , blogList : Maybe (List JsonMeta) + , category : String + } + + +init : Route { category : String } -> () -> ( Model, Effect Msg ) +init route () = + let + cmd : Cmd Msg + cmd = + Http.get + { url = "/content/posts/posts.json" + , expect = Http.expectJson BloglistReceived (Json.list jsonMetaDecoder) + } + in + ( { error = Nothing + , blogList = Nothing + , category = route.params.category + } + , Effect.sendCmd cmd + ) + + + +-- UPDATE + + +type Msg + = BloglistReceived (Result Http.Error (List JsonMeta)) + + +update : Msg -> Model -> ( Model, Effect Msg ) +update msg model = + case msg of + BloglistReceived (Ok data) -> + ( { model | blogList = Just data } + , Effect.none + ) + + BloglistReceived (Err err) -> + ( { model | error = Just (UU.errorToString err) }, Effect.none ) + + + +-- SUBSCRIPTIONS + + +subscriptions : Model -> Sub Msg +subscriptions model = + Sub.none + + + +-- VIEW + + +view : Model -> View Msg +view model = + let + card : JsonMeta -> Html msg + card blog = + Html.a [ class "flex flex-wrap mb-6 ", href <| "/posts/" ++ blog.category ++ "/" ++ blog.slug ] + [ Html.div [ class "grow-0 shrink-0 basis-auto w-full md:w-3/12 md:mb-0 ml-auto" ] + [ Html.div [ class "overflow-hidden relative bg-no-repeat bg-cover ripple" ] + [ Html.img [ src blog.image, class "w-full h-44 object-cover rounded" ] [] + ] + ] + , Html.div [ class "grow-0 shrink-0 basis-auto w-full md:w-9/12 xl:w-7/12 p-3 md:mb-0 mr-auto bg-neutral-900" ] + [ Html.h5 [ class "text-2xl font-bold mb-2" ] [ Html.text blog.title ] + , Html.span [ class "text-gray-400 text-sm" ] [ Html.text blog.date ] + , Html.p [ class "text-gray-500 mt-4 text-md" ] [ Html.text <| String.left 200 blog.description ] + ] + ] + + footerLinkToLeft : Link -> Html msg + footerLinkToLeft link = + Html.li [] + [ Html.a + [ href link.url + , class "mr-4 md:mr-6 underline decoration-cyan-500 hover:decoration-pink-500" + ] + [ Html.text link.text ] + ] + in + case model.blogList of + Just blogList -> + let + filteredList = + filterBlogListByCategory blogList model.category + in + case filteredList of + [] -> + { title = "No posts in this category" + , body = [] + } + + clist -> + { title = "Posts in " ++ model.category ++ " category" + , body = + [ Html.div [] + [ Html.section [ class "mb-32 text-gray-200 text-center md:text-left" ] <| + Html.h1 [ class "text-5xl font-bold mb-12 mt-12 text-center text-white" ] [ Html.text "Posts in ", Html.i [ class "text-pink-600" ] [ Html.text model.category ], Html.text " category" ] + :: List.map card clist + , Html.div [ class "fixed bottom-0 left-0 bg-neutral-900 z-20 p-4 w-full md:flex md:items-center md:justify-between md:p-4" ] + [ Html.ul + [ class "flex flex-wrap items-center mt-3 text-xl text-neutral-500 sm:mt-0" ] + (List.map footerLinkToLeft <| + { text = "Home", url = "/" } + :: Utils.Constants.footerLinks + ) + ] + ] + ] + } + + Nothing -> + { title = "There is no such category" + , body = [] + } + + + +-- UTILS + + +jsonMetaDecoder : Json.Decoder JsonMeta +jsonMetaDecoder = + Json.map6 JsonMeta + (Json.field "title" Json.string) + (Json.field "date" Json.string) + (Json.field "description" Json.string) + (Json.field "category" Json.string) + (Json.field "slug" Json.string) + (Json.field "image" Json.string) + + +filterBlogListByCategory : List JsonMeta -> String -> List JsonMeta +filterBlogListByCategory blogList category = + List.filter (\meta -> meta.category == category) blogList diff --git a/src/Pages/Posts/ALL_.elm b/src/Pages/Posts/Category_/Post_.elm similarity index 96% rename from src/Pages/Posts/ALL_.elm rename to src/Pages/Posts/Category_/Post_.elm index 0cbae23..7a6521b 100644 --- a/src/Pages/Posts/ALL_.elm +++ b/src/Pages/Posts/Category_/Post_.elm @@ -1,4 +1,4 @@ -module Pages.Posts.ALL_ exposing (Model, Msg, page) +module Pages.Posts.Category_.Post_ exposing (Model, Msg, page) import Effect exposing (Effect) import Html exposing (Html) @@ -17,7 +17,7 @@ import View exposing (View) import Yaml.Decode as Yaml -page : Shared.Model -> Route { first_ : String, rest_ : List String } -> Page Model Msg +page : Shared.Model -> Route { category : String, post : String } -> Page Model Msg page shared route = Page.new { init = init route @@ -59,14 +59,13 @@ type alias Blog = } -init : Route { first_ : String, rest_ : List String } -> () -> ( Model, Effect Msg ) +init : Route { category : String, post : String } -> () -> ( Model, Effect Msg ) init route () = let requestUrl = UU.contentUrl - { category = route.params.first_ - , post = - Maybe.withDefault "" <| List.head route.params.rest_ + { category = route.params.category + , post = route.params.post } cmd : Cmd Msg diff --git a/src/Utils/Constants.elm b/src/Utils/Constants.elm index f56ab8b..6e42f8d 100644 --- a/src/Utils/Constants.elm +++ b/src/Utils/Constants.elm @@ -34,6 +34,24 @@ iconLinks = ] +months : Array String +months = + Array.fromList + [ "January" + , "February" + , "March" + , "April" + , "May" + , "June" + , "July" + , "August" + , "September" + , "October" + , "November" + , "December" + ] + + nameMatrix : Array Int nameMatrix = Array.fromList diff --git a/src/Utils/Utils.elm b/src/Utils/Utils.elm index e1f624b..c23af20 100644 --- a/src/Utils/Utils.elm +++ b/src/Utils/Utils.elm @@ -1,21 +1,69 @@ module Utils.Utils exposing (..) +import Array exposing (Array) import Html exposing (Html) -import Html.Attributes exposing (class) +import Html.Attributes exposing (class, href, target) import Http exposing (Error(..)) +import Parser exposing (..) import Utils.Constants exposing (..) +type alias Date = + { day : Int + , month : Int + , year : Int + } + + +day : Parser Int +day = + succeed identity + |= int + + +month : Parser Int +month = + succeed identity + |= int + + +year : Parser Int +year = + succeed identity + |= int + + +getFormattedDate : String -> String +getFormattedDate dateString = + case Parser.run dateParser dateString of + Ok date -> + (Maybe.withDefault "Month" <| Array.get (date.month - 1) months) ++ " " ++ String.fromInt date.day ++ ", " ++ String.fromInt date.year + + Err err -> + "Invalid date!!" + + +dateParser : Parser Date +dateParser = + succeed Date + |= day + |. symbol "-" + |= month + |. symbol "-" + |= year + + + categoryNtags : String -> List String -> Html msg categoryNtags category tags = Html.span [ class "flex flex-wrap py-6 space-x-2 border-t border-dashed border-teal-500" ] - (Html.b [ class "px-3 py-1 m-1 rounded-sm hover:underline bg-pink-400 text-gray-900" ] + (Html.a [ class "px-3 py-1 m-1 rounded-sm hover:underline bg-pink-400 text-gray-900 font-bold", href <| "/posts/" ++ category, target "_blank" ] [ Html.text category ] :: List.map (\tag -> Html.i [ class "px-3 py-1 m-1 rounded-sm hover:underline bg-cyan-500 text-gray-900" ] - [ Html.text tag + [ Html.text <| "#" ++ tag ] ) tags diff --git a/src/View.elm b/src/View.elm index 2cd8da1..dbfa592 100644 --- a/src/View.elm +++ b/src/View.elm @@ -35,7 +35,7 @@ toBrowserDocument : } -> Browser.Document msg toBrowserDocument { view } = - { title = view.title + { title = view.title ++ " | Avinal's personal website" , body = [ Html.main_ [ class "container mx-auto bg-neutral-800" ] view.body @@ -57,7 +57,7 @@ authenticated pages. -} none : View msg none = - { title = "Be My SpaceTime" + { title = "Avinal | Personal Website" , body = [] }