diff --git a/.gitignore b/.gitignore index 66bca12..9bec4dc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ .env -venv/ +venv/ \ No newline at end of file diff --git a/README.md b/README.md index 899058f..1dde707 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,22 @@ ![frog](cover.jpg) -A way to archive and retrieve all our pads. Now with a fast way to insert new pads without too much effort uiii. +A way to create, archive and retrieve our pads. Now with a fast way to insert new pads without too much effort uiii. + + +# Features + +- Custom categories +- Filter by categories +- Table sorting +- Create new pad if URL is not provided + +# TODO + +- Setup script +- Search +- Server side rendering? + ![lifecycle](lifecycle.jpg) diff --git a/client/style.css b/client/style.css deleted file mode 100644 index ead13bd..0000000 --- a/client/style.css +++ /dev/null @@ -1,245 +0,0 @@ -:root { - --bg: dodgerblue; - --color: white; -} - -* { - box-sizing: border-box; -} - -html, -body { - font-family: Arial, Helvetica, sans-serif; - line-height: 1.6; - color: var(--color); - background-color: var(--bg); -} - -a { - color: currentColor; -} - -.version { - font-size: 18px; - font-weight: normal; -} - -input { - color: currentColor; - border: 1px solid currentColor; - outline: none; - padding: 0.4em; - margin: 0; - background: none; -} - -input.overview { - width: 60ch; -} - -button { - display: inline-block; - background: none; - border: 1px solid currentColor; - border-radius: 50%; - color: currentColor; - line-height: 1rem; - text-align: center; - padding: 0.4em; - cursor: pointer; -} - -button.add { - display: inline-block; - width: 2em; - height: 2em; - font-size: 1em; - margin-left: 8px; -} - -button.submit { - margin-left: 12px; - - padding: 0.6em; - font-size: 1em; -} - -button:hover { - background-color: var(--color); - color: var(--bg); -} - -*:not(h2) + input { - margin-left: 12px; -} - -.app-form { - display: inline-block; - padding-left: 12px; - border-left: 1px solid currentColor; - margin-top: 12px; - margin-bottom: 12px; -} - -.app-form h2 { - margin: 0; - font-weight: normal; -} - -table { - width: 100%; -} - -table a { - text-decoration: none; -} - -tr.header { - position: sticky; - top: 0; - background-color: var(--bg); - z-index: 100; -} - -th { - text-align: left; - padding: 24px 0; - border-bottom: 1px solid currentColor; - text-transform: capitalize; - font-size: 21px; - user-select: none; - cursor: pointer; -} - -td { - padding: 12px; - border-bottom: 1px solid currentColor; -} - -td.title { - font-weight: bold; -} - -td.overview, -th.overview { - flex-grow: 3; -} - -td.overview p { - margin: 0; -} - -tr { - position: relative; - transition: transform 0.1s ease-in; -} - -tr:hover:not(:first-of-type) { - transition: transform 0.2s ease-out; - transform: translateX(10px); -} - -.stretched:after { - content: ""; - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; -} - -.loading { - font-size: 28px; -} - -.grow { - animation: grow 5s; -} - -.filter { - padding-left: 12px; - border-left: 1px solid currentColor; - margin-top: 32px; - margin-bottom: 48px; -} - -.filter h2 { - font-weight: normal; - margin: 0; - margin-bottom: 8px; -} - -.filter ul { - display: flex; - flex-wrap: wrap; - list-style: none; - gap: 8px; - margin: 0; - padding: 0; -} - -.filter ul li, -.category { - display: inline-block; - border: 1px solid currentColor; - padding: 0 4px; - user-select: none; - cursor: pointer; -} - -.filter .active { - background-color: var(--color); - color: var(--bg); -} - -tr { - display: flex; - justify-content: space-between; -} - -th, -td { - padding: 12px 12px; -} - -th:first-of-type, -td:first-child { - padding-left: 0; -} - -th:last-of-type, -td:last-child { - padding-right: 0; -} - -th.date, -td.date { - flex-grow: 0.4; - margin-left: auto; -} - -tr > * { - flex: 1; -} - -td.categories { - align-content: flex-start; - - display: flex; - flex-wrap: wrap; - gap: 8px; - height: auto; -} - -.categories .category { - border: none; -} - -@keyframes grow { - from { - transform: scale(100%); - } - to { - transform: scale(0, 1000%) rotate(1turn); - } -} diff --git a/lifecycle.jpg b/lifecycle.jpg index 3a79b24..ea38237 100644 Binary files a/lifecycle.jpg and b/lifecycle.jpg differ diff --git a/pad-bis.py b/pad-bis.py index 5ef4b87..e9399e7 100644 --- a/pad-bis.py +++ b/pad-bis.py @@ -1,5 +1,5 @@ # Flask application to serve the web pages -from flask import Flask, request, redirect, url_for, jsonify +from flask import Flask, request, redirect, url_for, jsonify, render_template from flask_cors import CORS # Mediawiki client to interact with the Wiki @@ -14,10 +14,6 @@ import os from dotenv import load_dotenv from pathlib import Path -# datetime to work with dates -from datetime import datetime - - # load the mediawiki credentials from the shared folder dotenv_path = Path("/var/www/.mw-credentials") load_dotenv(dotenv_path=dotenv_path) @@ -47,11 +43,14 @@ app = Flask(__name__) CORS(app) -# Url prefix for the soupboat +# Url prefix for the soupboat and port base_url = os.environ.get('BASE_URL', '') +port = os.environ.get('FLASK_RUN_PORT', '') + # Page of the wiki with the pads -padliography = 'Padliography2' +padliography = os.environ.get('PAGE', '') + # register the middleware to prefix all the requests with our base_url app.wsgi_app = PrefixMiddleware(app.wsgi_app, prefix=base_url) @@ -98,8 +97,13 @@ def get_pads(): return pads -@app.route('/', methods=['GET', 'POST']) + +@app.route('/') def home(): + return render_template('home.html') + +@app.route('/api/', methods=['GET', 'POST']) +def api(): if request.method == 'POST': link = request.json.get('link', None) @@ -108,8 +112,8 @@ def home(): categories = request.json.get('categories', '') date = request.json.get('date', None) - date = datetime.strftime(datetime.strptime( - date, 'yyyy-mm-dd'), 'dd-mm-yyyy') + # date = datetime.strftime(datetime.strptime( + # date, '%Y-%m-%d'), '%d-%m-%Y') add_pad(link, title, overview, categories, date) redirect(url_for('home')) @@ -121,4 +125,4 @@ def home(): return jsonify(response) -app.run(port=3147) +app.run(port=port) diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..e2e1018 --- /dev/null +++ b/setup.py @@ -0,0 +1,16 @@ +from setuptools import find_packages, setup + +setup( + name='Padliography', + version='2.0.0', + packages=find_packages(), + include_package_data=True, + zip_safe=False, + install_requires=[ + 'flask', + 'flask_cors', + 'bs4', + 'mwclient', + 'python-dotenv', + ], +) diff --git a/static/css/style.css b/static/css/style.css index 50c6be5..ead13bd 100644 --- a/static/css/style.css +++ b/static/css/style.css @@ -1,124 +1,245 @@ -body { - font-size: 1.5rem; - margin: 0; - background: rgb(255, 244, 235); +:root { + --bg: dodgerblue; + --color: white; } -.contents { - margin: 32px; +* { + box-sizing: border-box; } -h1 { - color: #ecab72; - margin: 32px; - text-align: center; +html, +body { + font-family: Arial, Helvetica, sans-serif; + line-height: 1.6; + color: var(--color); + background-color: var(--bg); } a { color: currentColor; } -a.stretched-link::after { - content: ""; - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - z-index: 100; +.version { + font-size: 18px; + font-weight: normal; } -/* FILTERS */ - -.tag { +input { + color: currentColor; + border: 1px solid currentColor; + outline: none; + padding: 0.4em; + margin: 0; background: none; - font-size: 20px; +} + +input.overview { + width: 60ch; +} + +button { display: inline-block; + background: none; border: 1px solid currentColor; - padding: 0 0.5em; - border-radius: 1em; - margin: 4px; - text-transform: capitalize; - user-select: none; + border-radius: 50%; + color: currentColor; + line-height: 1rem; + text-align: center; + padding: 0.4em; cursor: pointer; +} - transform: translateY(0); - transition: transform 0.4s ease-in; +button.add { + display: inline-block; + width: 2em; + height: 2em; + font-size: 1em; + margin-left: 8px; } -.tag.active { - background-color: white; +button.submit { + margin-left: 12px; + + padding: 0.6em; + font-size: 1em; } -.tag.active.all { - background: none; +button:hover { + background-color: var(--color); + color: var(--bg); } -.filters .tag:focus, -.filters .tag:hover { - transform: translateY(-4px); - transition: transform 0.2s ease-out; +*:not(h2) + input { + margin-left: 12px; } -ul { - padding: 0; +.app-form { + display: inline-block; + padding-left: 12px; + border-left: 1px solid currentColor; + margin-top: 12px; + margin-bottom: 12px; } -/* TABLE */ +.app-form h2 { + margin: 0; + font-weight: normal; +} table { - border-collapse: collapse; + width: 100%; +} + +table a { + text-decoration: none; +} + +tr.header { + position: sticky; + top: 0; + background-color: var(--bg); + z-index: 100; +} + +th { + text-align: left; + padding: 24px 0; + border-bottom: 1px solid currentColor; + text-transform: capitalize; + font-size: 21px; + user-select: none; + cursor: pointer; +} + +td { + padding: 12px; + border-bottom: 1px solid currentColor; +} + +td.title { + font-weight: bold; +} + +td.overview, +th.overview { + flex-grow: 3; +} + +td.overview p { + margin: 0; } tr { - /* display: none; */ position: relative; + transition: transform 0.1s ease-in; +} + +tr:hover:not(:first-of-type) { + transition: transform 0.2s ease-out; + transform: translateX(10px); } -tr:nth-child(even) { - background-color: rgb(251, 237, 225); +.stretched:after { + content: ""; + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; } -tr.active { - display: table-row; +.loading { + font-size: 28px; } -tr:hover { - background-color: rgb(250, 232, 217); +.grow { + animation: grow 5s; } -tr .tag { - cursor: default; +.filter { + padding-left: 12px; + border-left: 1px solid currentColor; + margin-top: 32px; + margin-bottom: 48px; } -tr.header { - display: table-row; - text-align: left; +.filter h2 { + font-weight: normal; + margin: 0; + margin-bottom: 8px; +} + +.filter ul { + display: flex; + flex-wrap: wrap; + list-style: none; + gap: 8px; + margin: 0; + padding: 0; } -.categories { - width: 20%; +.filter ul li, +.category { + display: inline-block; + border: 1px solid currentColor; + padding: 0 4px; + user-select: none; + cursor: pointer; } -.overview { - font-style: italic; - width: 40%; +.filter .active { + background-color: var(--color); + color: var(--bg); } -.date { - width: 14ch; +tr { + display: flex; + justify-content: space-between; } -form { - margin: 32px; - display: inline-block; - padding: 2rem; - background-color: rgb(250, 232, 217); - border-radius: 50%; +th, +td { + padding: 12px 12px; } -form h2 { - margin: 0; - margin-bottom: 16px; - font-size: 28px; +th:first-of-type, +td:first-child { + padding-left: 0; +} + +th:last-of-type, +td:last-child { + padding-right: 0; +} + +th.date, +td.date { + flex-grow: 0.4; + margin-left: auto; +} + +tr > * { + flex: 1; +} + +td.categories { + align-content: flex-start; + + display: flex; + flex-wrap: wrap; + gap: 8px; + height: auto; +} + +.categories .category { + border: none; +} + +@keyframes grow { + from { + transform: scale(100%); + } + to { + transform: scale(0, 1000%) rotate(1turn); + } } diff --git a/client/components/PadForm.js b/static/js/components/PadForm.js similarity index 95% rename from client/components/PadForm.js rename to static/js/components/PadForm.js index 2a1eea2..2414e08 100644 --- a/client/components/PadForm.js +++ b/static/js/components/PadForm.js @@ -25,14 +25,14 @@ export default { link.value = newPad(); } - fetch("https://hub.xpub.nl/soupboat/padliography/", { + fetch("api", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ title: title.value.trim(), link: link.value, overview: overview.value, - categories: categories.value, + categories: categories.value.join(", "), date: date.value, }), }); diff --git a/client/components/PadTable.js b/static/js/components/PadTable.js similarity index 97% rename from client/components/PadTable.js rename to static/js/components/PadTable.js index 400bac5..481b95a 100644 --- a/client/components/PadTable.js +++ b/static/js/components/PadTable.js @@ -16,11 +16,11 @@ export default { selected.value.has(tag) ? selected.value.delete(tag) : selected.value.add(tag); }; - // fetch("https://hub.xpub.nl/soupboat/padliography/") - fetch("http://127.0.0.1:3147/") + fetch("api") .then((res) => res.json()) .then((data) => { pads.value = data.pads; + pads.value.map( (pad) => (pad.categories = pad.categories diff --git a/client/index.html b/templates/home.html similarity index 70% rename from client/index.html rename to templates/home.html index cc67b68..a8672e0 100644 --- a/client/index.html +++ b/templates/home.html @@ -4,7 +4,7 @@ - + Padliography @@ -16,8 +16,8 @@