forked from data.coop/membersystem
Compare commits
1 commit
Author | SHA1 | Date | |
---|---|---|---|
Víðir Valberg Guðmundsson | a9a88c9290 |
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -5,6 +5,3 @@ db.sqlite3
|
|||
.pytest_cache
|
||||
.idea/
|
||||
*.mo
|
||||
.env
|
||||
venv/
|
||||
.venv/
|
||||
|
|
|
@ -3,7 +3,7 @@ default_language_version:
|
|||
exclude: ^.*\b(migrations)\b.*$
|
||||
repos:
|
||||
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||
rev: v4.5.0
|
||||
rev: v4.4.0
|
||||
hooks:
|
||||
- id: check-ast
|
||||
- id: check-merge-conflict
|
||||
|
@ -16,13 +16,14 @@ repos:
|
|||
- id: end-of-file-fixer
|
||||
- id: trailing-whitespace
|
||||
- repo: https://github.com/charliermarsh/ruff-pre-commit
|
||||
rev: 'v0.1.11'
|
||||
rev: 'v0.0.209'
|
||||
hooks:
|
||||
- id: ruff
|
||||
args:
|
||||
- --force-exclude
|
||||
- --fix
|
||||
- repo: https://github.com/asottile/reorder_python_imports
|
||||
rev: v3.12.0
|
||||
rev: v3.9.0
|
||||
hooks:
|
||||
- id: reorder-python-imports
|
||||
args:
|
||||
|
@ -30,31 +31,33 @@ repos:
|
|||
- --application-directories=.:src
|
||||
exclude: migrations/
|
||||
- repo: https://github.com/asottile/pyupgrade
|
||||
rev: v3.15.0
|
||||
rev: v3.3.1
|
||||
hooks:
|
||||
- id: pyupgrade
|
||||
args:
|
||||
- --py311-plus
|
||||
exclude: migrations/
|
||||
- repo: https://github.com/adamchainz/django-upgrade
|
||||
rev: 1.15.0
|
||||
rev: 1.12.0
|
||||
hooks:
|
||||
- id: django-upgrade
|
||||
args:
|
||||
- --target-version=4.1
|
||||
- repo: https://github.com/asottile/yesqa
|
||||
rev: v1.5.0
|
||||
rev: v1.4.0
|
||||
hooks:
|
||||
- id: yesqa
|
||||
- repo: https://github.com/asottile/add-trailing-comma
|
||||
rev: v3.1.0
|
||||
rev: v2.4.0
|
||||
hooks:
|
||||
- id: add-trailing-comma
|
||||
args:
|
||||
- --py36-plus
|
||||
- repo: https://github.com/hadialqattan/pycln
|
||||
rev: v2.4.0
|
||||
rev: v2.1.2
|
||||
hooks:
|
||||
- id: pycln
|
||||
- repo: https://github.com/psf/black
|
||||
rev: 23.12.1
|
||||
rev: 22.12.0
|
||||
hooks:
|
||||
- id: black
|
||||
|
|
4
Makefile
4
Makefile
|
@ -1,4 +1,4 @@
|
|||
DOCKER_COMPOSE = COMPOSE_DOCKER_CLI_BUILD=1 DOCKER_BUILDKIT=1 docker compose
|
||||
DOCKER_COMPOSE = COMPOSE_DOCKER_CLI_BUILD=1 DOCKER_BUILDKIT=1 venv/bin/docker-compose
|
||||
DOCKER_RUN = ${DOCKER_COMPOSE} run -u `id -u`
|
||||
DOCKER_BUILD = DOCKER_BUILDKIT=1 docker build
|
||||
DOCKER_CONTAINER_NAME = backend
|
||||
|
@ -14,7 +14,7 @@ setup_venv:
|
|||
rm -rf venv
|
||||
python3.11 -m venv venv;
|
||||
venv/bin/python -m pip install wheel setuptools;
|
||||
venv/bin/python -m pip install pre-commit boto3 pip-tools;
|
||||
venv/bin/python -m pip install docker-compose pre-commit boto3 pip-tools;
|
||||
|
||||
pre_commit_install:
|
||||
venv/bin/pre-commit install
|
||||
|
|
40
README.md
40
README.md
|
@ -1,22 +1,12 @@
|
|||
# member.data.coop
|
||||
|
||||
## Development
|
||||
|
||||
### Setup environment
|
||||
|
||||
Copy over the .env.example file to .env and adjust DATABASE_URL accordingly
|
||||
|
||||
$ cp .env.example .env
|
||||
|
||||
### Docker
|
||||
|
||||
#### Requirements
|
||||
## Development requirements
|
||||
|
||||
- Docker
|
||||
- Docker compose
|
||||
- pre-commit (preferred for contributions)
|
||||
|
||||
#### Setup
|
||||
## Start local server
|
||||
|
||||
Given that the requirements above are installed, it should be as easy as:
|
||||
|
||||
|
@ -43,29 +33,3 @@ Make messages:
|
|||
Running tests:
|
||||
|
||||
$ make test
|
||||
|
||||
### Non-docker
|
||||
|
||||
Create a venv
|
||||
|
||||
$ python3 -m venv venv
|
||||
|
||||
Activate the venv
|
||||
|
||||
$ source venv/bin/activate
|
||||
|
||||
Install requirements
|
||||
|
||||
$ pip install -r requirements/dev.txt
|
||||
|
||||
Run migrations
|
||||
|
||||
$ ./src/manage.py migrate
|
||||
|
||||
Create a superuser
|
||||
|
||||
$ ./src/manage.py createsuperuser
|
||||
|
||||
Run the server
|
||||
|
||||
$ ./src/manage.py runserver
|
||||
|
|
|
@ -14,8 +14,9 @@ services:
|
|||
- ./:/app/
|
||||
depends_on:
|
||||
- postgres
|
||||
- keycloak
|
||||
env_file:
|
||||
- .env
|
||||
- env
|
||||
|
||||
postgres:
|
||||
image: postgres:13-alpine
|
||||
|
@ -24,7 +25,27 @@ services:
|
|||
ports:
|
||||
- 5432:5432
|
||||
env_file:
|
||||
- .env
|
||||
- env
|
||||
|
||||
keycloak_db:
|
||||
image: postgres:13-alpine
|
||||
volumes:
|
||||
- postgres_data:/var/lib/postgresql/data/
|
||||
env_file:
|
||||
- env
|
||||
|
||||
keycloak:
|
||||
image: "quay.io/keycloak/keycloak:20.0"
|
||||
restart: "unless-stopped"
|
||||
command:
|
||||
- "start-dev"
|
||||
- "--db=postgres"
|
||||
- "--db-url=jdbc:postgresql://keycloak_db:5432/postgres"
|
||||
- "--db-username=postgres"
|
||||
- "--db-password=postgres"
|
||||
- "--hostname=localhost"
|
||||
- "--http-relative-path=/auth"
|
||||
|
||||
|
||||
volumes:
|
||||
postgres_data:
|
||||
|
|
|
@ -3,7 +3,5 @@ POSTGRES_HOST=postgres
|
|||
POSTGRES_PASSWORD=postgres
|
||||
POSTGRES_PORT=5432
|
||||
DATABASE_URL=postgres://postgres:postgres@postgres:5432/postgres
|
||||
# Use something along the the following if you are not using docker
|
||||
# DATABASE_URL=postgres://postgres:postgres@localhost:5432/datacoop_membersystem
|
||||
DEBUG=True
|
||||
DJANGO_ENV=development
|
105
pyproject.toml
105
pyproject.toml
|
@ -1,105 +0,0 @@
|
|||
[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
|
|
@ -1,8 +1,8 @@
|
|||
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==4.1.5
|
||||
django-money==1.3
|
||||
django-allauth==0.46
|
||||
psycopg2-binary==2.9.5
|
||||
environs[django]==9.3
|
||||
uvicorn==0.13
|
||||
whitenoise==5.2
|
||||
django-zen-queries==2.1.0
|
||||
|
|
|
@ -1,98 +1,82 @@
|
|||
#
|
||||
# This file is autogenerated by pip-compile with Python 3.11
|
||||
# This file is autogenerated by pip-compile with Python 3.10
|
||||
# by the following command:
|
||||
#
|
||||
# pip-compile --output-file=requirements/base.txt pyproject.toml
|
||||
# pip-compile --output-file=requirements/base.txt requirements/base.in
|
||||
#
|
||||
asgiref==3.7.2
|
||||
asgiref==3.5.2
|
||||
# via django
|
||||
babel==2.14.0
|
||||
# via py-moneyed
|
||||
certifi==2023.11.17
|
||||
certifi==2022.9.24
|
||||
# via requests
|
||||
cffi==1.16.0
|
||||
cffi==1.15.1
|
||||
# via cryptography
|
||||
charset-normalizer==3.3.2
|
||||
charset-normalizer==2.1.1
|
||||
# via requests
|
||||
click==8.1.7
|
||||
click==7.1.2
|
||||
# via uvicorn
|
||||
cryptography==41.0.7
|
||||
cryptography==38.0.3
|
||||
# via pyjwt
|
||||
defusedxml==0.7.1
|
||||
# via python3-openid
|
||||
dj-database-url==2.1.0
|
||||
dj-database-url==1.0.0
|
||||
# via environs
|
||||
dj-email-url==1.0.6
|
||||
# via environs
|
||||
django==5.0.1
|
||||
django==4.1.5
|
||||
# via
|
||||
# -r requirements/base.in
|
||||
# dj-database-url
|
||||
# django-allauth
|
||||
# django-money
|
||||
# django-registries
|
||||
# django-zen-queries
|
||||
# membersystem (pyproject.toml)
|
||||
django-allauth==0.60.0
|
||||
# via membersystem (pyproject.toml)
|
||||
django-cache-url==3.4.5
|
||||
django-allauth==0.46
|
||||
# via -r requirements/base.in
|
||||
django-cache-url==3.4.2
|
||||
# via environs
|
||||
django-money==3.4.1
|
||||
# via membersystem (pyproject.toml)
|
||||
django-registries==0.0.3
|
||||
# via membersystem (pyproject.toml)
|
||||
django-money==1.3
|
||||
# via -r requirements/base.in
|
||||
django-zen-queries==2.1.0
|
||||
# via membersystem (pyproject.toml)
|
||||
environs[django]==10.0.0
|
||||
# via
|
||||
# environs
|
||||
# membersystem (pyproject.toml)
|
||||
# via -r requirements/base.in
|
||||
environs[django]==9.3
|
||||
# via -r requirements/base.in
|
||||
h11==0.14.0
|
||||
# via uvicorn
|
||||
idna==3.6
|
||||
idna==3.4
|
||||
# via requests
|
||||
marshmallow==3.20.1
|
||||
marshmallow==3.19.0
|
||||
# via environs
|
||||
oauthlib==3.2.2
|
||||
# via requests-oauthlib
|
||||
packaging==23.2
|
||||
packaging==21.3
|
||||
# via marshmallow
|
||||
psycopg[binary]==3.1.16
|
||||
# via
|
||||
# membersystem (pyproject.toml)
|
||||
# psycopg
|
||||
psycopg-binary==3.1.16
|
||||
# via psycopg
|
||||
py-moneyed==3.0
|
||||
psycopg2-binary==2.9.5
|
||||
# via -r requirements/base.in
|
||||
py-moneyed==0.8.0
|
||||
# via django-money
|
||||
pycparser==2.21
|
||||
# via cffi
|
||||
pyjwt[crypto]==2.8.0
|
||||
# via
|
||||
# django-allauth
|
||||
# pyjwt
|
||||
python-dotenv==1.0.0
|
||||
pyjwt[crypto]==2.6.0
|
||||
# via django-allauth
|
||||
pyparsing==3.0.9
|
||||
# via packaging
|
||||
python-dotenv==0.21.0
|
||||
# via environs
|
||||
python3-openid==3.2.0
|
||||
# via django-allauth
|
||||
requests==2.31.0
|
||||
requests==2.28.1
|
||||
# via
|
||||
# django-allauth
|
||||
# requests-oauthlib
|
||||
requests-oauthlib==1.3.1
|
||||
# via django-allauth
|
||||
sqlparse==0.4.4
|
||||
sqlparse==0.4.3
|
||||
# via django
|
||||
typing-extensions==4.9.0
|
||||
# via
|
||||
# dj-database-url
|
||||
# psycopg
|
||||
# py-moneyed
|
||||
urllib3==2.1.0
|
||||
urllib3==1.26.12
|
||||
# via requests
|
||||
uvicorn==0.25.0
|
||||
# via membersystem (pyproject.toml)
|
||||
whitenoise==6.6.0
|
||||
# via membersystem (pyproject.toml)
|
||||
uvicorn==0.13
|
||||
# via -r requirements/base.in
|
||||
whitenoise==5.2
|
||||
# via -r requirements/base.in
|
||||
|
||||
# The following packages are considered to be unsafe in a requirements file:
|
||||
# setuptools
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
-r test.txt
|
||||
|
||||
django-browser-reload==1.12.1
|
||||
django-debug-toolbar==4.2.0
|
||||
django-extensions==3.2.3
|
||||
django-stubs==4.2.7
|
||||
ipython==8.19.0
|
||||
mypy==1.8.0
|
||||
django-browser-reload==1.6.0
|
||||
django-debug-toolbar==3.7.0
|
||||
django-extensions==3.2.1
|
||||
django-stubs==1.12.0
|
||||
ipython==8.6.0
|
||||
mypy==0.990
|
||||
|
|
|
@ -1,39 +1,36 @@
|
|||
#
|
||||
# This file is autogenerated by pip-compile with Python 3.11
|
||||
# This file is autogenerated by pip-compile with Python 3.10
|
||||
# by the following command:
|
||||
#
|
||||
# pip-compile --output-file=requirements/dev.txt requirements/dev.in
|
||||
#
|
||||
asgiref==3.7.2
|
||||
asgiref==3.5.2
|
||||
# via
|
||||
# -r requirements/test.txt
|
||||
# django
|
||||
# django-browser-reload
|
||||
asttokens==2.4.1
|
||||
asttokens==2.1.0
|
||||
# via stack-data
|
||||
babel==2.14.0
|
||||
# via
|
||||
# -r requirements/test.txt
|
||||
# py-moneyed
|
||||
certifi==2023.11.17
|
||||
backcall==0.2.0
|
||||
# via ipython
|
||||
certifi==2022.9.24
|
||||
# via
|
||||
# -r requirements/test.txt
|
||||
# requests
|
||||
cffi==1.16.0
|
||||
cffi==1.15.1
|
||||
# via
|
||||
# -r requirements/test.txt
|
||||
# cryptography
|
||||
charset-normalizer==3.3.2
|
||||
charset-normalizer==2.1.1
|
||||
# via
|
||||
# -r requirements/test.txt
|
||||
# requests
|
||||
click==8.1.7
|
||||
click==7.1.2
|
||||
# via
|
||||
# -r requirements/test.txt
|
||||
# uvicorn
|
||||
coverage==7.4.0
|
||||
coverage==6.5.0
|
||||
# via -r requirements/test.txt
|
||||
cryptography==41.0.7
|
||||
cryptography==38.0.3
|
||||
# via
|
||||
# -r requirements/test.txt
|
||||
# pyjwt
|
||||
|
@ -43,7 +40,7 @@ defusedxml==0.7.1
|
|||
# via
|
||||
# -r requirements/test.txt
|
||||
# python3-openid
|
||||
dj-database-url==2.1.0
|
||||
dj-database-url==1.0.0
|
||||
# via
|
||||
# -r requirements/test.txt
|
||||
# environs
|
||||
|
@ -51,7 +48,7 @@ dj-email-url==1.0.6
|
|||
# via
|
||||
# -r requirements/test.txt
|
||||
# environs
|
||||
django==5.0.1
|
||||
django==4.1.5
|
||||
# via
|
||||
# -r requirements/test.txt
|
||||
# dj-database-url
|
||||
|
@ -63,81 +60,81 @@ django==5.0.1
|
|||
# django-stubs
|
||||
# django-stubs-ext
|
||||
# django-zen-queries
|
||||
django-allauth==0.60.0
|
||||
django-allauth==0.46
|
||||
# via -r requirements/test.txt
|
||||
django-browser-reload==1.12.1
|
||||
django-browser-reload==1.6.0
|
||||
# via -r requirements/dev.in
|
||||
django-cache-url==3.4.5
|
||||
django-cache-url==3.4.2
|
||||
# via
|
||||
# -r requirements/test.txt
|
||||
# environs
|
||||
django-debug-toolbar==4.2.0
|
||||
django-debug-toolbar==3.7.0
|
||||
# via -r requirements/dev.in
|
||||
django-extensions==3.2.3
|
||||
django-extensions==3.2.1
|
||||
# via -r requirements/dev.in
|
||||
django-money==3.4.1
|
||||
django-money==1.3
|
||||
# via -r requirements/test.txt
|
||||
django-stubs==4.2.7
|
||||
django-stubs==1.12.0
|
||||
# via -r requirements/dev.in
|
||||
django-stubs-ext==4.2.7
|
||||
django-stubs-ext==0.7.0
|
||||
# via django-stubs
|
||||
django-zen-queries==2.1.0
|
||||
# via -r requirements/test.txt
|
||||
environs[django]==10.0.0
|
||||
environs[django]==9.3
|
||||
# via -r requirements/test.txt
|
||||
executing==2.0.1
|
||||
executing==1.2.0
|
||||
# via stack-data
|
||||
h11==0.14.0
|
||||
# via
|
||||
# -r requirements/test.txt
|
||||
# uvicorn
|
||||
idna==3.6
|
||||
idna==3.4
|
||||
# via
|
||||
# -r requirements/test.txt
|
||||
# requests
|
||||
ipython==8.19.0
|
||||
ipython==8.6.0
|
||||
# via -r requirements/dev.in
|
||||
jedi==0.19.1
|
||||
jedi==0.18.1
|
||||
# via ipython
|
||||
lxml==5.0.1
|
||||
lxml==4.9.1
|
||||
# via
|
||||
# -r requirements/test.txt
|
||||
# unittest-xml-reporting
|
||||
marshmallow==3.20.1
|
||||
marshmallow==3.19.0
|
||||
# via
|
||||
# -r requirements/test.txt
|
||||
# environs
|
||||
matplotlib-inline==0.1.6
|
||||
# via ipython
|
||||
mypy==1.8.0
|
||||
# via -r requirements/dev.in
|
||||
mypy-extensions==1.0.0
|
||||
mypy==0.990
|
||||
# via
|
||||
# -r requirements/dev.in
|
||||
# django-stubs
|
||||
mypy-extensions==0.4.3
|
||||
# via mypy
|
||||
oauthlib==3.2.2
|
||||
# via
|
||||
# -r requirements/test.txt
|
||||
# requests-oauthlib
|
||||
packaging==23.2
|
||||
packaging==21.3
|
||||
# via
|
||||
# -r requirements/test.txt
|
||||
# marshmallow
|
||||
parso==0.8.3
|
||||
# via jedi
|
||||
pexpect==4.9.0
|
||||
pexpect==4.8.0
|
||||
# via ipython
|
||||
prompt-toolkit==3.0.43
|
||||
pickleshare==0.7.5
|
||||
# via ipython
|
||||
psycopg[binary]==3.1.16
|
||||
prompt-toolkit==3.0.32
|
||||
# via ipython
|
||||
psycopg2-binary==2.9.5
|
||||
# via -r requirements/test.txt
|
||||
psycopg-binary==3.1.16
|
||||
# via
|
||||
# -r requirements/test.txt
|
||||
# psycopg
|
||||
ptyprocess==0.7.0
|
||||
# via pexpect
|
||||
pure-eval==0.2.2
|
||||
# via stack-data
|
||||
py-moneyed==3.0
|
||||
py-moneyed==0.8.0
|
||||
# via
|
||||
# -r requirements/test.txt
|
||||
# django-money
|
||||
|
@ -145,13 +142,17 @@ pycparser==2.21
|
|||
# via
|
||||
# -r requirements/test.txt
|
||||
# cffi
|
||||
pygments==2.17.2
|
||||
pygments==2.13.0
|
||||
# via ipython
|
||||
pyjwt[crypto]==2.8.0
|
||||
pyjwt[crypto]==2.6.0
|
||||
# via
|
||||
# -r requirements/test.txt
|
||||
# django-allauth
|
||||
python-dotenv==1.0.0
|
||||
pyparsing==3.0.9
|
||||
# via
|
||||
# -r requirements/test.txt
|
||||
# packaging
|
||||
python-dotenv==0.21.0
|
||||
# via
|
||||
# -r requirements/test.txt
|
||||
# environs
|
||||
|
@ -159,7 +160,7 @@ python3-openid==3.2.0
|
|||
# via
|
||||
# -r requirements/test.txt
|
||||
# django-allauth
|
||||
requests==2.31.0
|
||||
requests==2.28.1
|
||||
# via
|
||||
# -r requirements/test.txt
|
||||
# django-allauth
|
||||
|
@ -170,43 +171,43 @@ requests-oauthlib==1.3.1
|
|||
# django-allauth
|
||||
six==1.16.0
|
||||
# via asttokens
|
||||
sqlparse==0.4.4
|
||||
sqlparse==0.4.3
|
||||
# via
|
||||
# -r requirements/test.txt
|
||||
# django
|
||||
# django-debug-toolbar
|
||||
stack-data==0.6.3
|
||||
stack-data==0.6.1
|
||||
# via ipython
|
||||
tblib==3.0.0
|
||||
tblib==1.7.0
|
||||
# via -r requirements/test.txt
|
||||
traitlets==5.14.1
|
||||
tomli==2.0.1
|
||||
# via
|
||||
# django-stubs
|
||||
# mypy
|
||||
traitlets==5.5.0
|
||||
# via
|
||||
# ipython
|
||||
# matplotlib-inline
|
||||
types-pytz==2023.3.1.1
|
||||
types-pytz==2022.6.0.1
|
||||
# via django-stubs
|
||||
types-pyyaml==6.0.12.12
|
||||
types-pyyaml==6.0.12.2
|
||||
# via django-stubs
|
||||
typing-extensions==4.9.0
|
||||
typing-extensions==4.4.0
|
||||
# via
|
||||
# -r requirements/test.txt
|
||||
# dj-database-url
|
||||
# django-stubs
|
||||
# django-stubs-ext
|
||||
# mypy
|
||||
# psycopg
|
||||
# py-moneyed
|
||||
unittest-xml-reporting==3.2.0
|
||||
# via -r requirements/test.txt
|
||||
urllib3==2.1.0
|
||||
urllib3==1.26.12
|
||||
# via
|
||||
# -r requirements/test.txt
|
||||
# requests
|
||||
uvicorn==0.25.0
|
||||
uvicorn==0.13
|
||||
# via -r requirements/test.txt
|
||||
wcwidth==0.2.13
|
||||
wcwidth==0.2.5
|
||||
# via prompt-toolkit
|
||||
whitenoise==6.6.0
|
||||
whitenoise==5.2
|
||||
# via -r requirements/test.txt
|
||||
|
||||
# The following packages are considered to be unsafe in a requirements file:
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
-r base.txt
|
||||
|
||||
coverage==7.4.0
|
||||
tblib==3.0.0
|
||||
coverage==6.5.0
|
||||
tblib==1.7.0
|
||||
unittest-xml-reporting==3.2.0
|
||||
|
|
|
@ -1,36 +1,32 @@
|
|||
#
|
||||
# This file is autogenerated by pip-compile with Python 3.11
|
||||
# This file is autogenerated by pip-compile with Python 3.10
|
||||
# by the following command:
|
||||
#
|
||||
# pip-compile --output-file=requirements/test.txt requirements/test.in
|
||||
#
|
||||
asgiref==3.7.2
|
||||
asgiref==3.5.2
|
||||
# via
|
||||
# -r requirements/base.txt
|
||||
# django
|
||||
babel==2.14.0
|
||||
# via
|
||||
# -r requirements/base.txt
|
||||
# py-moneyed
|
||||
certifi==2023.11.17
|
||||
certifi==2022.9.24
|
||||
# via
|
||||
# -r requirements/base.txt
|
||||
# requests
|
||||
cffi==1.16.0
|
||||
cffi==1.15.1
|
||||
# via
|
||||
# -r requirements/base.txt
|
||||
# cryptography
|
||||
charset-normalizer==3.3.2
|
||||
charset-normalizer==2.1.1
|
||||
# via
|
||||
# -r requirements/base.txt
|
||||
# requests
|
||||
click==8.1.7
|
||||
click==7.1.2
|
||||
# via
|
||||
# -r requirements/base.txt
|
||||
# uvicorn
|
||||
coverage==7.4.0
|
||||
coverage==6.5.0
|
||||
# via -r requirements/test.in
|
||||
cryptography==41.0.7
|
||||
cryptography==38.0.3
|
||||
# via
|
||||
# -r requirements/base.txt
|
||||
# pyjwt
|
||||
|
@ -38,7 +34,7 @@ defusedxml==0.7.1
|
|||
# via
|
||||
# -r requirements/base.txt
|
||||
# python3-openid
|
||||
dj-database-url==2.1.0
|
||||
dj-database-url==1.0.0
|
||||
# via
|
||||
# -r requirements/base.txt
|
||||
# environs
|
||||
|
@ -46,36 +42,36 @@ dj-email-url==1.0.6
|
|||
# via
|
||||
# -r requirements/base.txt
|
||||
# environs
|
||||
django==5.0.1
|
||||
django==4.1.5
|
||||
# via
|
||||
# -r requirements/base.txt
|
||||
# dj-database-url
|
||||
# django-allauth
|
||||
# django-money
|
||||
# django-zen-queries
|
||||
django-allauth==0.60.0
|
||||
django-allauth==0.46
|
||||
# via -r requirements/base.txt
|
||||
django-cache-url==3.4.5
|
||||
django-cache-url==3.4.2
|
||||
# via
|
||||
# -r requirements/base.txt
|
||||
# environs
|
||||
django-money==3.4.1
|
||||
django-money==1.3
|
||||
# via -r requirements/base.txt
|
||||
django-zen-queries==2.1.0
|
||||
# via -r requirements/base.txt
|
||||
environs[django]==10.0.0
|
||||
environs[django]==9.3
|
||||
# via -r requirements/base.txt
|
||||
h11==0.14.0
|
||||
# via
|
||||
# -r requirements/base.txt
|
||||
# uvicorn
|
||||
idna==3.6
|
||||
idna==3.4
|
||||
# via
|
||||
# -r requirements/base.txt
|
||||
# requests
|
||||
lxml==5.0.1
|
||||
lxml==4.9.1
|
||||
# via unittest-xml-reporting
|
||||
marshmallow==3.20.1
|
||||
marshmallow==3.19.0
|
||||
# via
|
||||
# -r requirements/base.txt
|
||||
# environs
|
||||
|
@ -83,17 +79,13 @@ oauthlib==3.2.2
|
|||
# via
|
||||
# -r requirements/base.txt
|
||||
# requests-oauthlib
|
||||
packaging==23.2
|
||||
packaging==21.3
|
||||
# via
|
||||
# -r requirements/base.txt
|
||||
# marshmallow
|
||||
psycopg[binary]==3.1.16
|
||||
psycopg2-binary==2.9.5
|
||||
# via -r requirements/base.txt
|
||||
psycopg-binary==3.1.16
|
||||
# via
|
||||
# -r requirements/base.txt
|
||||
# psycopg
|
||||
py-moneyed==3.0
|
||||
py-moneyed==0.8.0
|
||||
# via
|
||||
# -r requirements/base.txt
|
||||
# django-money
|
||||
|
@ -101,11 +93,15 @@ pycparser==2.21
|
|||
# via
|
||||
# -r requirements/base.txt
|
||||
# cffi
|
||||
pyjwt[crypto]==2.8.0
|
||||
pyjwt[crypto]==2.6.0
|
||||
# via
|
||||
# -r requirements/base.txt
|
||||
# django-allauth
|
||||
python-dotenv==1.0.0
|
||||
pyparsing==3.0.9
|
||||
# via
|
||||
# -r requirements/base.txt
|
||||
# packaging
|
||||
python-dotenv==0.21.0
|
||||
# via
|
||||
# -r requirements/base.txt
|
||||
# environs
|
||||
|
@ -113,7 +109,7 @@ python3-openid==3.2.0
|
|||
# via
|
||||
# -r requirements/base.txt
|
||||
# django-allauth
|
||||
requests==2.31.0
|
||||
requests==2.28.1
|
||||
# via
|
||||
# -r requirements/base.txt
|
||||
# django-allauth
|
||||
|
@ -122,27 +118,21 @@ requests-oauthlib==1.3.1
|
|||
# via
|
||||
# -r requirements/base.txt
|
||||
# django-allauth
|
||||
sqlparse==0.4.4
|
||||
sqlparse==0.4.3
|
||||
# via
|
||||
# -r requirements/base.txt
|
||||
# django
|
||||
tblib==3.0.0
|
||||
tblib==1.7.0
|
||||
# via -r requirements/test.in
|
||||
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==2.1.0
|
||||
urllib3==1.26.12
|
||||
# via
|
||||
# -r requirements/base.txt
|
||||
# requests
|
||||
uvicorn==0.25.0
|
||||
uvicorn==0.13
|
||||
# via -r requirements/base.txt
|
||||
whitenoise==6.6.0
|
||||
whitenoise==5.2
|
||||
# via -r requirements/base.txt
|
||||
|
||||
# The following packages are considered to be unsafe in a requirements file:
|
||||
|
|
|
@ -6,6 +6,7 @@ from . import models
|
|||
|
||||
@admin.register(models.Order)
|
||||
class OrderAdmin(admin.ModelAdmin):
|
||||
|
||||
list_display = ("who", "description", "created", "is_paid")
|
||||
|
||||
@admin.display(description=_("Customer"))
|
||||
|
@ -15,6 +16,7 @@ class OrderAdmin(admin.ModelAdmin):
|
|||
|
||||
@admin.register(models.Payment)
|
||||
class PaymentAdmin(admin.ModelAdmin):
|
||||
|
||||
list_display = ("who", "description", "order_id", "created")
|
||||
|
||||
@admin.display(description=_("Customer"))
|
||||
|
|
|
@ -9,6 +9,7 @@ from djmoney.models.fields import MoneyField
|
|||
|
||||
|
||||
class CreatedModifiedAbstract(models.Model):
|
||||
|
||||
modified = models.DateTimeField(auto_now=True, verbose_name=_("modified"))
|
||||
created = models.DateTimeField(auto_now_add=True, verbose_name=_("created"))
|
||||
|
||||
|
@ -96,6 +97,7 @@ class Order(CreatedModifiedAbstract):
|
|||
|
||||
|
||||
class Payment(CreatedModifiedAbstract):
|
||||
|
||||
amount = MoneyField(max_digits=16, decimal_places=2)
|
||||
order = models.ForeignKey(Order, on_delete=models.PROTECT)
|
||||
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
# Generated by Django 4.1.5 on 2023-09-16 14:09
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('auth', '0012_alter_user_first_name_max_length'),
|
||||
('membership', '0004_alter_membership_period'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Member',
|
||||
fields=[
|
||||
],
|
||||
options={
|
||||
'proxy': True,
|
||||
'indexes': [],
|
||||
'constraints': [],
|
||||
},
|
||||
bases=('auth.user',),
|
||||
),
|
||||
]
|
|
@ -12,18 +12,13 @@ from utils.mixins import CreatedModifiedAbstract
|
|||
class Member(User):
|
||||
class QuerySet(models.QuerySet):
|
||||
def annotate_membership(self):
|
||||
from .selectors import get_current_subscription_period
|
||||
|
||||
current_subscription_period = get_current_subscription_period()
|
||||
|
||||
if not current_subscription_period:
|
||||
raise ValueError("No current subscription period found")
|
||||
from membership.selectors import get_current_subscription_period
|
||||
|
||||
return self.annotate(
|
||||
active_membership=models.Exists(
|
||||
Membership.objects.filter(
|
||||
user=models.OuterRef("pk"),
|
||||
period=current_subscription_period.id,
|
||||
period=get_current_subscription_period().id,
|
||||
),
|
||||
),
|
||||
)
|
||||
|
|
|
@ -27,6 +27,7 @@ class Permission:
|
|||
return f"{self.app_label}.{self.codename}"
|
||||
|
||||
def persist_permission(self):
|
||||
|
||||
content_type, _ = ContentType.objects.get_or_create(
|
||||
app_label=self.app_label,
|
||||
model=self.model,
|
||||
|
|
|
@ -23,7 +23,7 @@ def get_subscription_periods(member: Member | None = None) -> list[SubscriptionP
|
|||
period=OuterRef("pk"),
|
||||
),
|
||||
),
|
||||
).filter(membership_exists=True)
|
||||
)
|
||||
|
||||
return list(subscription_periods)
|
||||
|
||||
|
|
|
@ -1,13 +1,9 @@
|
|||
{% extends "base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block head_title %}
|
||||
{% trans "Member detail" %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="content-view">
|
||||
|
||||
<h1>
|
||||
{{ member.username }}
|
||||
</h1>
|
||||
|
@ -16,30 +12,28 @@
|
|||
|
||||
<h3>{% trans "Membership" %}</h3>
|
||||
|
||||
{% if subscription_periods %}
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "Start" %}</th>
|
||||
<th>{% trans "End" %}</th>
|
||||
<th>{% trans "Has membership" %}</th>
|
||||
<th>{% trans "Actions" %}</th>
|
||||
<table class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>{% trans "Start" %}</th>
|
||||
<th>{% trans "End" %}</th>
|
||||
<th>{% trans "Has membership" %}</th>
|
||||
<th>{% trans "Actions" %}</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for period in subscription_periods %}
|
||||
<tr {% if not period.period.upper %}class="table-active"{% endif %}>
|
||||
<td>{{ period.period.lower }}</td>
|
||||
<td>{{ period.period.upper }}</td>
|
||||
<td>{{ period.membership_exists }}</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for period in subscription_periods %}
|
||||
<tr {% if not period.period.upper %}class="table-active"{% endif %}>
|
||||
<td>{{ period.period.lower }}</td>
|
||||
<td>{{ period.period.upper }}</td>
|
||||
<td>{{ period.membership_exists }}</td>
|
||||
<td></td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% else %}
|
||||
{% trans "No memberships" %}
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
|
||||
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
@ -1,14 +1,8 @@
|
|||
{% extends "base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block head_title %}
|
||||
{% trans "Membership" %}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="content-view">
|
||||
<h2>Membership settings</h2>
|
||||
{% if not current_membership %}
|
||||
<p>{% trans "You do not have an active membership!" %}</p>
|
||||
|
||||
|
@ -26,38 +20,4 @@
|
|||
<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 %}
|
||||
</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 %}
|
||||
{% endblock %}
|
||||
|
|
|
@ -1,20 +1,20 @@
|
|||
from django.contrib.auth.decorators import login_required
|
||||
from django.contrib.auth.decorators import permission_required
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from zen_queries import render
|
||||
|
||||
from .permissions import ADMINISTRATE_MEMBERS
|
||||
from .selectors import get_member
|
||||
from .selectors import get_members
|
||||
from .selectors import get_memberships
|
||||
from .selectors import get_subscription_periods
|
||||
from utils.view_utils import render
|
||||
from utils.view_utils import render_list
|
||||
from utils.view_utils import RowAction
|
||||
|
||||
|
||||
@login_required
|
||||
def membership_overview(request):
|
||||
memberships = get_memberships(member=request.user)
|
||||
memberships = get_memberships(user=request.user)
|
||||
current_membership = memberships.current()
|
||||
previous_memberships = memberships.previous()
|
||||
|
||||
|
@ -39,8 +39,6 @@ def members_admin(request):
|
|||
users = get_members()
|
||||
|
||||
return render_list(
|
||||
entity_name="member",
|
||||
entity_name_plural="members",
|
||||
request=request,
|
||||
paginate_by=20,
|
||||
objects=users,
|
||||
|
@ -70,7 +68,6 @@ def members_admin_detail(request, member_id):
|
|||
context = {
|
||||
"member": member,
|
||||
"subscription_periods": subscription_periods,
|
||||
"base_path": "admin-members",
|
||||
}
|
||||
|
||||
return render(
|
||||
|
|
5
src/project/context_processors.py
Normal file
5
src/project/context_processors.py
Normal file
|
@ -0,0 +1,5 @@
|
|||
from django.contrib.sites.shortcuts import get_current_site
|
||||
|
||||
|
||||
def current_site(request):
|
||||
return {"site": get_current_site(request)}
|
|
@ -65,7 +65,6 @@ MIDDLEWARE = [
|
|||
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
||||
"django.contrib.messages.middleware.MessageMiddleware",
|
||||
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
||||
"allauth.account.middleware.AccountMiddleware",
|
||||
]
|
||||
|
||||
ROOT_URLCONF = "project.urls"
|
||||
|
@ -81,9 +80,7 @@ TEMPLATES = [
|
|||
"django.template.context_processors.request",
|
||||
"django.contrib.auth.context_processors.auth",
|
||||
"django.contrib.messages.context_processors.messages",
|
||||
],
|
||||
"builtins": [
|
||||
"django.templatetags.i18n",
|
||||
"project.context_processors.current_site",
|
||||
],
|
||||
},
|
||||
},
|
||||
|
|
|
@ -1,384 +0,0 @@
|
|||
/* 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.
|
@ -1,200 +0,0 @@
|
|||
@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");
|
||||
}
|
|
@ -4,61 +4,53 @@
|
|||
|
||||
{% 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">
|
||||
{{ error }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
<h2>Log in</h2>
|
||||
{% if form.non_field_errors %}
|
||||
{% for error in form.non_field_errors %}
|
||||
<div class="alert alert-danger" role="alert">
|
||||
{{ error }}
|
||||
</div>
|
||||
{% endfor %}
|
||||
{% endif %}
|
||||
|
||||
<form method="post" action="">
|
||||
{% csrf_token %}
|
||||
<div>
|
||||
<label for="id_username"
|
||||
class="visually-hidden">
|
||||
{% trans "E-mail" %}
|
||||
</label>
|
||||
<input type="text"
|
||||
id="id_username"
|
||||
name="login"
|
||||
class="form-control mb-lg-2"
|
||||
placeholder="{% trans "E-mail" %}"
|
||||
required
|
||||
autofocus>
|
||||
</div>
|
||||
<div>
|
||||
<label for="id_password" class="visually-hidden">
|
||||
{% trans "Password" %}
|
||||
</label>
|
||||
<input type="password"
|
||||
id="id_password"
|
||||
name="password"
|
||||
class="form-control mb-lg-2"
|
||||
placeholder="{% trans "Password" %}"
|
||||
required>
|
||||
</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>
|
||||
</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>
|
||||
<form method="post" action="">
|
||||
{% csrf_token %}
|
||||
|
||||
<label for="id_username"
|
||||
class="visually-hidden">
|
||||
{% trans "E-mail" %}
|
||||
</label>
|
||||
<input type="text"
|
||||
id="id_username"
|
||||
name="login"
|
||||
class="form-control mb-lg-2"
|
||||
placeholder="{% trans "E-mail" %}"
|
||||
required
|
||||
autofocus>
|
||||
|
||||
<label for="id_password" class="visually-hidden">
|
||||
{% trans "Password" %}
|
||||
</label>
|
||||
<input type="password"
|
||||
id="id_password"
|
||||
name="password"
|
||||
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>
|
||||
</form>
|
||||
<a href="{% url "account_reset_password" %}"
|
||||
class="w-100 btn btn-lg btn-outline-info">
|
||||
{% trans "Forgot password?" %}
|
||||
</a>
|
||||
|
||||
<hr class="hr-text" data-content="{% trans "Or"|upper %}">
|
||||
|
||||
<a class="w-100 btn btn-lg btn-outline-success"
|
||||
type="submit"
|
||||
href="{% url "account_signup" %}">
|
||||
{% trans "Become a member" %}
|
||||
</a>
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
@ -5,18 +5,17 @@
|
|||
{% block head_title %}{% trans "Sign Out" %}{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="content-view">
|
||||
<h2>{% trans "Sign Out" %}</h2>
|
||||
<h1>{% trans "Sign Out" %}</h1>
|
||||
|
||||
<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 %}
|
||||
|
|
|
@ -7,13 +7,71 @@
|
|||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="description" content="">
|
||||
<title>Login – {{ site.name }}</title>
|
||||
<link rel="stylesheet" href="{% static "/fonts/inter.css" %}">
|
||||
<link rel="stylesheet" href="{% static "/css/style.css" %}">
|
||||
|
||||
<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:after {
|
||||
content: attr(data-content);
|
||||
padding: 0 4px;
|
||||
position: relative;
|
||||
top: -13px;
|
||||
background-color: #a8f3f4;
|
||||
}
|
||||
|
||||
</style>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<main id="login">
|
||||
{% block non_login_content %}
|
||||
{% endblock %}
|
||||
</main>
|
||||
<body class="text-center">
|
||||
|
||||
<main class="form-signin">
|
||||
<img class="mb-4" src="https://new.data.coop/static/img/logo_da.svg" alt=""
|
||||
width="260" height="160">
|
||||
|
||||
{% block non_login_content %}
|
||||
{% endblock %}
|
||||
</main>
|
||||
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -3,82 +3,211 @@
|
|||
{% load static %}
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<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>
|
||||
<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>
|
||||
|
||||
{% if current_membership %}
|
||||
<dl>
|
||||
<dt>Membership</dt>
|
||||
<dd>
|
||||
Active
|
||||
</dd>
|
||||
<link href="{% static "/css/bootstrap.min.css" %}" rel="stylesheet"
|
||||
crossorigin="anonymous">
|
||||
|
||||
<dt>Period</dt>
|
||||
<dd>
|
||||
Until {{ current_period.upper }} <span class="time_remaining">({{ current_period.upper|timeuntil }})</span>
|
||||
</dd>
|
||||
<link href="{% static "/css/bootstrap-icons.css" %}" rel="stylesheet"
|
||||
crossorigin="anonymous">
|
||||
|
||||
<dt>Membership type</dt>
|
||||
<dd>Normal member</dd>
|
||||
</dl>
|
||||
{% else %}
|
||||
Your membership status will be displayed here in the future.
|
||||
{% endif %}
|
||||
</div>
|
||||
</aside>
|
||||
<nav>
|
||||
<ol>
|
||||
<li>
|
||||
<a href="/" class="{% active_path "index" "current" %}">
|
||||
Dashboard
|
||||
<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>
|
||||
|
||||
{% comment %}
|
||||
<li>
|
||||
<a href="/services" class="{% active_path "services" "current" %}">
|
||||
Services
|
||||
<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>
|
||||
{% endcomment %}
|
||||
|
||||
<li>
|
||||
<a href="{% url "account_email" %}" class="{% active_path "account_email" "current" %}">
|
||||
Email
|
||||
<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>
|
||||
|
||||
{% if perms.membership.administrate_memberships %}
|
||||
<li>
|
||||
<a href="{% url "admin-members" %}" class="{% active_path "admin-members" "current" %}">
|
||||
Admin
|
||||
<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>
|
||||
{% endif %}
|
||||
</ol>
|
||||
</nav>
|
||||
<article>
|
||||
{% block content %}{% endblock %}
|
||||
</article>
|
||||
</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>
|
||||
{% endif %}
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<main class="col-md-9 ms-sm-auto col-lg-10 px-md-4 p-4">
|
||||
{% block content %}
|
||||
{% endblock %}
|
||||
</main>
|
||||
<footer>
|
||||
data.coop membersystem version 0.0.1
|
||||
</footer>
|
||||
</body>
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,28 +1 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% 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 %}
|
||||
|
|
|
@ -1,62 +1,10 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<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 …</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 …</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 …</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 …</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 …</a>
|
||||
</div>
|
||||
<a>Subscribe</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endcomment %}
|
||||
<p>
|
||||
Services and signup to these will be
|
||||
</p>
|
||||
<p>
|
||||
This is yet to be implemented.
|
||||
</p>
|
||||
{% endblock %}
|
||||
|
|
|
@ -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"),
|
||||
path("services/", login_required(services_overview), name="services-overview"),
|
||||
path("membership/", membership_overview, name="membership-overview"),
|
||||
path("admin/members/", members_admin, name="admin-members"),
|
||||
path(
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
from utils.view_utils import render
|
||||
from django.shortcuts import render
|
||||
|
||||
|
||||
def index(request):
|
||||
|
|
|
@ -3,6 +3,7 @@ from django.utils.translation import gettext_lazy as _
|
|||
|
||||
|
||||
class CreatedModifiedAbstract(models.Model):
|
||||
|
||||
modified = models.DateTimeField(auto_now=True, verbose_name=_("modified"))
|
||||
created = models.DateTimeField(auto_now_add=True, verbose_name=_("created"))
|
||||
|
||||
|
|
|
@ -1,16 +1,10 @@
|
|||
{% extends "base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block head_title %}
|
||||
{{ entity_name_plural|capfirst }}
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
|
||||
<div class="content-view">
|
||||
|
||||
<h1>
|
||||
{{ entity_name_plural|capfirst }} <small class="text-muted">{{ total_count }}</small>
|
||||
Users <small class="text-muted">{{ total_count }}</small>
|
||||
</h1>
|
||||
|
||||
<table class="table table-striped">
|
||||
|
@ -50,6 +44,7 @@
|
|||
</table>
|
||||
|
||||
{% if is_paginated %}
|
||||
<nav aria-label="Page navigation example">
|
||||
<ul class="pagination justify-content-center">
|
||||
|
||||
{% if not page.has_previous %}
|
||||
|
@ -102,9 +97,8 @@
|
|||
{% endif %}
|
||||
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
|
||||
{% endblock %}
|
||||
|
|
|
@ -5,17 +5,9 @@ register = template.Library()
|
|||
|
||||
|
||||
@register.simple_tag(takes_context=True)
|
||||
def active_path(context, path_name, class_name) -> str | None:
|
||||
"""Return the given class name if the current path matches the given path name."""
|
||||
|
||||
def active_path(context, path_name, class_name):
|
||||
path = reverse(path_name)
|
||||
request_path = context.get("request").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:
|
||||
if path == request_path or ("basepath" in context and context["basepath"] == path):
|
||||
return class_name
|
||||
|
|
|
@ -1,16 +1,13 @@
|
|||
import contextlib
|
||||
from dataclasses import dataclass
|
||||
from typing import Any
|
||||
|
||||
from django.contrib.sites.shortcuts import get_current_site
|
||||
from django.core.exceptions import FieldError
|
||||
from django.core.paginator import Paginator
|
||||
from django.db.models import Model
|
||||
from django.http import HttpRequest
|
||||
from django.http import HttpResponse
|
||||
from django.urls import reverse
|
||||
from zen_queries import queries_disabled
|
||||
from zen_queries import render as zen_queries_render
|
||||
from zen_queries import render
|
||||
|
||||
|
||||
@dataclass
|
||||
|
@ -46,8 +43,6 @@ class RowAction:
|
|||
|
||||
def render_list(
|
||||
request: HttpRequest,
|
||||
entity_name: str,
|
||||
entity_name_plural: str,
|
||||
objects: list["Model"],
|
||||
columns: list[tuple[str, str]],
|
||||
row_actions: list[RowAction] = None,
|
||||
|
@ -75,12 +70,11 @@ def render_list(
|
|||
|
||||
rows = []
|
||||
for obj in objects:
|
||||
with queries_disabled():
|
||||
row = Row(
|
||||
data={column: getattr(obj, column[0]) for column in columns},
|
||||
actions=[action.render(obj) for action in row_actions],
|
||||
)
|
||||
rows.append(row)
|
||||
row = Row(
|
||||
data={column: getattr(obj, column[0]) for column in columns},
|
||||
actions=[action.render(obj) for action in row_actions],
|
||||
)
|
||||
rows.append(row)
|
||||
|
||||
context = {
|
||||
"rows": rows,
|
||||
|
@ -89,8 +83,6 @@ def render_list(
|
|||
"list_actions": list_actions,
|
||||
"total_count": total_count,
|
||||
"order_by": order_by,
|
||||
"entity_name": entity_name,
|
||||
"entity_name_plural": entity_name_plural,
|
||||
}
|
||||
|
||||
if paginate_by:
|
||||
|
@ -104,20 +96,3 @@ def render_list(
|
|||
template_name="utils/list.html",
|
||||
context=context,
|
||||
)
|
||||
|
||||
|
||||
def base_context(request: HttpRequest) -> dict[str, Any]:
|
||||
"""
|
||||
Return a base context for all views.
|
||||
"""
|
||||
return {"site": get_current_site(request)}
|
||||
|
||||
|
||||
def render(request, template_name, context=None):
|
||||
"""
|
||||
Render a template with a base context.
|
||||
"""
|
||||
if context is None:
|
||||
context = {}
|
||||
context = base_context(request) | context
|
||||
return zen_queries_render(request, template_name, context)
|
||||
|
|
Loading…
Reference in a new issue