Compare commits
No commits in common. "main" and "em_change-site" have entirely different histories.
main
...
em_change-
19
.drone.yml
19
.drone.yml
@ -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
|
||||||
|
@ -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
|
|
11
Dockerfile
11
Dockerfile
@ -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
|
|
||||||
|
@ -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
|
||||||
|
|
||||||
[](https://build.coopcloud.tech/coop-cloud/recipes.coopcloud.tech)
|
[](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)
|
||||||
|
25
compose.yml
25
compose.yml
@ -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:
|
||||||
|
1
elm.json
1
elm.json
@ -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": {
|
||||||
|
4
makefile
4
makefile
@ -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
|
||||||
|
17
nginx.conf
17
nginx.conf
@ -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
654
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
10
package.json
10
package.json
@ -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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -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>
|
||||||
|
@ -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;
|
|
||||||
}
|
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "https://docs.renovatebot.com/renovate-schema.json"
|
|
||||||
}
|
|
@ -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 ")"
|
||||||
]
|
]
|
||||||
|
@ -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 "")
|
||||||
|
@ -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,53 +156,39 @@ 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
|
||||||
@ -231,17 +196,11 @@ update msg model =
|
|||||||
( { 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" ]
|
|
||||||
, input [ ariaLabel "search", id "text", onInput FilterText ] []
|
|
||||||
]
|
]
|
||||||
, div []
|
, div [ class "col-md-6" ]
|
||||||
[ 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 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
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -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" ] ]
|
||||||
|
Loading…
x
Reference in New Issue
Block a user