Compare commits

...

4 commits

Author SHA1 Message Date
Halfdan Mouritzen 02907a7684 Minimal CSS for tables 2024-01-13 22:14:23 +01:00
valberg 33f5e7a285 ui-overhaul (#24)
Co-authored-by: Halfdan <halm@itu.dk>
Co-authored-by: Halfdan Mouritzen <halfdan@robothangarskib.dk>
Reviewed-on: data.coop/membersystem#24
2024-01-13 20:07:36 +00:00
Víðir Valberg Guðmundsson 2ed5df5241 Add pyproject.toml file. 2024-01-13 15:39:31 +01:00
Víðir Valberg Guðmundsson 9b52cd4270 Update stuff. 2024-01-07 15:04:08 +01:00
62 changed files with 1099 additions and 475 deletions

1
.gitignore vendored
View file

@ -7,3 +7,4 @@ db.sqlite3
*.mo
.env
venv/
.venv/

View file

@ -3,7 +3,7 @@ default_language_version:
exclude: ^.*\b(migrations)\b.*$
repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
rev: v4.5.0
hooks:
- id: check-ast
- id: check-merge-conflict
@ -16,13 +16,13 @@ repos:
- id: end-of-file-fixer
- id: trailing-whitespace
- repo: https://github.com/charliermarsh/ruff-pre-commit
rev: 'v0.0.292'
rev: 'v0.1.11'
hooks:
- id: ruff
args:
- --fix
- repo: https://github.com/asottile/reorder_python_imports
rev: v3.11.0
rev: v3.12.0
hooks:
- id: reorder-python-imports
args:
@ -30,7 +30,7 @@ repos:
- --application-directories=.:src
exclude: migrations/
- repo: https://github.com/asottile/pyupgrade
rev: v3.13.0
rev: v3.15.0
hooks:
- id: pyupgrade
args:
@ -51,10 +51,10 @@ repos:
hooks:
- id: add-trailing-comma
- repo: https://github.com/hadialqattan/pycln
rev: v2.2.2
rev: v2.4.0
hooks:
- id: pycln
- repo: https://github.com/psf/black
rev: 23.9.1
rev: 23.12.1
hooks:
- id: black

105
pyproject.toml Normal file
View file

@ -0,0 +1,105 @@
[build-system]
requires = ["hatchling", "hatch-vcs"]
build-backend = "hatchling.build"
[project]
name = "membersystem"
description = ''
readme = "README.md"
requires-python = ">=3.11"
keywords = []
authors = [
{ name = "Víðir Valberg Guðmundsson", email = "valberg@orn.li" },
]
dependencies = [
"Django==5.0.1",
"django-money==3.4.1",
"django-allauth==0.60.0",
"psycopg[binary]==3.1.16",
"environs[django]==10.0.0",
"uvicorn==0.25.0",
"whitenoise==6.6.0",
"django-zen-queries==2.1.0",
"django-registries==0.0.3",
]
dynamic = ["version"]
[tool.hatch.version]
source = "vcs"
[tool.hatch.envs.default]
dependencies = [
"coverage[toml]==7.3.0",
"pytest==7.2.2",
"pytest-cov",
"pytest-django==4.5.2",
"mypy==1.1.1",
"django-stubs==1.16.0",
"pip-tools==7.3.0",
"django-debug-toolbar==4.2.0",
"django-browser-reload==1.7.0",
"model-bakery==1.17.0",
]
[[tool.hatch.envs.tests.matrix]]
python = ["3.12"]
django = ["4.2", "5.0"]
[tool.hatch.envs.tests.overrides]
matrix.django.dependencies = [
{ value = "django~={matrix:django}" },
]
matrix.python.dependencies = [
{ value = "typing_extensions==4.5.0", if = ["3.10"]},
]
[tool.hatch.envs.default.scripts]
cov = "pytest --cov-report=term-missing --cov-config=pyproject.toml --cov=src --cov=tests --cov=append {args}"
no-cov = "cov --no-cov {args}"
typecheck = "mypy --config-file=pyproject.toml ."
requirements = "pip-compile --output-file requirements/base.txt pyproject.toml"
server = "./src/manage.py runserver"
migrate = "./src/manage.py migrate"
makemigrations = "./src/manage.py makemigrations"
createsuperuser = "./src/manage.py createsuperuser"
shell = "./src/manage.py shell"
[tool.pytest.ini_options]
DJANGO_SETTINGS_MODULE="tests.settings"
addopts = "--reuse-db"
norecursedirs = "build dist docs .eggs/* *.egg-info htmlcov .git"
python_files = "test*.py"
testpaths = "tests"
pythonpath = ". tests"
[tool.coverage.run]
branch = true
parallel = true
[tool.coverage.report]
exclude_lines = [
"no cov",
"if __name__ == .__main__.:",
"if TYPE_CHECKING:",
]
[tool.mypy]
mypy_path = "src/"
exclude = [
"venv/",
"dist/",
"docs/",
]
namespace_packages = false
show_error_codes = true
strict = true
warn_unreachable = true
follow_imports = "normal"
#plugins = ["mypy_django_plugin.main"]
[tool.django-stubs]
#django_settings_module = "tests.settings"
[[tool.mypy.overrides]]
module = "tests.*"
allow_untyped_defs = true

View file

@ -1,8 +1,8 @@
Django==4.2.5
django-money==3.3.0
django-allauth==0.57.0
psycopg[binary]==3.1.12
environs[django]==9.5
uvicorn==0.23.2
whitenoise==6.5.0
Django==5.0.1
django-money==3.4.1
django-allauth==0.60.0
psycopg[binary]==3.1.16
environs[django]==10.0.0
uvicorn==0.25.0
whitenoise==6.6.0
django-zen-queries==2.1.0

View file

@ -2,89 +2,97 @@
# This file is autogenerated by pip-compile with Python 3.11
# by the following command:
#
# pip-compile --output-file=requirements/base.txt requirements/base.in
# pip-compile --output-file=requirements/base.txt pyproject.toml
#
asgiref==3.7.2
# via django
babel==2.12.1
babel==2.14.0
# via py-moneyed
certifi==2022.9.24
certifi==2023.11.17
# via requests
cffi==1.15.1
cffi==1.16.0
# via cryptography
charset-normalizer==2.1.1
charset-normalizer==3.3.2
# via requests
click==7.1.2
click==8.1.7
# via uvicorn
cryptography==38.0.3
cryptography==41.0.7
# via pyjwt
defusedxml==0.7.1
# via python3-openid
dj-database-url==1.0.0
dj-database-url==2.1.0
# via environs
dj-email-url==1.0.6
# via environs
django==4.2.5
django==5.0.1
# via
# -r requirements/base.in
# dj-database-url
# django-allauth
# django-money
# django-registries
# django-zen-queries
django-allauth==0.57.0
# via -r requirements/base.in
django-cache-url==3.4.2
# membersystem (pyproject.toml)
django-allauth==0.60.0
# via membersystem (pyproject.toml)
django-cache-url==3.4.5
# via environs
django-money==3.3.0
# via -r requirements/base.in
django-money==3.4.1
# via membersystem (pyproject.toml)
django-registries==0.0.3
# via membersystem (pyproject.toml)
django-zen-queries==2.1.0
# via -r requirements/base.in
environs[django]==9.5.0
# via -r requirements/base.in
# via membersystem (pyproject.toml)
environs[django]==10.0.0
# via
# environs
# membersystem (pyproject.toml)
h11==0.14.0
# via uvicorn
idna==3.4
idna==3.6
# via requests
marshmallow==3.19.0
marshmallow==3.20.1
# via environs
oauthlib==3.2.2
# via requests-oauthlib
packaging==21.3
packaging==23.2
# via marshmallow
psycopg[binary]==3.1.12
# via -r requirements/base.in
psycopg-binary==3.1.12
psycopg[binary]==3.1.16
# via
# membersystem (pyproject.toml)
# psycopg
psycopg-binary==3.1.16
# via psycopg
py-moneyed==3.0
# via django-money
pycparser==2.21
# via cffi
pyjwt[crypto]==2.6.0
# via django-allauth
pyparsing==3.0.9
# via packaging
python-dotenv==0.21.0
pyjwt[crypto]==2.8.0
# via
# django-allauth
# pyjwt
python-dotenv==1.0.0
# via environs
python3-openid==3.2.0
# via django-allauth
requests==2.28.1
requests==2.31.0
# via
# django-allauth
# requests-oauthlib
requests-oauthlib==1.3.1
# via django-allauth
sqlparse==0.4.3
sqlparse==0.4.4
# via django
typing-extensions==4.8.0
typing-extensions==4.9.0
# via
# dj-database-url
# psycopg
# py-moneyed
urllib3==1.26.12
urllib3==2.1.0
# via requests
uvicorn==0.23.2
# via -r requirements/base.in
whitenoise==6.5.0
# via -r requirements/base.in
uvicorn==0.25.0
# via membersystem (pyproject.toml)
whitenoise==6.6.0
# via membersystem (pyproject.toml)
# The following packages are considered to be unsafe in a requirements file:
# setuptools

View file

@ -1,8 +1,8 @@
-r test.txt
django-browser-reload==1.11.0
django-browser-reload==1.12.1
django-debug-toolbar==4.2.0
django-extensions==3.2.3
django-stubs==1.12.0
ipython==8.6.0
mypy==1.5.1
django-stubs==4.2.7
ipython==8.19.0
mypy==1.8.0

View file

@ -8,33 +8,32 @@ asgiref==3.7.2
# via
# -r requirements/test.txt
# django
asttokens==2.1.0
# django-browser-reload
asttokens==2.4.1
# via stack-data
babel==2.12.1
babel==2.14.0
# via
# -r requirements/test.txt
# py-moneyed
backcall==0.2.0
# via ipython
certifi==2022.9.24
certifi==2023.11.17
# via
# -r requirements/test.txt
# requests
cffi==1.15.1
cffi==1.16.0
# via
# -r requirements/test.txt
# cryptography
charset-normalizer==2.1.1
charset-normalizer==3.3.2
# via
# -r requirements/test.txt
# requests
click==7.1.2
click==8.1.7
# via
# -r requirements/test.txt
# uvicorn
coverage==7.3.1
coverage==7.4.0
# via -r requirements/test.txt
cryptography==38.0.3
cryptography==41.0.7
# via
# -r requirements/test.txt
# pyjwt
@ -44,7 +43,7 @@ defusedxml==0.7.1
# via
# -r requirements/test.txt
# python3-openid
dj-database-url==1.0.0
dj-database-url==2.1.0
# via
# -r requirements/test.txt
# environs
@ -52,7 +51,7 @@ dj-email-url==1.0.6
# via
# -r requirements/test.txt
# environs
django==4.2.5
django==5.0.1
# via
# -r requirements/test.txt
# dj-database-url
@ -64,11 +63,11 @@ django==4.2.5
# django-stubs
# django-stubs-ext
# django-zen-queries
django-allauth==0.57.0
django-allauth==0.60.0
# via -r requirements/test.txt
django-browser-reload==1.11.0
django-browser-reload==1.12.1
# via -r requirements/dev.in
django-cache-url==3.4.2
django-cache-url==3.4.5
# via
# -r requirements/test.txt
# environs
@ -76,65 +75,61 @@ django-debug-toolbar==4.2.0
# via -r requirements/dev.in
django-extensions==3.2.3
# via -r requirements/dev.in
django-money==3.3.0
django-money==3.4.1
# via -r requirements/test.txt
django-stubs==1.12.0
django-stubs==4.2.7
# via -r requirements/dev.in
django-stubs-ext==0.7.0
django-stubs-ext==4.2.7
# via django-stubs
django-zen-queries==2.1.0
# via -r requirements/test.txt
environs[django]==9.5.0
environs[django]==10.0.0
# via -r requirements/test.txt
executing==1.2.0
executing==2.0.1
# via stack-data
h11==0.14.0
# via
# -r requirements/test.txt
# uvicorn
idna==3.4
idna==3.6
# via
# -r requirements/test.txt
# requests
ipython==8.6.0
ipython==8.19.0
# via -r requirements/dev.in
jedi==0.18.1
jedi==0.19.1
# via ipython
lxml==4.9.1
lxml==5.0.1
# via
# -r requirements/test.txt
# unittest-xml-reporting
marshmallow==3.19.0
marshmallow==3.20.1
# via
# -r requirements/test.txt
# environs
matplotlib-inline==0.1.6
# via ipython
mypy==1.5.1
# via
# -r requirements/dev.in
# django-stubs
mypy==1.8.0
# via -r requirements/dev.in
mypy-extensions==1.0.0
# via mypy
oauthlib==3.2.2
# via
# -r requirements/test.txt
# requests-oauthlib
packaging==21.3
packaging==23.2
# via
# -r requirements/test.txt
# marshmallow
parso==0.8.3
# via jedi
pexpect==4.8.0
pexpect==4.9.0
# via ipython
pickleshare==0.7.5
prompt-toolkit==3.0.43
# via ipython
prompt-toolkit==3.0.32
# via ipython
psycopg[binary]==3.1.12
psycopg[binary]==3.1.16
# via -r requirements/test.txt
psycopg-binary==3.1.12
psycopg-binary==3.1.16
# via
# -r requirements/test.txt
# psycopg
@ -150,17 +145,13 @@ pycparser==2.21
# via
# -r requirements/test.txt
# cffi
pygments==2.13.0
pygments==2.17.2
# via ipython
pyjwt[crypto]==2.6.0
pyjwt[crypto]==2.8.0
# via
# -r requirements/test.txt
# django-allauth
pyparsing==3.0.9
# via
# -r requirements/test.txt
# packaging
python-dotenv==0.21.0
python-dotenv==1.0.0
# via
# -r requirements/test.txt
# environs
@ -168,7 +159,7 @@ python3-openid==3.2.0
# via
# -r requirements/test.txt
# django-allauth
requests==2.28.1
requests==2.31.0
# via
# -r requirements/test.txt
# django-allauth
@ -179,28 +170,27 @@ requests-oauthlib==1.3.1
# django-allauth
six==1.16.0
# via asttokens
sqlparse==0.4.3
sqlparse==0.4.4
# via
# -r requirements/test.txt
# django
# django-debug-toolbar
stack-data==0.6.1
stack-data==0.6.3
# via ipython
tblib==2.0.0
tblib==3.0.0
# via -r requirements/test.txt
tomli==2.0.1
# via django-stubs
traitlets==5.5.0
traitlets==5.14.1
# via
# ipython
# matplotlib-inline
types-pytz==2022.6.0.1
types-pytz==2023.3.1.1
# via django-stubs
types-pyyaml==6.0.12.2
types-pyyaml==6.0.12.12
# via django-stubs
typing-extensions==4.8.0
typing-extensions==4.9.0
# via
# -r requirements/test.txt
# dj-database-url
# django-stubs
# django-stubs-ext
# mypy
@ -208,15 +198,15 @@ typing-extensions==4.8.0
# py-moneyed
unittest-xml-reporting==3.2.0
# via -r requirements/test.txt
urllib3==1.26.12
urllib3==2.1.0
# via
# -r requirements/test.txt
# requests
uvicorn==0.23.2
uvicorn==0.25.0
# via -r requirements/test.txt
wcwidth==0.2.5
wcwidth==0.2.13
# via prompt-toolkit
whitenoise==6.5.0
whitenoise==6.6.0
# via -r requirements/test.txt
# The following packages are considered to be unsafe in a requirements file:

View file

@ -1,5 +1,5 @@
-r base.txt
coverage==7.3.1
tblib==2.0.0
coverage==7.4.0
tblib==3.0.0
unittest-xml-reporting==3.2.0

View file

@ -8,29 +8,29 @@ asgiref==3.7.2
# via
# -r requirements/base.txt
# django
babel==2.12.1
babel==2.14.0
# via
# -r requirements/base.txt
# py-moneyed
certifi==2022.9.24
certifi==2023.11.17
# via
# -r requirements/base.txt
# requests
cffi==1.15.1
cffi==1.16.0
# via
# -r requirements/base.txt
# cryptography
charset-normalizer==2.1.1
charset-normalizer==3.3.2
# via
# -r requirements/base.txt
# requests
click==7.1.2
click==8.1.7
# via
# -r requirements/base.txt
# uvicorn
coverage==7.3.1
coverage==7.4.0
# via -r requirements/test.in
cryptography==38.0.3
cryptography==41.0.7
# via
# -r requirements/base.txt
# pyjwt
@ -38,7 +38,7 @@ defusedxml==0.7.1
# via
# -r requirements/base.txt
# python3-openid
dj-database-url==1.0.0
dj-database-url==2.1.0
# via
# -r requirements/base.txt
# environs
@ -46,36 +46,36 @@ dj-email-url==1.0.6
# via
# -r requirements/base.txt
# environs
django==4.2.5
django==5.0.1
# via
# -r requirements/base.txt
# dj-database-url
# django-allauth
# django-money
# django-zen-queries
django-allauth==0.57.0
django-allauth==0.60.0
# via -r requirements/base.txt
django-cache-url==3.4.2
django-cache-url==3.4.5
# via
# -r requirements/base.txt
# environs
django-money==3.3.0
django-money==3.4.1
# via -r requirements/base.txt
django-zen-queries==2.1.0
# via -r requirements/base.txt
environs[django]==9.5.0
environs[django]==10.0.0
# via -r requirements/base.txt
h11==0.14.0
# via
# -r requirements/base.txt
# uvicorn
idna==3.4
idna==3.6
# via
# -r requirements/base.txt
# requests
lxml==4.9.1
lxml==5.0.1
# via unittest-xml-reporting
marshmallow==3.19.0
marshmallow==3.20.1
# via
# -r requirements/base.txt
# environs
@ -83,13 +83,13 @@ oauthlib==3.2.2
# via
# -r requirements/base.txt
# requests-oauthlib
packaging==21.3
packaging==23.2
# via
# -r requirements/base.txt
# marshmallow
psycopg[binary]==3.1.12
psycopg[binary]==3.1.16
# via -r requirements/base.txt
psycopg-binary==3.1.12
psycopg-binary==3.1.16
# via
# -r requirements/base.txt
# psycopg
@ -101,15 +101,11 @@ pycparser==2.21
# via
# -r requirements/base.txt
# cffi
pyjwt[crypto]==2.6.0
pyjwt[crypto]==2.8.0
# via
# -r requirements/base.txt
# django-allauth
pyparsing==3.0.9
# via
# -r requirements/base.txt
# packaging
python-dotenv==0.21.0
python-dotenv==1.0.0
# via
# -r requirements/base.txt
# environs
@ -117,7 +113,7 @@ python3-openid==3.2.0
# via
# -r requirements/base.txt
# django-allauth
requests==2.28.1
requests==2.31.0
# via
# -r requirements/base.txt
# django-allauth
@ -126,26 +122,27 @@ requests-oauthlib==1.3.1
# via
# -r requirements/base.txt
# django-allauth
sqlparse==0.4.3
sqlparse==0.4.4
# via
# -r requirements/base.txt
# django
tblib==2.0.0
tblib==3.0.0
# via -r requirements/test.in
typing-extensions==4.8.0
typing-extensions==4.9.0
# via
# -r requirements/base.txt
# dj-database-url
# psycopg
# py-moneyed
unittest-xml-reporting==3.2.0
# via -r requirements/test.in
urllib3==1.26.12
urllib3==2.1.0
# via
# -r requirements/base.txt
# requests
uvicorn==0.23.2
uvicorn==0.25.0
# via -r requirements/base.txt
whitenoise==6.5.0
whitenoise==6.6.0
# via -r requirements/base.txt
# The following packages are considered to be unsafe in a requirements file:

View file

@ -7,6 +7,7 @@
{% block content %}
<div class="content-view">
<h1>
{{ member.username }}
</h1>
@ -39,5 +40,6 @@
{% else %}
{% trans "No memberships" %}
{% endif %}
</div>
{% endblock %}

View file

@ -7,6 +7,8 @@
{% block content %}
<div class="content-view">
<h2>Membership settings</h2>
{% if not current_membership %}
<p>{% trans "You do not have an active membership!" %}</p>
@ -24,4 +26,38 @@
<p>{% trans "Period" %}: {{ current_period.lower|date:"SHORT_DATE_FORMAT" }} to {{ current_period.upper|date:"SHORT_DATE_FORMAT"|default:next_general_assembly }}</p>
<p>{% trans "Type" %}: {{ current_membership.membership_type }}</p>
{% endif %}
{% endblock %}
</div>
<div class="content-view">
<h2>Profile settings</h2>
<form>
<div>
<label for="username">
Username
</label>
<input id="username" type="text" value="{{user}}" />
</div>
<div>
<label for="first_name">
First name
</label>
<input id="first_name" type="text" value="{{user.first_name}}" />
</div>
<div>
<label for="last_name">
Last name
</label>
<input id="last_name" type="text" value="{{user.last_name}}" />
</div>
<button>Update Profile</button>
</form>
</div>
<div class="view-list">
<h2>Email settings</h2>
<button>Update Email</button>
</div>
{% endblock %}

View file

@ -70,6 +70,7 @@ def members_admin_detail(request, member_id):
context = {
"member": member,
"subscription_periods": subscription_periods,
"base_path": "admin-members",
}
return render(

View file

@ -0,0 +1,384 @@
/* Reset */
*, *::before, *::after {
box-sizing: border-box;
}
* {
margin: 0;
}
body {
line-height: 1.5;
}
img, picture, video, canvas, svg {
display: block;
max-width: 100%;
}
input, button, textarea, select {
font: inherit;
}
p, h1, h2, h3, h4, h5, h6 {
overflow-wrap: break-word;
}
#root, #__next {
isolation: isolate;
}
/* Variables */
:root {
/* Colors */
--light : #fff;
--light-dust : #f6f6f6;
--dust : #f1f1f1;
--medium-dust : #dadada;
--dark-dust : #bfbfbf;
--fade : #878787;
--twilight : #4a4a4a;
--dark : #2a2a2a;
--custard : #f0dcac;
--water : #a8f3f4;
--splash : #4b3aba;
/* Sizes */
--space : 12px;
--double-space : calc(var(--space) * 2);
--half-space : calc(var(--space) / 2);
--quarter-space : calc(var(--space) / 4);
--outer-space : var(--double-space);
}
@media (min-width: 1380px) {
:root {
--outer-space : 15%;
}
}
html, body {
height : 100%;
}
h1, h2, h3, h4, h5, h6 {
font-weight : 600;
color : var(--twilight);
}
a {
font-weight : 500;
color : var(--splash);
text-decoration : none;
}
body {
margin : 0;
padding : 0;
background : var(--custard);
font-family : Inter;
font-weight : 400;
line-height : 1.6;
color : var(--dark);
}
header {
display : flex;
padding : var(--double-space) var(--outer-space);
background : var(--light);
justify-content : space-between;
align-items : center;
}
header > h1 {
font-size : 1.44em;
}
header > a.logout {
padding : 6px 12px;
border-radius : 6px;
background : var(--twilight);
text-decoration : none;
color : var(--dust);
}
header > a.logout:hover {
background : var(--splash);
color : var(--light);
}
aside {
padding : var(--double-space) var(--outer-space);
background : var(--light);
}
aside > div {
background : var(--dust);
padding : var(--double-space);
border-radius : var(--quarter-space);
overflow : hidden;
}
aside > div > h2 {
font-size : 1.22em;
margin : 0 0 6px 0;
}
aside > div > figure {
width : 100px;
height : 100px;
border : 1px solid var(--dark-dust);
float : left;
margin-right : var(--double-space);
}
aside > div > dl {
overflow : hidden;
}
aside > div > dl > dt {
float : left;
clear : left;
margin : 0 var(--double-space) 0 0;
width : 180px;
font-weight : 600;
color : var(--twilight);
}
nav {
display : block;
border-bottom : 1px solid var(--dark-dust);
background : var(--light);
}
nav ol {
margin: 0 calc(var(--outer-space));
padding : 0;
list-style-type : none;
overflow : hidden;
}
nav ol li {
margin : 0;
padding : 0;
float : left;
}
nav > ol > li > a {
display : block;
padding : var(--half-space) var(--half-space) var(--quarter-space);
margin : 0 var(--space);
border-bottom : var(--half-space) solid transparent;
text-decoration : none;
color : var(--dark);
cursor : pointer;
font-weight : 500;
letter-spacing : 0.04em;
}
nav > ol > li:first-child > a {
margin-left : 0;
}
nav ol li a:hover {
border-color : rgba(0,0,0,0.6);
}
nav ol li a.current {
font-weight : bold;
border-color : var(--splash);
color : var(--splash);
}
article {
padding : var(--double-space) var(--outer-space);
}
article > div {
background : var(--dust);
padding : var(--double-space);
margin-bottom : var(--double-space);
}
div.content-view > h2 {
margin : 0 0 var(--double-space) 0;
}
div.services {
display : flex;
justify-content : space-between;
gap : var(--double-space);
flex-wrap: wrap;
}
div.services > div,
div.infobox {
background : var(--light);
padding : var(--double-space);
border-radius : 6px;
flex : 240px;
max-width : 420px;
display : flex;
flex-flow : column;
justify-content : space-between;
}
div.infobox button {
margin-top : var(--double-space);
}
div.services > div > div.description {
margin-bottom : var(--double-space);
}
div.services > div > div.description > p {
margin-top : var(--half-space);
}
div.services > div > a,
a.button,
button {
display : block;
color : var(--light);
background : var(--splash);
padding : var(--space) var(--double-space);
border-radius : 3px;
opacity : 0.9;
cursor : pointer;
text-align : center;
border : 0;
font-weight : 600;
letter-spacing : 0.03em;
text-decoration : none;
}
div.services > div > a:hover,
a.button:hover,
button:hover {
opacity : 1.0;
}
article table {
width : 100%;
border-spacing : 0;
margin : var(--space) 0;
}
article table thead th {
text-align : left;
}
article table tbody tr:nth-child(odd) {
background : var(--medium-dust);
}
article table thead th,
article table tbody td {
padding : var(--half-space);
}
form > div {
margin : 0 0 var(--double-space);
}
form > div >label {
display : block;
margin : 0 0 6px;
}
form > div > input[type="text"],
form > div > input[type="password"] {
border : 2px solid var(--twilight);
border-radius : 6px;
padding : 8px;
background : var(--light-dust);
width : 100%;
}
#login {
height : 100%;
display : flex;
align-items : center;
justify-content : center;
}
#loginbox {
border-radius : var(--space);
border : 6px solid white;
width : 800px;
height : 500px;
display : flex;
flex-flow : row;
}
#loginbox > div {
padding : var(--double-space);
flex : 1;
}
#loginbox label {
color : var(--twilight);
}
#loginbox > div.login {
background : var(--light-dust);
display: flex;
flex-flow: column;
justify-content: space-between;
}
#loginbox > div.signup {
background : var(--water);
display: flex;
flex-flow: column;
align-items: center;
}
#loginbox > div:first-child {
border-radius : var(--half-space) 0 0 var(--half-space);
}
#loginbox > div:last-child {
border-radius : 0 var(--half-space) var(--half-space) 0;
}
#loginbox > div:last-child > * {
flex : 1;
}
#loginbox div.new_here {
margin-top : var(--double-space);
}
#loginbox div.new_here h2 {
margin: var(--double-space) 0;
}
#loginbox button {
width : 100%;
}
#loginbox img {
padding : 0 var(--double-space);
}
footer {
margin : var(--space) var(--outer-space);
padding : var(--space);
border-radius : var(--quarter-space);
background : var(--dark);
color : var(--dust);
font-size : 0.78em;
opacity : 0.8;
}
span.time_remaining {
color : var(--fade);
}
.pagination {
display : flex;
justify-content : center;
list-style : none;
padding : 0;
margin : 0;
}
.pagination > li {
margin : 0 6px;
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,200 @@
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 100;
font-display: swap;
src: url("Inter-Thin.woff2?v=3.19") format("woff2"),
url("Inter-Thin.woff?v=3.19") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: italic;
font-weight: 100;
font-display: swap;
src: url("Inter-ThinItalic.woff2?v=3.19") format("woff2"),
url("Inter-ThinItalic.woff?v=3.19") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 200;
font-display: swap;
src: url("Inter-ExtraLight.woff2?v=3.19") format("woff2"),
url("Inter-ExtraLight.woff?v=3.19") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: italic;
font-weight: 200;
font-display: swap;
src: url("Inter-ExtraLightItalic.woff2?v=3.19") format("woff2"),
url("Inter-ExtraLightItalic.woff?v=3.19") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 300;
font-display: swap;
src: url("Inter-Light.woff2?v=3.19") format("woff2"),
url("Inter-Light.woff?v=3.19") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: italic;
font-weight: 300;
font-display: swap;
src: url("Inter-LightItalic.woff2?v=3.19") format("woff2"),
url("Inter-LightItalic.woff?v=3.19") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url("Inter-Regular.woff2?v=3.19") format("woff2"),
url("Inter-Regular.woff?v=3.19") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: italic;
font-weight: 400;
font-display: swap;
src: url("Inter-Italic.woff2?v=3.19") format("woff2"),
url("Inter-Italic.woff?v=3.19") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 500;
font-display: swap;
src: url("Inter-Medium.woff2?v=3.19") format("woff2"),
url("Inter-Medium.woff?v=3.19") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: italic;
font-weight: 500;
font-display: swap;
src: url("Inter-MediumItalic.woff2?v=3.19") format("woff2"),
url("Inter-MediumItalic.woff?v=3.19") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 600;
font-display: swap;
src: url("Inter-SemiBold.woff2?v=3.19") format("woff2"),
url("Inter-SemiBold.woff?v=3.19") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: italic;
font-weight: 600;
font-display: swap;
src: url("Inter-SemiBoldItalic.woff2?v=3.19") format("woff2"),
url("Inter-SemiBoldItalic.woff?v=3.19") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 700;
font-display: swap;
src: url("Inter-Bold.woff2?v=3.19") format("woff2"),
url("Inter-Bold.woff?v=3.19") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: italic;
font-weight: 700;
font-display: swap;
src: url("Inter-BoldItalic.woff2?v=3.19") format("woff2"),
url("Inter-BoldItalic.woff?v=3.19") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 800;
font-display: swap;
src: url("Inter-ExtraBold.woff2?v=3.19") format("woff2"),
url("Inter-ExtraBold.woff?v=3.19") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: italic;
font-weight: 800;
font-display: swap;
src: url("Inter-ExtraBoldItalic.woff2?v=3.19") format("woff2"),
url("Inter-ExtraBoldItalic.woff?v=3.19") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: normal;
font-weight: 900;
font-display: swap;
src: url("Inter-Black.woff2?v=3.19") format("woff2"),
url("Inter-Black.woff?v=3.19") format("woff");
}
@font-face {
font-family: 'Inter';
font-style: italic;
font-weight: 900;
font-display: swap;
src: url("Inter-BlackItalic.woff2?v=3.19") format("woff2"),
url("Inter-BlackItalic.woff?v=3.19") format("woff");
}
/* -------------------------------------------------------
Variable font.
Usage:
html { font-family: 'Inter', sans-serif; }
@supports (font-variation-settings: normal) {
html { font-family: 'Inter var', sans-serif; }
}
*/
@font-face {
font-family: 'Inter var';
font-weight: 100 900;
font-display: swap;
font-style: normal;
font-named-instance: 'Regular';
src: url("Inter-roman.var.woff2?v=3.19") format("woff2");
}
@font-face {
font-family: 'Inter var';
font-weight: 100 900;
font-display: swap;
font-style: italic;
font-named-instance: 'Italic';
src: url("Inter-italic.var.woff2?v=3.19") format("woff2");
}
/* --------------------------------------------------------------------------
[EXPERIMENTAL] Multi-axis, single variable font.
Slant axis is not yet widely supported (as of February 2019) and thus this
multi-axis single variable font is opt-in rather than the default.
When using this, you will probably need to set font-variation-settings
explicitly, e.g.
* { font-variation-settings: "slnt" 0deg }
.italic { font-variation-settings: "slnt" 10deg }
*/
@font-face {
font-family: 'Inter var experimental';
font-weight: 100 900;
font-display: swap;
font-style: oblique 0deg 10deg;
src: url("Inter.var.woff2?v=3.19") format("woff2");
}

