Merge pull request 'Homepage re-jig' (#32) from filter-rejig into main
continuous-integration/drone/push Build is passing Details

Reviewed-on: #32
This commit is contained in:
3wordchant 2023-03-28 17:42:59 +00:00
commit 41ccf212a9
6 changed files with 442 additions and 323 deletions

View File

@ -16,6 +16,7 @@
"elm/url": "1.0.0", "elm/url": "1.0.0",
"elm-community/json-extra": "4.3.0", "elm-community/json-extra": "4.3.0",
"elm-community/string-extra": "4.0.1", "elm-community/string-extra": "4.0.1",
"fapian/elm-html-aria": "1.4.0",
"pablohirafuji/elm-markdown": "2.0.5" "pablohirafuji/elm-markdown": "2.0.5"
}, },
"indirect": { "indirect": {

480
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -30,6 +30,6 @@
"npm-run-all": "4.1.5" "npm-run-all": "4.1.5"
}, },
"dependencies": { "dependencies": {
"elm-format": "^0.8.5" "elm-format": "^0.8.6"
} }
} }

View File

@ -29,7 +29,27 @@ i.fas, i.fab {
} }
/* Categories */ /* Filtering */
#filter {
position: fixed;
left: 0;
background-color: var(--light-blue);
padding-top: 1rem;
height: 100%;
}
#filter > form > div {
margin: 1rem 0;
}
#filter h3 {
font-size: 18px;
}
#filter label {
display: block;
}
.category-tile { .category-tile {
cursor: pointer; cursor: pointer;
@ -41,6 +61,12 @@ i.fas, i.fab {
border-radius: 5px; border-radius: 5px;
} }
/* Intro */
.card#intro {
margin: 1em 0;
}
/* Cards */ /* Cards */
.app-category { .app-category {
@ -100,31 +126,6 @@ i.fas, i.fab {
} }
/* Search bar*/
.search-bar-container {
margin: 2em;
display: flex;
flex-flow: row wrap;
align-items: center;
}
.search-bar-input, .search-dropdown {
margin: 0.5em;
height: calc(1.5em + .75rem + 2px);
background-color: var(--white);
border: 1px solid var(--dark-pink);
border-radius: .5rem;
}
.search-bar-input {
width: 30rem;
}
.search-dropdown {
width: 10rem;
}
/* Navbar */ /* Navbar */
.navbar-text { .navbar-text {
color: var(--dark-blue); color: var(--dark-blue);
@ -146,3 +147,10 @@ i.fas, i.fab {
.card-body p { .card-body p {
} }
} }
@media (max-width: 768px) {
#filter {
width: 100%;
position: relative;
margin-bottom: 1rem;
}

View File

@ -272,9 +272,9 @@ viewApp app readme =
] ]
, div [ class "card-footer" ] , div [ class "card-footer" ]
[ h5 [] [ text "Versions" ] [ h5 [] [ text "Versions" ]
, ul [] ( , ul []
List.map (\version -> li [] [ text (version) ]) app.versions (List.map (\version -> li [] [ text version ]) app.versions)
)] ]
] ]
] ]
@ -319,8 +319,6 @@ featuresDecoder =
] ]
versionsDecoder : Decode.Decoder (List String) versionsDecoder : Decode.Decoder (List String)
versionsDecoder = versionsDecoder =
Decode.list (Decode.keyValuePairs Decode.value) Decode.list (Decode.keyValuePairs Decode.value)
@ -332,7 +330,6 @@ buildVersions versions =
List.concatMap (List.map (\( version, _ ) -> version)) versions List.concatMap (List.map (\( version, _ ) -> version)) versions
appDecoder : Decode.Decoder App appDecoder : Decode.Decoder App
appDecoder = appDecoder =
Decode.succeed App Decode.succeed App

View File

