Compare commits

..

No commits in common. "main" and "em_change-site" have entirely different histories.

17 changed files with 443 additions and 745 deletions

View File

@ -1,25 +1,24 @@
--- ---
kind: pipeline kind: pipeline
name: deploy to recipes.coopcloud.tech name: deploy to dev.apps.coopcloud.tech
steps: steps:
- name: build image - name: build image
image: plugins/docker image: plugins/docker
settings: settings:
username: 3wordchant username:
from_secret: docker_reg_username_3wc
password: password:
from_secret: git_coopcloud_tech_token_3wc from_secret: docker_reg_passwd_3wc
repo: git.coopcloud.tech/coop-cloud/recipes.coopcloud.tech repo: 3wordchant/abra-apps
tags: latest tags: latest
registry: git.coopcloud.tech
- name: deployment - name: deployment
image: git.coopcloud.tech/coop-cloud/stack-ssh-deploy:latest image: decentral1se/stack-ssh-deploy:latest
settings: settings:
stack: recipes_coopcloud_tech stack: dev_apps_coopcloud_tech
host: swarm-0.coopcloud.tech
deploy_key: deploy_key:
from_secret: drone_ssh_swarm-0_coopcloud_tech from_secret: drone_ssh_swarm-demo.autonomic.zone
host: swarm-demo.autonomic.zone
trigger: trigger:
branch: branch:
- main - main

View File

@ -1,8 +0,0 @@
# authors
> If you're looking at this and you hack on `abra` and you're not listed here,
> please do add yourself! This is a community project, let's show some :heart:
- 3wordchant
- eejum
- hazelnot

View File

@ -1,4 +1,4 @@
FROM node:21-alpine AS build FROM node:12-alpine
RUN mkdir /code RUN mkdir /code
WORKDIR /code WORKDIR /code
@ -11,10 +11,5 @@ ENV PATH=$PATH:/code/node_modules/elm-linter/bin:/code/node_modules/elm-format/b
# Add remainder of files # Add remainder of files
COPY . . COPY . .
RUN ["npm", "run", "build"] ENTRYPOINT ["/usr/local/bin/npm"]
CMD ["run", "prod"]
FROM nginx
COPY --from=build /code/public/ /usr/share/nginx/html/
COPY ./nginx.conf /etc/nginx/conf.d/default.conf

View File