View file

@ -4,6 +4,8 @@
{% block non_login_content %}
<div id="loginbox">
<div class="login">
{% if form.non_field_errors %}
{% for error in form.non_field_errors %}
<div class="alert alert-danger" role="alert">
@ -11,10 +13,11 @@
</div>
{% endfor %}
{% endif %}
<h2>Log in</h2>
<form method="post" action="">
{% csrf_token %}
<div>
<label for="id_username"
class="visually-hidden">
{% trans "E-mail" %}
@ -26,7 +29,8 @@
placeholder="{% trans "E-mail" %}"
required
autofocus>
</div>
<div>
<label for="id_password" class="visually-hidden">
{% trans "Password" %}
</label>
@ -36,23 +40,25 @@
class="form-control mb-lg-2"
placeholder="{% trans "Password" %}"
required>
<button class="w-100 mb-lg-2 btn btn-lg btn-primary"
type="submit">{% trans "Sign in" %}</button>
</div>
<div>
<button type="submit">{% trans "Sign in" %}</button>
</div>
</form>
<div>
<a href="{% url "account_reset_password" %}"
class="w-100 btn btn-lg btn-outline-info">
{% trans "Forgot password?" %}
</a>
<div class="hr-text">
<span>{% trans "Or"|upper %}</span>
</div>
</div>
<div class="signup">
<img src="https://data.coop/static/img/logo_da.svg" alt="data.coop logo">
<div class="new_here">
<h2> Are you new here? </h2>
<a class="button" href="{% url "account_signup" %}">{% trans "Become a member" %}</a>
</div>
</div>
</div>
<a class="w-100 btn btn-lg btn-outline-success"
type="submit"
href="{% url "account_signup" %}">
{% trans "Become a member" %}
</a>
{% endblock %}