@ -1,8 +1,9 @@
module Pages.Top exposing (Model, Msg, Params, page) module Pages.Top exposing (Model, Msg, Params, page)
import Enum exposing (Enum) import Enum exposing (Enum)
import Html exposing (Html, a, button, div, h2, h5, i, img, li, p, span, text, ul, form, label, select, option, input) import Html exposing (Html, a, button, div, form, h2, h3, h5, i, img, input, label, li, option, p, select, span, text, ul)
import Html.Attributes exposing (alt, class, href, src, style, id, for, value) import Html.Attributes exposing (alt, class, for, href, id, src, style, value)
import Html.Attributes.Aria exposing (ariaLabel)
import Html.Events exposing (onClick, onInput) import Html.Events exposing (onClick, onInput)
import Http import Http
import Json.Decode as Decode import Json.Decode as Decode
@ -26,6 +27,7 @@ page =
} }
-- INIT -- INIT
@ -52,8 +54,8 @@ type alias Model =
, filter_category : Maybe Category , filter_category : Maybe Category
, filter_text : Maybe String , filter_text : Maybe String
, status : Status , status : Status
, apps : (List App) , apps : List App
, results : (List App) , results : List App
} }
@ -77,11 +79,10 @@ categories =
[ ( "Apps", Apps ) [ ( "Apps", Apps )
, ( "Utilities", Utilities ) , ( "Utilities", Utilities )
, ( "Development", Development ) , ( "Development", Development )
, ("All", All) , ( "All", All )
] ]
init : Url Params -> ( Model, Cmd Msg ) init : Url Params -> ( Model, Cmd Msg )
init { params } = init { params } =
( default_model, loadApps ) ( default_model, loadApps )
@ -94,8 +95,14 @@ default_image =
default_model : Model default_model : Model
default_model = default_model =
{ filter_score = Nothing, filter_category = Nothing, filter_text = Nothing, { filter_score = Nothing
status = Loading, apps = [], results = [] } , filter_category = Nothing
, filter_text = Nothing
, status = Loading
, apps = []
, results = []
}
-- UPDATE -- UPDATE
@ -113,41 +120,55 @@ filterAppsScore : List App -> Maybe Int -> List App
filterAppsScore apps score = filterAppsScore apps score =
case score of case score of
Just s -> Just s ->
List.filter (\app -> List.filter
(\app ->
app.status >= s app.status >= s
) apps )
apps
Nothing -> Nothing ->
apps apps
filterAppsCategory : List App -> Maybe Category -> List App filterAppsCategory : List App -> Maybe Category -> List App
filterAppsCategory apps category = filterAppsCategory apps category =
case category of case category of
Just c -> Just c ->
if c == All then if c == All then
apps apps
else else
List.filter (\app -> List.filter
app.category == categories.toString c (\app ->
) apps app.category == categories.toString c
)
apps
Nothing -> Nothing ->
apps apps
filterAppsText : List App -> Maybe String -> List App filterAppsText : List App -> Maybe String -> List App
filterAppsText apps text = filterAppsText apps text =
case text of case text of
Just "" -> Just "" ->
apps apps
Just t -> Just t ->
List.filter (\app -> List.filter
String.contains (String.toLower t) ( (\app ->
String.toLower app.name ++ String.toLower ( String.contains (String.toLower t)
Maybe.withDefault "" app.description (String.toLower app.name
) ++ String.toLower
(Maybe.withDefault "" app.description)
)
) )
) apps apps
Nothing -> Nothing ->
apps apps
update : Msg -> Model -> ( Model, Cmd Msg ) update : Msg -> Model -> ( Model, Cmd Msg )
update msg model = update msg model =
case msg of case msg of
@ -156,51 +177,71 @@ update msg model =
FilterScore filter -> FilterScore filter ->
let let
filter_score = String.toInt filter filter_score =
String.toInt filter
in in
( { model ( { model
| filter_score = filter_score | filter_score = filter_score
, results = filterAppsScore ( , results =
filterAppsCategory ( filterAppsScore
filterAppsText model.apps model.filter_text (filterAppsCategory
) model.filter_category (filterAppsText model.apps model.filter_text)
) (String.toInt filter) model.filter_category
}, Cmd.none ) )
(String.toInt filter)
}
, Cmd.none
)
FilterCategory filter -> FilterCategory filter ->
let let
category = categories.fromString filter category =
categories.fromString filter
in in
( { model ( { model
| filter_category = category | filter_category = category
, results = filterAppsCategory ( , results =
filterAppsScore ( filterAppsCategory
filterAppsText model.apps model.filter_text (filterAppsScore
)model.filter_score (filterAppsText model.apps model.filter_text)
) category model.filter_score
}, Cmd.none ) )
category
}
, Cmd.none
)
FilterText filter -> FilterText filter ->
( { model ( { model
| filter_text = Just filter | filter_text = Just filter
, results = filterAppsText ( , results =
filterAppsScore ( filterAppsText
filterAppsCategory model.apps model.filter_category (filterAppsScore
) model.filter_score (filterAppsCategory model.apps model.filter_category)
) (Just filter) model.filter_score
}, Cmd.none ) )
(Just filter)
}
, Cmd.none
)
GotApps result -> GotApps result ->
case result of case result of
Ok apps -> Ok apps ->
( { default_model ( { default_model
| status = Success | status = Success
, apps = apps , apps = apps
, results = apps }, Cmd.none ) , results = apps
}
, Cmd.none
)
Err _ -> Err _ ->
( { default_model ( { default_model
| status = Failure}, Cmd.none ) | status = Failure
}
, Cmd.none
)
subscriptions : Model -> Sub Msg subscriptions : Model -> Sub Msg
@ -221,7 +262,7 @@ view model =
body : Model -> Html Msg body : Model -> Html Msg
body model = body model =
div [ class "pt-3" ] div []
[ viewApps model [ viewApps model
] ]
@ -247,7 +288,7 @@ viewStatusBadge app =
"badge-dark" "badge-dark"
in in
span [ class ("card-link badge " ++ status_class) ] span [ class ("card-link badge " ++ status_class) ]
[ text ("Status: " ++ String.fromInt app.status) ] [ text ("Score: " ++ String.fromInt app.status) ]
viewApp : App -> Html Msg viewApp : App -> Html Msg
@ -303,16 +344,14 @@ viewApp app =
, p [ class "card-description" ] [ text (ellipsis 100 (withDefault "" app.description)) ] , p [ class "card-description" ] [ text (ellipsis 100 (withDefault "" app.description)) ]
] ]
, div [ class "footer" ] , div [ class "footer" ]
[ viewStatusBadge app, span [ class "badge app-category" ] [ text app.category ]] [ viewStatusBadge app, span [ class "badge app-category" ] [ text app.category ] ]
] ]
] ]
viewCategories : ( String, Category ) -> Html Msg
viewCategories : (String, Category) -> Html Msg
viewCategories category = viewCategories category =
div [ class "category-tile", onClick (FilterCategory (Tuple.first category)) ] [text (Tuple.first category)] div [ class "category-tile", onClick (FilterCategory (Tuple.first category)) ] [ text (Tuple.first category) ]
viewApps : Model -> Html Msg viewApps : Model -> Html Msg
@ -335,32 +374,52 @@ viewApps model =
Success -> Success ->
div [ class "row justify-content-center" ] div [ class "row justify-content-center" ]
[ div [ class "col-md-3" ] [ [ div [ class "col-md-3", id "filter" ]
h2 [ class "app-headings" ] [text "Categories"] [ h2 [ class "app-headings" ] [ text "Finding things" ]
, div [] (List.map viewCategories categories.list) , form []
] [ div []
, div [ class "col-md-6" ] [ h3 [] [ text "Categories" ]
[ div [ class "row" ] , div [] (List.map viewCategories categories.list)
[ div [ class "col-sm-12" ] [ ]
div [] [ , div []
form [ class "search-bar-container" ] [ [ h3 [] [ text "Search" ]
label [ for "text" ] [ text "Search" ] , input [ ariaLabel "search", id "text", onInput FilterText ] []
, input [ class "search-bar-input", id "text", onInput FilterText ] [] ]
, label [ for "level" ] [ text " and show only items that have at least " ] , div []
, select [ class "search-dropdown", id "level", onInput FilterScore ] [ [ h3 [] [ text "Status" ]
option [ ] [ text "any" ] , label [ for "level" ] [ text "Minimum score:" ]
, option [ value "5" ] [ text "5 (production)" ] , select [ class "search-dropdown", id "level", onInput FilterScore ]
, option [ value "4" ] [ text "4 (beta)" ] [ option [] [ text "any" ]
, option [ value "3" ] [ text "3 (alpha)" ] , option [ value "5" ] [ text "5 (amazing)" ]
, option [ value "2" ] [ text "2 (pre-alpha)" ] , option [ value "4" ] [ text "4 (good)" ]
, option [ value "1" ] [ text "1 (work-in-progress)" ] , option [ value "3" ] [ text "3 (ok)" ]
] , option [ value "2" ] [ text "2 (basic)" ]
, text "builds " , option [ value "1" ] [ text "1 (chaos)" ]
]
] ]
] ]
] ]
, h2 [ class "app-headings" ] [ text "Apps" ] ]
, div [ class "col-md-6 offset-md-3" ]
[ div [ class "row" ]
[ div [ class "col-sm-12" ]
[ div []
[]
]
]
, div [ id "intro", class "card" ] [ div [ class "card-body" ] [
h2 [ class "app-headings card-title" ] [ text "Co-op Cloud Recipe Catalogue" ]
, text "You can use these recipes ("
, a [ href
"https://docs.coopcloud.tech/glossary/#recipe" ] [
text "What's a recipe?"
]
, text ") with "
, a [ href "https://coopcloud.tech" ] [
text "Co-op Cloud"
]
, text "."
]
]
, div [ class "row" ] , div [ class "row" ]
(List.map viewApp (List.map viewApp
(model.results (model.results
@ -371,9 +430,11 @@ viewApps model =
) )
) )
] ]
, div [class "col-md-3"] [] , div [ class "col-md-3" ] []
] ]
-- HTTP -- HTTP