@ -1,8 +1,8 @@
# recipes.coopcloud.tech # recipes.coopcloud.tech
> https://recipes.coopcloud.tech is the current live site > WIP! https://dev.apps.coopcloud.tech is the current live site
[![Build Status](https://build.coopcloud.tech/api/badges/coop-cloud/recipes.coopcloud.tech/status.svg?ref=refs/heads/main)](https://build.coopcloud.tech/coop-cloud/recipes.coopcloud.tech) [![Build Status](https://drone.autonomic.zone/api/badges/coop-cloud/recipes.coopcloud.tech/status.svg?ref=refs/heads/main)](https://drone.autonomic.zone/coop-cloud/recipes.coopcloud.tech)
The Co-op Cloud recipes catalogue. Our digital configuration commons, browsable in a simple and pleasant web UI. The Co-op Cloud recipes catalogue. Our digital configuration commons, browsable in a simple and pleasant web UI.
@ -33,8 +33,6 @@ __Build command:__ `npm run build`
__Publish directory:__ `public` __Publish directory:__ `public`
Then, run `make` to deploy the new version to `mellor.coopcloud.tech`.
## thanks ## thanks
created using [`elm-spa`](https://elm-spa.dev) created using [`elm-spa`](https://elm-spa.dev)

View File

@ -3,28 +3,25 @@ version: "3.8"
services: services:
app: app:
image: "git.coopcloud.tech/coop-cloud/recipes.coopcloud.tech:latest" image: "3wordchant/abra-apps:latest"
networks: networks:
- proxy - proxy
# healthcheck: healthcheck:
# test: "nodejs -e \"http.get('http://localhost:8000', (res) => { console.log('status: ', res.statusCode); if (res.statusCode == 200) { process.exit(0); } else { process.exit(1); } });\"" test: "nodejs -e \"http.get('http://localhost:8000', (res) => { console.log('status: ', res.statusCode); if (res.statusCode == 200) { process.exit(0); } else { process.exit(1); } });\""
# interval: 30s interval: 30s
# timeout: 10s timeout: 10s
# retries: 3 retries: 3
# start_period: 1m start_period: 1m
deploy: deploy:
update_config: update_config:
failure_action: rollback failure_action: rollback
order: start-first order: start-first
labels: labels:
- "traefik.enable=true" - "traefik.enable=true"
- "traefik.http.services.abra-recipes.loadbalancer.server.port=80" - "traefik.http.services.abra-apps-dev.loadbalancer.server.port=8000"
- "traefik.http.routers.abra-recipes.rule=(Host(`dev.apps.coopcloud.tech`)||Host(`recipes.coopcloud.tech`))" - "traefik.http.routers.abra-apps-dev.rule=Host(`recipes.coopcloud.tech`)"
- "traefik.http.routers.abra-recipes.entrypoints=web-secure" - "traefik.http.routers.abra-apps-dev.entrypoints=web-secure"
- "traefik.http.routers.abra-recipes.tls.certresolver=production" - "traefik.http.routers.abra-apps-dev.tls.certresolver=production"
- "traefik.http.routers.abra-recipes.middlewares=abra-recipes-redirect"
- "traefik.http.middlewares.abra-recipes-redirect.headers.SSLForceHost=true"
- "traefik.http.middlewares.abra-recipes-redirect.headers.SSLHost=recipes.coopcloud.tech"
networks: networks:
proxy: proxy:

View File

@ -16,7 +16,6 @@
"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": {

View File

@ -1,6 +1,6 @@
deploy: deploy:
@DOCKER_CONTEXT=mellor.coopcloud.tech \ @DOCKER_CONTEXT=mellor.coopcloud.tech \
docker stack rm recipes_coopcloud_tech && \ docker stack rm dev_apps_coopcloud_tech && \
DOCKER_CONTEXT=mellor.coopcloud.tech \ DOCKER_CONTEXT=mellor.coopcloud.tech \
docker stack deploy -c compose.yml recipes_coopcloud_tech docker stack deploy -c compose.yml dev_apps_coopcloud_tech

View File

@ -1,17 +0,0 @@
server {
listen 80;
listen [::]:80;
server_name localhost;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri /index.html =404;
}
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html;
location = /50x.html {
root /usr/share/nginx/html;
}
}

654
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,8 +1,8 @@
{ {
"name": "abra-apps", "name": "abra-apps",
"version": "1.0.0", "version": "1.0.0",
"description": "Co-op Cloud recipe catalogue", "description": "Co-op Cloud app catalogue",
"repository": "https://git.coopcloud.tech/toolshed/recipes.coopcloud.tech", "repository": "https://git.coopcloud.tech/coop-cloud/abra-apps",
"scripts": { "scripts": {
"start": "npm install && npm run build:dev && npm run dev", "start": "npm install && npm run build:dev && npm run dev",
"prod": "npm install && npm run build && npm run serve", "prod": "npm install && npm run build && npm run serve",
@ -22,14 +22,14 @@
"author": "", "author": "",
"license": "ISC", "license": "ISC",
"devDependencies": { "devDependencies": {
"chokidar-cli": "3.0.0", "chokidar-cli": "2.1.0",
"elm": "0.19.1-6", "elm": "0.19.1-3",
"elm-live": "4.0.2", "elm-live": "4.0.2",
"elm-spa": "5.0.4", "elm-spa": "5.0.4",
"elm-test": "0.19.1-revision2", "elm-test": "0.19.1-revision2",
"npm-run-all": "4.1.5" "npm-run-all": "4.1.5"
}, },
"dependencies": { "dependencies": {
"elm-format": "^0.8.6" "elm-format": "^0.8.5"
} }
} }

View File

@ -5,7 +5,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- CSS goes here --> <!-- CSS goes here -->
<link rel="stylesheet" href="/style.css"> <link rel="stylesheet" href="/style.css">
<title>Co-op Cloud Recipes</title> <title>abra recipes</title>
<link rel="stylesheet" <link rel="stylesheet"
href="https://maxcdn.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css"> href="https://maxcdn.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css">
<!--<link rel="stylesheet" <!--<link rel="stylesheet"
@ -20,7 +20,7 @@
<nav class="navbar navbar-expand-lg sticky-top font-weight-bold" style="background: rgb(255, 79, 136) none repeat scroll 0% 0%;"> <nav class="navbar navbar-expand-lg sticky-top font-weight-bold" style="background: rgb(255, 79, 136) none repeat scroll 0% 0%;">
<a class="navbar-brand text-dark" href="/"> <a class="navbar-brand text-dark" href="/">
<img src="/logo.png" class="d-inline-block align-top mr-2" alt="" width="30" height="30"> <img src="/logo.png" class="d-inline-block align-top mr-2" alt="" width="30" height="30">
Co-op Cloud Recipes</a> abra recipes</a>
</nav> </nav>
<div class="container-fluid"> <div class="container-fluid">
<div class="pt-3"> <div class="pt-3">
@ -28,7 +28,7 @@
<div class="card"> <div class="card">
<div class="card-header"> <div class="card-header">
<h2> <h2>
Co-op Cloud Recipes</h2> abra recipes</h2>
</div> </div>
<div class="card-body"> <div class="card-body">
<p> <p>

View File

@ -29,51 +29,16 @@ i.fas, i.fab {
} }
/* Filtering */ /* Categories */
#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;
}
input[type=checkbox] {
margin-right: 0.5rem;
}
a.help {
display: inline-block;
margin-left: 0.5rem;
font-size: 14px;
color: var(--dark-pink);
}
.category-tile { .category-tile {
cursor: pointer; cursor: pointer;
} }
.category-tile:hover { .category-tile:hover {
color: var(--dark-blue);
}
.category-tile.active {
color: var(--white); color: var(--white);
} background-color: var(--light-blue);
border-radius: 5px;
/* Intro */
.card#intro {
margin: 1em 0;
} }
/* Cards */ /* Cards */
@ -81,7 +46,6 @@ a.help {
.app-category { .app-category {
background-color: var(--light-blue); background-color: var(--light-blue);
color: var(--white); color: var(--white);
margin-left: 0.5rem;
} }
.card-description { .card-description {
@ -109,14 +73,10 @@ a.help {
.card-img-top { .card-img-top {
width: 100%; width: 100%;
height: 5vw; height: 16vw;
object-fit: cover; object-fit: cover;
} }
.card-img-large {
height: 10vw;
}
.smaller-card { .smaller-card {
border: 1px solid var(--dark-pink); border: 1px solid var(--dark-pink);
border-radius: 20px; border-radius: 20px;
@ -135,6 +95,31 @@ a.help {
} }
/* 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);
@ -147,7 +132,7 @@ a.help {
} }
@media (min-width: 768px) { @media (min-width: 768px) {
.smaller-card .card-body p { .card-body p {
height: 2.5rem; height: 2.5rem;
} }
} }
@ -156,10 +141,3 @@ a.help {
.card-body p { .card-body p {
} }
} }
@media (max-width: 768px) {
#filter {
width: 100%;
position: relative;
margin-bottom: 1rem;
}

View File

@ -1,3 +0,0 @@
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json"
}

View File

@ -36,16 +36,15 @@ view { params } =
let let
elm_link = a [ href "https://elm-lang.org/" ] [ text "Elm" ] elm_link = a [ href "https://elm-lang.org/" ] [ text "Elm" ]
coopcloud_link = a [ href "https://coopcloud.tech/" ] [ text "Co-op Cloud" ] coopcloud_link = a [ href "https://coopcloud.tech/" ] [ text "Co-op Cloud" ]
source_link = a [ href "https://git.coopcloud.tech/toolshed/recipes.coopcloud.tech" ] [ text "source" ] source_link = a [ href "https://git.coopcloud.tech/coop-cloud/abra-apps" ] [ text "source" ]
autonomic_link = a [ href "https://autonomic.zone" ] [ text "Autonomic" ]
in in
{ title = "About Co-op Cloud Recipes" { title = "about abra recipes"
, body = , body =
[ div [ class "pt-3" ] [ div [ class "pt-3" ]
[ div [ class "col-md-6 col-sm-10 mb-3 offset-md-3 offset-sm-1" ] [ div [ class "col-md-6 col-sm-10 mb-3 offset-md-3 offset-sm-1" ]
[ div [ class "card" ] [ div [ class "card" ]
[ div [ class "card-header" ] [ div [ class "card-header" ]
[ h2 [] [ text "Co-op Cloud Recipes" ] [ h2 [] [ text "abra recipes" ]
] ]
, div [ class "card-body" ] , div [ class "card-body" ]
[ p [] [ p []
@ -53,7 +52,7 @@ view { params } =
text "a lil' " text "a lil' "
, text " tool to display " , text " tool to display "
, coopcloud_link , coopcloud_link
, text " recipes." , text " apps."
] ]
, p [] , p []
[ [
@ -64,9 +63,7 @@ view { params } =
] ]
, div [ class "card-footer" ] , div [ class "card-footer" ]
[ p [] [ p []
[ text "made with 🤪 at " [ text "by @3wc ("
, autonomic_link
, text " ("
, source_link , source_link
, text ")" , text ")"
] ]

View File

@ -37,9 +37,9 @@ type alias App =
{ name : String { name : String
, category : String , category : String
, repository : Maybe String , repository : Maybe String
, versions : List String , versions : Maybe (List String)
, icon : Maybe String , icon : Maybe String
, status : Int , status : String
, slug : String , slug : String
, default_branch : String , default_branch : String
, website : Maybe String , website : Maybe String
@ -151,13 +151,13 @@ title : Model -> String
title model = title model =
case model.status of case model.status of
Loading -> Loading ->
"loading Co-op Cloud Recipes" "loading abra recipes"
Failure -> Failure ->
"error - Co-op Cloud Recipes" "error - abra recipes"
Success app -> Success app ->
app.name ++ " Co-op Cloud Recipes" app.name ++ " abra recipes"
body : Model -> Html Msg body : Model -> Html Msg
@ -192,23 +192,23 @@ viewStatusBadge app =
let let
status_class = status_class =
case app.status of case app.status of
5 -> "1" ->
"badge-success" "badge-success"
4 -> "2" ->
"badge-info" "badge-info"
3 -> "3" ->
"badge-warning" "badge-warning"
2 -> "4" ->
"badge-danger" "badge-danger"
_ -> _ ->
"badge-dark" "badge-dark"
in in
span [ class ("card-link badge " ++ status_class) ] span [ class ("card-link badge " ++ status_class) ]
[ text ("Score: " ++ String.fromInt app.status) ] [ text ("Score: " ++ app.status) ]
viewApp : App -> String -> Html Msg viewApp : App -> String -> Html Msg
@ -265,16 +265,13 @@ viewApp app readme =
, repository_link , repository_link
, website_link , website_link
] ]
, img [ class "card-img-top card-img-large", src icon_url, alt ("icon for " ++ app.name) ] [] , img [ class "card-img-top", src icon_url, alt ("icon for " ++ app.name) ] []
, div [ class "card-body" ] , div [ class "card-body" ]
-- render Markdown with no special options -- render Markdown with no special options
[ div [] (Markdown.toHtml Nothing readme) [ div [] (Markdown.toHtml Nothing readme)
] ]
, div [ class "card-footer" ] , div [ class "card-footer" ]
[ h5 [] [ text "Versions" ] []
, ul []
(List.map (\version -> li [] [ text version ]) (List.reverse app.versions))
]
] ]
] ]
@ -286,7 +283,7 @@ viewApp app readme =
loadApp : Cmd Msg loadApp : Cmd Msg
loadApp = loadApp =
Http.get Http.get
{ url = "https://recipes.coopcloud.tech/recipes.json" { url = "https://apps.coopcloud.tech/"
, expect = Http.expectJson GotApps appListDecoder , expect = Http.expectJson GotApps appListDecoder
} }
@ -307,37 +304,27 @@ loadREADME app =
in in
Http.get Http.get
-- FIXME use live Gitea link -- FIXME use live Gitea link
{ url = "https://git.coopcloud.tech/coop-cloud/" ++ app.slug ++ "/raw/branch/" ++ app.default_branch ++ "/README.md" { url = "https://cors-container.herokuapp.com/https://git.coopcloud.tech/coop-cloud/" ++ app.slug ++ "/raw/branch/" ++ app.default_branch ++ "/README.md"
, expect = Http.expectString GotText , expect = Http.expectString GotText
} }
featuresDecoder : Decode.Decoder String
featuresDecoder = featuresDecoder =
-- get features.status if it's there
Decode.oneOf Decode.oneOf
[ Decode.at [ "status" ] Decode.int [ Decode.at [ "status" ] Decode.string
, Decode.succeed 5 , Decode.succeed ""
] ]
versionsDecoder : Decode.Decoder (List String)
versionsDecoder =
Decode.list (Decode.keyValuePairs Decode.value)
|> Decode.map buildVersions
buildVersions : List (List ( String, Decode.Value )) -> List String
buildVersions versions =
List.concatMap (List.map (\( version, _ ) -> version)) versions
appDecoder : Decode.Decoder App appDecoder : Decode.Decoder App
appDecoder = appDecoder =
Decode.succeed App Decode.succeed App
|> andMap (Decode.field "name" Decode.string) |> andMap (Decode.field "name" Decode.string)
|> andMap (Decode.field "category" Decode.string) |> andMap (Decode.field "category" Decode.string)
|> andMap (Decode.maybe (Decode.field "repository" Decode.string)) |> andMap (Decode.maybe (Decode.field "repository" Decode.string))
|> andMap (Decode.at [ "versions" ] versionsDecoder) |> andMap (Decode.succeed Nothing)
-- |> andMap (Decode.succeed Nothing)
|> andMap (Decode.maybe (Decode.field "icon" Decode.string)) |> andMap (Decode.maybe (Decode.field "icon" Decode.string))
|> andMap (Decode.at [ "features" ] featuresDecoder) |> andMap (Decode.at [ "features" ] featuresDecoder)
|> andMap (Decode.succeed "") |> andMap (Decode.succeed "")

View File

@ -1,9 +1,8 @@
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, form, h2, h3, h5, i, img, input, label, li, option, p, select, span, text, ul) import Html exposing (Html, a, button, div, h2, h5, i, img, li, p, span, text, ul, form, label, select, option, input)
import Html.Attributes exposing (alt, class, classList, for, href, id, src, style, target, type_, value) import Html.Attributes exposing (alt, class, href, src, style, id, for, 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
@ -27,7 +26,6 @@ page =
} }
-- INIT -- INIT
@ -54,8 +52,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)
} }
@ -76,13 +74,14 @@ type Category
categories : Enum Category categories : Enum Category
categories = categories =
Enum.create Enum.create
[ ( "(Everything)", All ) [ ( "Apps", Apps )
, ( "Apps", Apps )
, ( "Utilities", Utilities ) , ( "Utilities", Utilities )
, ( "Development", Development ) , ( "Development", Development )
, ("All", All)
] ]
init : Url Params -> ( Model, Cmd Msg ) init : Url Params -> ( Model, Cmd Msg )
init { params } = init { params } =
( default_model, loadApps ) ( default_model, loadApps )
@ -95,14 +94,8 @@ default_image =
default_model : Model default_model : Model
default_model = default_model =
{ filter_score = Nothing { filter_score = Nothing, filter_category = Nothing, filter_text = Nothing,
, filter_category = Nothing status = Loading, apps = [], results = [] }
, filter_text = Nothing
, status = Loading
, apps = []
, results = []
}
-- UPDATE -- UPDATE
@ -120,55 +113,41 @@ 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 List.filter (\app ->
(\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 List.filter (\app ->
(\app -> app.category == categories.toString c
app.category == categories.toString c ) apps
)
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 List.filter (\app ->
(\app -> String.contains (String.toLower t) (
String.contains (String.toLower t) String.toLower app.name ++ String.toLower (
(String.toLower app.name Maybe.withDefault "" app.description
++ 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
@ -177,71 +156,51 @@ update msg model =
FilterScore filter -> FilterScore filter ->
let let
filter_score = filter_score = String.toInt filter
String.toInt filter
in in
( { model ( { model
| filter_score = filter_score | filter_score = filter_score
, results = , results = filterAppsScore (
filterAppsScore filterAppsCategory (
(filterAppsCategory filterAppsText model.apps model.filter_text
(filterAppsText model.apps model.filter_text) ) model.filter_category
model.filter_category ) (String.toInt filter)
) }, Cmd.none )
(String.toInt filter)
}
, Cmd.none
)
FilterCategory filter -> FilterCategory filter ->
let let
category = category = categories.fromString filter
categories.fromString filter
in in
( { model ( { model
| filter_category = category | filter_category = category
, results = , results = filterAppsCategory (
filterAppsCategory filterAppsScore (
(filterAppsScore filterAppsText model.apps model.filter_text
(filterAppsText model.apps model.filter_text) )model.filter_score
model.filter_score ) category
) }, Cmd.none )
category
}
, Cmd.none
)
FilterText filter -> FilterText filter ->
( { model ( { model
| filter_text = Just filter | filter_text = Just filter
, results = , results = filterAppsText (
filterAppsText filterAppsScore (
(filterAppsScore filterAppsCategory model.apps model.filter_category
(filterAppsCategory model.apps model.filter_category) ) model.filter_score
model.filter_score ) (Just filter)
) }, 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 , results = apps }, Cmd.none )
}
, Cmd.none
)
Err _ -> Err _ ->
( { default_model ( { default_model
| status = Failure | status = Failure}, Cmd.none )
}
, Cmd.none
)
subscriptions : Model -> Sub Msg subscriptions : Model -> Sub Msg
@ -255,14 +214,14 @@ subscriptions model =
view : Model -> Document Msg view : Model -> Document Msg
view model = view model =
{ title = "Co-op Cloud Recipes" { title = "abra recipes"
, body = [ body model ] , body = [ body model ]
} }
body : Model -> Html Msg body : Model -> Html Msg
body model = body model =
div [] div [ class "pt-3" ]
[ viewApps model [ viewApps model
] ]
@ -272,16 +231,16 @@ viewStatusBadge app =
let let
status_class = status_class =
case app.status of case app.status of
5 -> 1 ->
"badge-success" "badge-success"
4 -> 2 ->
"badge-info" "badge-info"
3 -> 3 ->
"badge-warning" "badge-warning"
2 -> 4 ->
"badge-danger" "badge-danger"
_ -> _ ->
@ -344,21 +303,16 @@ 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 ] ] [ span [ class "badge app-category" ] [ text app.category ]]
] ]
] ]
viewCategory : Model -> ( String, Category ) -> Html Msg
viewCategory model category = viewCategories : (String, Category) -> Html Msg
div viewCategories category =
[ classList div [ class "category-tile", onClick (FilterCategory (Tuple.first category)) ] [text (Tuple.first category)]
[ ( "category-tile", True )
, ( "active", categories.toString (Maybe.withDefault All model.filter_category) == Tuple.first category )
]
, onClick (FilterCategory (Tuple.first category))
]
[ text (Tuple.first category) ]
viewApps : Model -> Html Msg viewApps : Model -> Html Msg
@ -381,86 +335,52 @@ viewApps model =
Success -> Success ->
div [ class "row justify-content-center" ] div [ class "row justify-content-center" ]
[ div [ class "col-md-3", id "filter" ] [ div [ class "col-md-3" ] [
[ h2 [ class "app-headings" ] [ text "Finding things" ] h2 [ class "app-headings" ] [text "Categories"]
, form [] , div [] (List.map viewCategories categories.list)
[ div [] ]
[ h3 [] [ text "Search" ] , div [ class "col-md-6" ]
, input [ ariaLabel "search", id "text", onInput FilterText ] []
]
, div []
[ h3 [] [ text "Categories" ]
, div [] (List.map (viewCategory model) categories.list)
]
, div []
[ h3 [] [ text "Status" ]
, div []
[ label [] [ text "Minimum score: " ]
, a [ class "help", target "_blank", href "https://docs.coopcloud.tech/abra/recipes/#status-features-score" ] [ text "(help)" ]
]
, select [ class "search-dropdown", id "level", onInput FilterScore ]
[ option [] [ text "any" ]
, option [ value "5" ] [ text "5 (amazing)" ]
, option [ value "4" ] [ text "4 (good)" ]
, option [ value "3" ] [ text "3 (ok)" ]
, option [ value "2" ] [ text "2 (basic)" ]
, option [ value "1" ] [ text "1 (chaos)" ]
]
]
]
]
, div [ class "col-md-6 offset-md-3" ]
[ div [ class "row" ] [ div [ class "row" ]
[ div [ class "col-sm-12" ] [ div [ class "col-sm-12" ] [
[ div [] div [] [
[] form [ class "search-bar-container" ] [
] label [ for "text" ] [ text "Search" ]
] , input [ class "search-bar-input", id "text", onInput FilterText ] []
, div [ id "intro", class "card" ] , label [ for "level" ] [ text " and show only items that have at least " ]
[ div [ class "card-body" ] , select [ class "search-dropdown", id "level", onInput FilterScore ] [
[ h2 [ class "app-headings card-title" ] [ text "Co-op Cloud Recipes" ] option [ ] [ text "any" ]
, text "You can use these recipes (" , option [ value "1" ] [ text "1 (production)" ]
, a , option [ value "2" ] [ text "2 (beta)" ]
[ href , option [ value "3" ] [ text "3 (alpha)" ]
"https://docs.coopcloud.tech/intro/glossary/#recipe" , option [ value "4" ] [ text "4 (pre-alpha)" ]
] ]
[ text "What's a recipe?" , text "builds "
] ]
, text ") with " ]
, a [ href "https://coopcloud.tech" ]
[ text "Co-op Cloud"
]
, text "."
] ]
] ]
, h2 [ class "app-headings" ] [ text "Apps" ]
, div [ class "row" ] , div [ class "row" ]
(List.map viewApp (List.map viewApp
(model.results (model.results
|> List.sortWith |> List.sortWith
(by .status DESC (by .status ASC
|> andThen (String.toLower << .name) ASC |> andThen (String.toLower << .name) ASC
) )
) )
) )
] ]
, div [ class "col-md-3" ] [] , div [class "col-md-3"] []
] ]
-- HTTP -- HTTP
loadApps : Cmd Msg loadApps : Cmd Msg
loadApps = loadApps =
Http.request Http.get
{ url = "https://recipes.coopcloud.tech/recipes.json" { url = "https://apps.coopcloud.tech/"
, expect = Http.expectJson GotApps appListDecoder , expect = Http.expectJson GotApps appListDecoder
, headers = [ Http.header "Content-Type" "application/json" ]
, body = Http.emptyBody
, method = "GET"
, timeout = Nothing
, tracker = Nothing
} }

View File

@ -70,15 +70,15 @@ view { page, toMsg } model =
, body = , body =
[ div [ class "background" ] [ div [ class "background" ]
[ nav [ nav
[ class "navbar navbar-expand-lg sticky-top" ] [ class "navbar navbar-expand-lg sticky-top font-weight-bold" ]
[ a [ class "font-weight-bold navbar-text", href (Route.toString Route.Top) ] [ a [ class "navbar-text", href (Route.toString Route.Top) ]
[ img [ img
[ src "/logo-2.png" [ src "/logo-2.png"
, class "d-inline-block align-top mr-2" , class "d-inline-block align-top mr-2"
, alt "" , alt ""
, width 48 , width 48
, height 16 ] [] , height 16 ] []
, text "Co-op Cloud Recipes" , text "abra recipes"
] ]
, ul [ class "navbar-nav" ] , ul [ class "navbar-nav" ]
[ li [ class "nav-tem" ] [ a [ class "nav-link navbar-text", href (Route.toString Route.About) ] [ text "about" ] ] [ li [ class "nav-tem" ] [ a [ class "nav-link navbar-text", href (Route.toString Route.About) ] [ text "about" ] ]