View file

@ -5,17 +5,18 @@
{% block head_title %}{% trans "Sign Out" %}{% endblock %}
{% block content %}
<h1>{% trans "Sign Out" %}</h1>
<div class="content-view">
<h2>{% trans "Sign Out" %}</h2>
<p>{% trans 'Are you sure you want to sign out?' %}</p>
<form method="post" action="{% url 'account_logout' %}">
{% csrf_token %}
{% if redirect_field_value %}
<input type="hidden" name="{{ redirect_field_name }}" value="{{ redirect_field_value }}"/>
{% endif %}
<button type="submit">{% trans 'Sign Out' %}</button>
{% csrf_token %}
{% if redirect_field_value %}
<input type="hidden" name="{{ redirect_field_name }}" value="{{ redirect_field_value }}"/>
{% endif %}
<button type="submit">{% trans 'Sign Out' %}</button>
</form>
</div>
{% endblock %}

View file

@ -7,76 +7,13 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<title>Login {{ site.name }}</title>
<link href="{% static "/css/bootstrap.min.css" %}" rel="stylesheet"
crossorigin="anonymous">
<link href="{% static "/css/bootstrap-icons.css" %}" rel="stylesheet"
crossorigin="anonymous">
<style>
html,
body {
height: 100%;
}
body {
display: flex;
align-items: center;
padding-top: 40px;
padding-bottom: 40px;
background-color: #a8f3f4;
}
.form-signin {
width: 100%;
max-width: 330px;
padding: 15px;
margin: auto;
}
.form-signin .checkbox {
font-weight: 400;
}
.form-signin .form-control {
position: relative;
box-sizing: border-box;
height: auto;
padding: 10px;
font-size: 16px;
}
.form-signin .form-control:focus {
z-index: 2;
}
.hr-text {
width: 100%;
text-align: center;
border-bottom: 1px solid #000;
line-height: 0.1em;
margin: 20px 0 20px;
}
.hr-text span {
background: #a8f3f4;
padding: 0 10px;
}
</style>
<link rel="stylesheet" href="{% static "/fonts/inter.css" %}">
<link rel="stylesheet" href="{% static "/css/style.css" %}">
</head>
<body class="text-center">
<main class="form-signin">
<img class="mb-4" src="https://data.coop/static/img/logo_da.svg" alt=""
width="260" height="160">
<body>
<main id="login">
{% block non_login_content %}
{% endblock %}
</main>
</main>
</body>
</html>

