51 Commits

Author SHA1 Message Date
3fff2db563 fix: link
Some checks failed
continuous-integration/drone/pr Build is failing
2025-01-20 10:50:44 +01:00
3wc
cc696ee0c8 Switch to self-hosted docker image
All checks were successful
continuous-integration/drone/push Build is passing
2024-12-05 07:49:28 -05:00
bbf8b3baa5 Merge pull request 'Update node Docker tag to v21 (main)' (#35) from renovate/main-node-21.x into main
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
Reviewed-on: coop-cloud/recipes.coopcloud.tech#35
2024-04-06 18:11:27 +00:00
3wc
a5230dfde3 And switch to correct server & secret..
All checks were successful
continuous-integration/drone/push Build is passing
2024-04-01 20:12:43 -03:00
3wc
9ab689f69e Switch to selfhosted stack-ssh-deploy
Some checks failed
continuous-integration/drone/push Build is failing
2024-04-01 20:10:42 -03:00
3wc
e566a6eef4 Add help link for status score
Some checks failed
continuous-integration/drone/push Build is failing
2024-04-01 20:08:46 -03:00
209cdc2770 Update dependency elm to v0.19.1-6
Some checks failed
renovate/artifacts Artifact file update failure
continuous-integration/drone/pr Build is failing
continuous-integration/drone/push Build is failing
2023-11-02 08:02:07 +00:00
2482caf2e0 Update node Docker tag to v21
Some checks failed
continuous-integration/drone/pr Build is failing
2023-10-20 07:02:07 +00:00
d15df0f9e4 Merge pull request 'Update node Docker tag to v20 (main)' (#34) from renovate/main-node-20.x into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: coop-cloud/recipes.coopcloud.tech#34
2023-04-24 08:14:37 +00:00
91f8340ac8 Update node Docker tag to v20
Some checks failed
continuous-integration/drone/pr Build is failing
2023-04-21 07:05:02 +00:00
3wc
6851d2fe1f Add indicator to show active category filter
All checks were successful
continuous-integration/drone/push Build is passing
Closes #33
2023-03-28 23:08:16 -04:00
3wc
e2f9cdd7e2 Tweak text
All checks were successful
continuous-integration/drone/push Build is passing
2023-03-28 21:11:53 -04:00
3wc
6ed6cbb33e Further filtering tweaks
All checks were successful
continuous-integration/drone/push Build is passing
2023-03-28 19:35:57 -04:00
41ccf212a9 Merge pull request 'Homepage re-jig' (#32) from filter-rejig into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: coop-cloud/recipes.coopcloud.tech#32
2023-03-28 17:42:59 +00:00
3wc
382d36d001 Tweak score names, add intro, stylin'
Some checks reported errors
continuous-integration/drone/pr Build was killed
2023-03-28 11:27:12 -04:00
3wc
2dec76fbfd elm-format
Some checks reported errors
continuous-integration/drone/pr Build was killed
2023-03-28 11:25:53 -04:00
3wc
377c8a7d04 Tweak text 'n style 2023-03-28 11:25:53 -04:00
3wc
f7ffc49b76 Some preliminary rejiggin' 2023-03-28 11:25:53 -04:00
3wc
338da444d8 Merge branch 'recipe-versions'
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2023-03-28 11:22:35 -04:00
3wc
57b4db9bb2 Add preliminary list of recipe versions
Some checks failed
continuous-integration/drone/pr Build is failing
2023-03-28 11:22:10 -04:00
3wc
70307aad9e Attempt at parsing "version" from JSON 2023-03-28 11:22:10 -04:00
af5565dcc5 Merge pull request 'Reinstate score badges, fix sorting' (#29) from recipe-scores into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: coop-cloud/recipes.coopcloud.tech#29
2023-03-28 15:20:22 +00:00
3wc
4a375f3d5f Make logos bigger on recipe detail page
All checks were successful
continuous-integration/drone/push Build is passing
2023-03-28 01:07:06 -04:00
3wc
d8b2192240 Reinstate score badges, fix sorting
Some checks failed
continuous-integration/drone/pr Build is failing
2023-03-26 18:47:47 -04:00
3wc
65ef1d2772 Switch back to Nginx, fix direct URLs
All checks were successful
continuous-integration/drone/push Build is passing
2023-03-23 13:43:43 -04:00
c50635319b Small CSS change to make recipe logo smaller on homepage
All checks were successful
continuous-integration/drone/push Build is passing
2023-03-23 17:20:04 +00:00
3wc
32366b7bf7 Whoops, wrong path
All checks were successful
continuous-integration/drone/push Build is passing
2023-01-25 10:36:14 -08:00
3wc
dbb701a2a4 Fix image (again)
All checks were successful
continuous-integration/drone/push Build is passing
2023-01-25 10:30:53 -08:00
3wc
b761f39247 Fix embarrassing deployment snafus
All checks were successful
continuous-integration/drone/push Build is passing
2023-01-24 09:04:19 -08:00
3wc
a7435c3d37 Switch to static deployment
Some checks reported errors
continuous-integration/drone/push Build was killed
Closes #9

Generates static HTML + JS version of the Elm app, and serves it using
thhtpd, for bonus permacomputing joy.
2023-01-22 20:41:30 -08:00
3wc
429cb371a8 Add AUTHORS 2023-01-22 20:28:35 -08:00
3wc
774c59bfa8 Update package-lock.json 2023-01-22 20:28:14 -08:00
3wc
39c3122230 Merge remote-tracking branches 'origin/renovate/main-chokidar-cli-3.x' and 'origin/renovate/main-elm-0.x' 2023-01-22 20:26:27 -08:00
3wc
a7ea4207ce Update attribution 2023-01-22 20:25:11 -08:00
6769455c27 Update dependency chokidar-cli to v3
Some checks failed
renovate/artifacts Artifact file update failure
continuous-integration/drone/pr Build is failing
2023-01-20 08:15:30 +00:00
984c205299 Update dependency elm to v0.19.1-5
Some checks failed
renovate/artifacts Artifact file update failure
continuous-integration/drone/pr Build is failing
2023-01-20 08:14:25 +00:00
5b28441102 Merge pull request 'Configure Renovate' (#19) from renovate/configure into main
All checks were successful
continuous-integration/drone/push Build is passing
Reviewed-on: coop-cloud/recipes.coopcloud.tech#19
2023-01-19 10:55:02 +00:00
d278ec2ad4 Add renovate.json
Some checks failed
continuous-integration/drone/pr Build is failing
2023-01-19 08:11:18 +00:00
3wc
3f10abe1c5 Fix Drone badge links
[ci skip]
2023-01-08 19:06:45 -08:00
3wc
1a7ade0f76 Drop CORS proxy
All checks were successful
continuous-integration/drone/push Build is passing
RE: #18
2023-01-08 19:04:39 -08:00
3wc
0777f7c008 Old URL work pls
All checks were successful
continuous-integration/drone/push Build is passing
continuous-integration/drone Build is passing
2022-05-08 17:43:36 +01:00
3wc
69a7fbc036 More URL fixin'
All checks were successful
continuous-integration/drone/push Build is passing
2022-05-04 10:30:54 +01:00
3wc
4c5e151615 Rename stack for manual deploy
All checks were successful
continuous-integration/drone/push Build is passing
2022-05-04 10:25:46 +01:00
3wc
9548d0715e Fix deployment
All checks were successful
continuous-integration/drone/push Build is passing
2022-05-04 10:25:32 +01:00
3wc
68669b7445 Add manual deploy instructions
Some checks reported errors
continuous-integration/drone/push Build was killed
2022-05-04 10:25:18 +01:00
1c306e8607 disable for now
Some checks reported errors
continuous-integration/drone/push Build was killed
2022-05-04 11:20:19 +02:00
375cafb946 more naming fixups
Some checks reported errors
continuous-integration/drone/push Build was killed
2022-05-04 11:19:28 +02:00
910d0bf2ae fixup that title
All checks were successful
continuous-integration/drone/push Build is passing
2022-05-04 11:16:14 +02:00
3wc
31f8a0be69 Add missing arguments to Http.expect 🤔🤔🤔
All checks were successful
continuous-integration/drone/push Build is passing
2022-05-04 09:58:33 +01:00
3wc
9c4522dceb Switch URL, upd8 README
Some checks failed
continuous-integration/drone/push Build is failing
2022-05-03 18:57:37 +01:00
83f0919cd8 Merge pull request 'Change dev.site to recipes.site' (#16) from em_change-site into main
Some checks reported errors
continuous-integration/drone/push Build was killed
Reviewed-on: coop-cloud/recipes.coopcloud.tech#16
2022-05-03 16:06:46 +00:00
17 changed files with 651 additions and 435 deletions

View File

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

8
AUTHORS.md Normal file
View File

@ -0,0 +1,8 @@
# 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:12-alpine FROM node:21-alpine AS build
RUN mkdir /code RUN mkdir /code
WORKDIR /code WORKDIR /code
@ -11,5 +11,10 @@ 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 . .
ENTRYPOINT ["/usr/local/bin/npm"] RUN ["npm", "run", "build"]
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
> WIP! https://dev.apps.coopcloud.tech is the current live site > https://recipes.coopcloud.tech is the current live site
[![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) [![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)
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,6 +33,8 @@ __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,25 +3,28 @@ version: "3.8"
services: services:
app: app:
image: "3wordchant/abra-apps:latest" image: "git.coopcloud.tech/coop-cloud/recipes.coopcloud.tech: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-apps-dev.loadbalancer.server.port=8000" - "traefik.http.services.abra-recipes.loadbalancer.server.port=80"
- "traefik.http.routers.abra-apps-dev.rule=Host(`recipes.coopcloud.tech`)" - "traefik.http.routers.abra-recipes.rule=(Host(`dev.apps.coopcloud.tech`)||Host(`recipes.coopcloud.tech`))"
- "traefik.http.routers.abra-apps-dev.entrypoints=web-secure" - "traefik.http.routers.abra-recipes.entrypoints=web-secure"
- "traefik.http.routers.abra-apps-dev.tls.certresolver=production" - "traefik.http.routers.abra-recipes.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,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": {

View File

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

17
nginx.conf Normal file
View File

@ -0,0 +1,17 @@
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;
}
}

560
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -22,14 +22,14 @@
"author": "", "author": "",
"license": "ISC", "license": "ISC",
"devDependencies": { "devDependencies": {
"chokidar-cli": "2.1.0", "chokidar-cli": "3.0.0",
"elm": "0.19.1-3", "elm": "0.19.1-6",
"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.5" "elm-format": "^0.8.6"
} }
} }

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>abra recipes</title> <title>Co-op Cloud 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">
abra recipes</a> Co-op Cloud 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>
abra recipes</h2> Co-op Cloud Recipes</h2>
</div> </div>
<div class="card-body"> <div class="card-body">
<p> <p>

View File

@ -29,16 +29,51 @@ 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;
}
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 */
@ -46,6 +81,7 @@ i.fas, i.fab {
.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 {
@ -73,10 +109,14 @@ i.fas, i.fab {
.card-img-top { .card-img-top {
width: 100%; width: 100%;
height: 16vw; height: 5vw;
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;
@ -95,31 +135,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);
@ -132,7 +147,7 @@ i.fas, i.fab {
} }
@media (min-width: 768px) { @media (min-width: 768px) {
.card-body p { .smaller-card .card-body p {
height: 2.5rem; height: 2.5rem;
} }
} }
@ -141,3 +156,10 @@ i.fas, i.fab {
.card-body p { .card-body p {
} }
} }
@media (max-width: 768px) {
#filter {
width: 100%;
position: relative;
margin-bottom: 1rem;
}

3
renovate.json Normal file
View File

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

View File

@ -37,14 +37,15 @@ view { params } =
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/coop-cloud/abra-apps" ] [ 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 abra recipes" { title = "About Co-op Cloud 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 "abra recipes" ] [ h2 [] [ text "Co-op Cloud Recipes" ]
] ]
, div [ class "card-body" ] , div [ class "card-body" ]
[ p [] [ p []
@ -52,7 +53,7 @@ view { params } =
text "a lil' " text "a lil' "
, text " tool to display " , text " tool to display "
, coopcloud_link , coopcloud_link
, text " apps." , text " recipes."
] ]
, p [] , p []
[ [
@ -63,7 +64,9 @@ view { params } =
] ]
, div [ class "card-footer" ] , div [ class "card-footer" ]
[ p [] [ p []
[ text "by @3wc (" [ text "made with 🤪 at "
, 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 : Maybe (List String) , versions : List String
, icon : Maybe String , icon : Maybe String
, status : String , status : Int
, 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 abra recipes" "loading Co-op Cloud Recipes"
Failure -> Failure ->
"error - abra recipes" "error - Co-op Cloud Recipes"
Success app -> Success app ->
app.name ++ " abra recipes" app.name ++ " Co-op Cloud 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
"1" -> 5 ->
"badge-success" "badge-success"
"2" -> 4 ->
"badge-info" "badge-info"
"3" -> 3 ->
"badge-warning" "badge-warning"
"4" -> 2 ->
"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: " ++ app.status) ] [ text ("Score: " ++ String.fromInt app.status) ]
viewApp : App -> String -> Html Msg viewApp : App -> String -> Html Msg
@ -265,13 +265,16 @@ viewApp app readme =
, repository_link , repository_link
, website_link , website_link
] ]
, img [ class "card-img-top", src icon_url, alt ("icon for " ++ app.name) ] [] , img [ class "card-img-top card-img-large", 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 ]) app.versions)
]
] ]
] ]
@ -283,7 +286,7 @@ viewApp app readme =
loadApp : Cmd Msg loadApp : Cmd Msg
loadApp = loadApp =
Http.get Http.get
{ url = "https://apps.coopcloud.tech/" { url = "https://recipes.coopcloud.tech/recipes.json"
, expect = Http.expectJson GotApps appListDecoder , expect = Http.expectJson GotApps appListDecoder
} }
@ -304,27 +307,37 @@ loadREADME app =
in in
Http.get Http.get
-- FIXME use live Gitea link -- FIXME use live Gitea link
{ url = "https://cors-container.herokuapp.com/https://git.coopcloud.tech/coop-cloud/" ++ app.slug ++ "/raw/branch/" ++ app.default_branch ++ "/README.md" { url = "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.string [ Decode.at [ "status" ] Decode.int
, Decode.succeed "" , Decode.succeed 5
] ]
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.succeed Nothing) |> andMap (Decode.at [ "versions" ] versionsDecoder)
-- |> 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,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, classList, for, href, id, src, style, target, type_, 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
} }
@ -74,14 +76,13 @@ type Category
categories : Enum Category categories : Enum Category
categories = categories =
Enum.create Enum.create
[ ( "Apps", Apps ) [ ( "(Everything)", All )
, ( "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 )
@ -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.status <= s (\app ->
) apps app.status >= s
)
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 ->
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 (\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,39 +177,53 @@ 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
@ -196,11 +231,17 @@ update msg model =
( { 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
@ -214,14 +255,14 @@ subscriptions model =
view : Model -> Document Msg view : Model -> Document Msg
view model = view model =
{ title = "abra recipes" { title = "Co-op Cloud Recipes"
, body = [ body model ] , body = [ body model ]
} }
body : Model -> Html Msg body : Model -> Html Msg
body model = body model =
div [ class "pt-3" ] div []
[ viewApps model [ viewApps model
] ]
@ -231,16 +272,16 @@ viewStatusBadge app =
let let
status_class = status_class =
case app.status of case app.status of
1 -> 5 ->
"badge-success" "badge-success"
2 -> 4 ->
"badge-info" "badge-info"
3 -> 3 ->
"badge-warning" "badge-warning"
4 -> 2 ->
"badge-danger" "badge-danger"
_ -> _ ->
@ -303,16 +344,21 @@ 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" ]
[ span [ class "badge app-category" ] [ text app.category ]] [ viewStatusBadge app, span [ class "badge app-category" ] [ text app.category ] ]
] ]
] ]
viewCategory : Model -> ( String, Category ) -> Html Msg
viewCategories : (String, Category) -> Html Msg viewCategory model category =
viewCategories category = div
div [ class "category-tile", onClick (FilterCategory (Tuple.first category)) ] [text (Tuple.first category)] [ classList
[ ( "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
@ -335,52 +381,86 @@ 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 []
[ h3 [] [ text "Search" ]
, input [ ariaLabel "search", id "text", onInput FilterText ] []
] ]
, div [ class "col-md-6" ] , 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 ] []
, label [ for "level" ] [ text " and show only items that have at least " ]
, select [ class "search-dropdown", id "level", onInput FilterScore ] [
option [ ] [ text "any" ]
, option [ value "1" ] [ text "1 (production)" ]
, option [ value "2" ] [ text "2 (beta)" ]
, option [ value "3" ] [ text "3 (alpha)" ]
, option [ value "4" ] [ text "4 (pre-alpha)" ]
]
, text "builds "
] ]
] ]
, div [ id "intro", class "card" ]
[ div [ class "card-body" ]
[ h2 [ class "app-headings card-title" ] [ text "Co-op Cloud Recipes" ]
, text "You can use these recipes ("
, a
[ href
"https://docs.coopcloud.tech/intro/glossary/#recipe"
]
[ text "What's a recipe?"
]
, 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 ASC (by .status DESC
|> 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.get Http.request
{ url = "https://apps.coopcloud.tech/" { url = "https://recipes.coopcloud.tech/recipes.json"
, 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 font-weight-bold" ] [ class "navbar navbar-expand-lg sticky-top" ]
[ a [ class "navbar-text", href (Route.toString Route.Top) ] [ a [ class "font-weight-bold 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 "abra recipes" , text "Co-op Cloud 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" ] ]