View file

@ -3,211 +3,82 @@
{% load static %}
<!doctype html>
<html lang="en">
<head>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<title>{% block head_title %}{% endblock %} {{ site.name }}</title>
<link rel="stylesheet" href="{% static "fonts/inter.css" %}">
<link rel="stylesheet" href="{% static "css/style.css" %}">
</head>
<body>
<header>
<h1> data.coop membersystem </h1>
<a class="logout" href="{% url "account_logout" %}">Log out</a>
</header>
<main>
<aside>
<div>
<figure></figure>
<h2>{{ user }}</h2>
<link href="{% static "/css/bootstrap.min.css" %}" rel="stylesheet"
crossorigin="anonymous">
{% if current_membership %}
<dl>
<dt>Membership</dt>
<dd>
Active
</dd>
<link href="{% static "/css/bootstrap-icons.css" %}" rel="stylesheet"
crossorigin="anonymous">
<dt>Period</dt>
<dd>
Until {{ current_period.upper }} <span class="time_remaining">({{ current_period.upper|timeuntil }})</span>
</dd>
<style>
body {
font-size: .875rem;
}
/*
* Sidebar
*/
.sidebar {
position: fixed;
top: 0;
bottom: 0;
left: 0;
z-index: 100; /* Behind the navbar */
padding: 48px 0 0; /* Height of navbar */
box-shadow: inset -1px 0 0 rgba(0, 0, 0, .1);
}
@media (max-width: 767.98px) {
.sidebar {
top: 5rem;
}
}
.sidebar-sticky {
position: relative;
top: 0;
height: calc(100vh - 48px);
padding-top: .5rem;
overflow-x: hidden;
overflow-y: auto; /* Scrollable contents if viewport is shorter than content. */
}
.sidebar .nav-link {
font-weight: 500;
color: #333;
}
.sidebar .nav-link .feather {
margin-right: 4px;
color: #727272;
}
.sidebar .nav-link.active {
color: #007bff;
}
.sidebar .nav-link:hover .feather,
.sidebar .nav-link.active .feather {
color: inherit;
}
.sidebar-heading {
font-size: .75rem;
text-transform: uppercase;
}
/*
* Navbar
*/
.navbar-brand {
padding-top: .75rem;
padding-bottom: .75rem;
font-size: 1rem;
background-color: rgba(0, 0, 0, .25);
box-shadow: inset -1px 0 0 rgba(0, 0, 0, .25);
}
.navbar .navbar-toggler {
top: .25rem;
right: 1rem;
}
.navbar .form-control {
padding: .75rem 1rem;
border-width: 0;
border-radius: 0;
}
.form-control-dark {
color: #fff;
background-color: rgba(255, 255, 255, .1);
border-color: rgba(255, 255, 255, .1);
}
.form-control-dark:focus {
border-color: transparent;
box-shadow: 0 0 0 3px rgba(255, 255, 255, .25);
} </style>
<script src="{% static "js/bootstrap.bundle.min.js" %}"
crossorigin="anonymous"></script>
</head>
<body>
<header class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0 shadow">
<a class="navbar-brand col-md-3 col-lg-2 me-0 px-3" href="#">{{ site.name }}</a>
<button class="navbar-toggler position-absolute d-md-none collapsed" type="button"
data-bs-toggle="collapse" data-bs-target="#sidebarMenu"
aria-controls="sidebarMenu" aria-expanded="false"
aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<ul class="navbar-nav px-3">
<li class="nav-item text-nowrap">
<a class="nav-link" href="{% url "account_logout" %}">
<i class="bi bi-person-circle"></i>
Sign out</a>
</li>
</ul>
</header>
<div class="container-fluid">
<div class="row">
<nav id="sidebarMenu"
class="col-md-3 col-lg-2 d-md-block bg-light sidebar collapse">
<div class="position-sticky pt-3">
<ul class="nav flex-column">
<li class="nav-item">
<a class="nav-link {% active_path "index" "active" %}"
href="{% url "index" %}">
{% trans "Dashboard" %}
</a>
</li>
</ul>
<h6 class="sidebar-heading d-flex justify-content-between align-items-center px-3 mt-4 mb-1 text-muted">
<span>{% trans "Profile" %}</span>
</h6>
<ul class="nav flex-column">
<li class="nav-item">
<a class="nav-link" href="#">
{% trans "Details" %}
</a>
</li>
<li class="nav-item">
<a class="nav-link {% active_path "account_email" "active" %}"
aria-current="page" href="{% url "account_email" %}">
{% trans "Emails" %}
</a>
</li>
</ul>
<h6 class="sidebar-heading d-flex justify-content-between align-items-center px-3 mt-4 mb-1 text-muted">
<span>{% trans "Membership" %}</span>
</h6>
<ul class="nav flex-column">
<li class="nav-item">
<a class="nav-link {% active_path "membership-overview" "active" %}"
aria-current="page" href="{% url "membership-overview" %}">
{% trans "Overview" %}
</a>
</li>
</ul>
<h6 class="sidebar-heading d-flex justify-content-between align-items-center px-3 mt-4 mb-1 text-muted">
<span>{% trans "Services" %}</span>
</h6>
<ul class="nav flex-column">
<li class="nav-item">
<a class="nav-link {% active_path "services-overview" "active" %}"
href="{% url "services-overview" %}">
{% trans "Overview" %}
</a>
</li>
</ul>
{% if perms.membership.administrate_memberships %}
<h6 class="sidebar-heading d-flex justify-content-between align-items-center px-3 mt-4 mb-1 text-muted">
<span>{% trans "Admin" %}</span>
</h6>
<ul class="nav flex-column mb-2">
<li class="nav-item {% active_path "admin-members" "active" %}">
<a class="nav-link" href="{% url "admin-members" %}">
<span data-feather="file-text"></span>
{% trans "Members" %}
</a>
</li>
</ul>
<dt>Membership type</dt>
<dd>Normal member</dd>
</dl>
{% else %}
Your membership status will be displayed here in the future.
{% endif %}
</div>
</nav>
</aside>
<nav>
<ol>
<li>
<a href="/" class="{% active_path "index" "current" %}">
Dashboard
</a>
</li>
<main class="col-md-9 ms-sm-auto col-lg-10 px-md-4 p-4">
{% block content %}
{% endblock %}
{% comment %}
<li>
<a href="/services" class="{% active_path "services" "current" %}">
Services
</a>
</li>
{% endcomment %}
<li>
<a href="{% url "account_email" %}" class="{% active_path "account_email" "current" %}">
Email
</a>
</li>
{% if perms.membership.administrate_memberships %}
<li>
<a href="{% url "admin-members" %}" class="{% active_path "admin-members" "current" %}">
Admin
</a>
</li>
{% endif %}
</ol>
</nav>
<article>
{% block content %}{% endblock %}
</article>
</main>
</div>
</div>
</body>
<footer>
data.coop membersystem version 0.0.1
</footer>
</body>
</html>

View file

@ -3,3 +3,26 @@
{% block head_title %}
{% trans "Home" %}
{% endblock %}
{% block content %}
<div class="content-view">
<h2>Welcome {{ user }}!</h2>
<p>
This is the new member area.
</p>
<p>
It is very much under construction.
</p>
{% comment %}
<hr>
<br>
<div class="infobox">
<p>
To get started we need you to verify your email!
</p>
<button>Verify email</button>
</div>
{% endcomment %}
</div>
{% endblock %}

View file

@ -1,10 +1,62 @@
{% extends "base.html" %}
{% block content %}
<p>
Services and signup to these will be
</p>
<p>
This is yet to be implemented.
</p>
<div class="content-view">
Coming soon!
</div>
{% comment %}
<div class="content-view">
<h2>Services you subscribe to</h2>
<div class="services">
<div>
<div class="description">
<h3>Passit</h3>
<p>Passit is a service that blabla</p>
<a href="#">Read more &hellip;</a>
</div>
<a>Unsubscribe</a>
</div>
</div>
</div>
<div class="content-view">
<h2>Available services</h2>
<div class="services">
<div>
<div class="description">
<h3>Forgejo</h3>
<p>Forgejo is a service that blabla</p>
<a href="#">Read more &hellip;</a>
</div>
<a>Subscribe</a>
</div>
<div>
<div class="description">
<h3>Mastodon</h3>
<p>Mastodon is a service where you can write things to people around the world.</p>
<a href="#">Read more &hellip;</a>
</div>
<a>Subscribe</a>
</div>
<div>
<div class="description">
<h3>Matrix</h3>
<p>Matrix is a service that blabla</p>
<a href="#">Read more &hellip;</a>
</div>
<a>Subscribe</a>
</div>
<div>
<div class="description">
<h3>NextCloud</h3>
<p>NextCloud is a service that blabla</p>
<a href="#">Read more &hellip;</a>
</div>
<a>Subscribe</a>
</div>
</div>
</div>
{% endcomment %}
{% endblock %}

View file

@ -13,7 +13,7 @@ from membership.views import membership_overview
urlpatterns = [
path("", login_required(index), name="index"),
path("services/", login_required(services_overview), name="services-overview"),
path("services/", login_required(services_overview), name="services"),
path("membership/", membership_overview, name="membership-overview"),
path("admin/members/", members_admin, name="admin-members"),
path(

View file

@ -7,6 +7,8 @@
{% block content %}
<div class="content-view">
<h1>
{{ entity_name_plural|capfirst }} <small class="text-muted">{{ total_count }}</small>
</h1>
@ -48,7 +50,6 @@
</table>
{% if is_paginated %}
<nav aria-label="Page navigation example">
<ul class="pagination justify-content-center">
{% if not page.has_previous %}
@ -101,8 +102,9 @@
{% endif %}
</ul>
</nav>
{% endif %}
</div>
{% endblock %}

View file

@ -5,9 +5,17 @@ register = template.Library()
@register.simple_tag(takes_context=True)
def active_path(context, path_name, class_name):
def active_path(context, path_name, class_name) -> str | None:
"""Return the given class name if the current path matches the given path name."""
path = reverse(path_name)
request_path = context.get("request").path
if path == request_path or ("basepath" in context and context["basepath"] == path):
# Check if the current path matches the given path name.
is_path = path == request_path
# Check if the current path is a sub-path of the given path name.
is_base_path = "base_path" in context and reverse(context["base_path"]) == path
if is_path or is_base_path:
return class_name