Author SHA1 Message Date
nikolark 56efe6760e 💄 Fix deposit 2022-04-17 16:37:42 +02:00
nikolark ce31ef84cc 💄 Pretty deposit user select 2022-04-17 16:29:18 +02:00
nikolark 491604c720 🚚 Move ProductCard and UserCard
closer to their feature
2022-04-17 16:04:54 +02:00
nikolark 05d3dcbb3e 💄 Style product grid, rm images
♻ Move Wireframe to own component
2022-04-17 16:01:09 +02:00
nikolark 8577fb9f11 ⬆ Bump minor frontend deps
♻ Stop using React.FC
2022-04-17 15:00:10 +02:00
nikolark a038340e4a 📝 TODO 2022-03-27 21:42:27 +02:00
nikolark 34a01e9293 💄 move grid behind everything else 2022-03-27 21:41:30 +02:00
nikolark 54dd21f1eb 💄 Make you nauseous 2022-03-22 23:59:31 +01:00
nikolark 53ba5a05a1 ✨ Add credits
🐛 Fix navigating below page zero
2022-03-06 21:16:53 +01:00
nikolark f775446224 💄 Style Navlink, primary buttons and headings 2022-03-06 15:16:16 +01:00
nikolark 13cc0d92a2 💄 Put new brand in header and fix toasts 2022-03-06 14:27:52 +01:00
nikolark 7bfa587a77 💄 Avatar rhombus 2022-03-06 13:34:05 +01:00
nikolark 3d91148b7e 💄 First round of styling
✨ Added new splash
2022-03-06 13:13:08 +01:00
nikolark 54bbd75730 🔥 Remove all things bootstrap 2022-03-05 20:00:03 +01:00
nikolark 06f43b06b8 ⚡ Avoid redirect 2022-02-20 12:36:15 +01:00
nikolark ad116c2c28 💚 Update pytest config with new app name 2022-02-20 01:33:16 +01:00
nikolark eb960bdb84 🔧 Update scripts with new path 2022-02-20 01:32:36 +01:00
nikolark f4c99153ca 🔧 Point VITE_API_URL to the right place 2022-02-20 01:31:03 +01:00
nikolark 3e457de416 🐛 Fix VITE_API_URL reference 2022-02-20 01:25:55 +01:00
nikolark faca98b7f9 🔧 Reference static assests from / 2022-02-20 01:20:41 +01:00
nikolark 53ef262ba4 🔧 Build frontend on deploy 2022-02-20 01:16:13 +01:00
nikolark f4749e6a6e 🔧 output frontend build to root static folder 2022-02-20 01:12:37 +01:00
nikolark e5de16f227 🐛 Remove 3.9 type syntax 2022-02-20 00:57:12 +01:00
nikolark eace79f012 ⬇ Downgrade to python 3.8 2022-02-20 00:54:00 +01:00
nikolark 9b903f42d6 🚚 Rename app main to bbs
🚚 Move settings into main
➕ Add django-rename-app
2022-02-20 00:39:16 +01:00
nikolark e1eaf3df98 💚 Try fixing deploy script 2022-02-20 00:17:51 +01:00
nikolark b79d9fc9c8 👷 Add ssh config 2022-02-20 00:08:15 +01:00
nikolark 42f23b1d77 Merge branch 'chore/bbs-bumps' into 'master'
💥 Rewrite in react

See merge request edb/bbs!1
2022-02-19 22:39:07 +00:00
92 changed files with 2215 additions and 1512 deletions

No files matched your search

+1
View File
@@ -0,0 +1 @@
VITE_API_URL=http://localhost:3000
+2
View File
@@ -12,3 +12,5 @@ media/
tmp/ tmp/
frontend/dist frontend/dist
static/modern static/modern
.python-version
.env
+12 -1
View File
@@ -2,7 +2,7 @@ variables:
PIP_CACHE_DIR: "${CI_PROJECT_DIR}/.cache/pip" PIP_CACHE_DIR: "${CI_PROJECT_DIR}/.cache/pip"
default: default:
image: nikolaik/python-nodejs:python3.9-nodejs16 image: nikolaik/python-nodejs:python3.8-nodejs16
cache: cache:
paths: paths:
- .cache/pip - .cache/pip
@@ -29,6 +29,17 @@ lint:
deploy: deploy:
stage: deploy stage: deploy
before_script:
# Install ssh if not already
- 'command -v ssh-agent >/dev/null || ( apt-get update -y && apt-get install openssh-client -y )'
- eval $(ssh-agent -s)
# Make SSH key available
- echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add -
- mkdir -p ~/.ssh
- chmod 700 ~/.ssh
# Add known host keys
- echo "$SSH_KNOWN_HOSTS" >> ~/.ssh/known_hosts
- chmod 644 ~/.ssh/known_hosts
script: script:
- ssh gitdeploy@dreamcast.neuf.no "$(< bin/deploy)" - ssh gitdeploy@dreamcast.neuf.no "$(< bin/deploy)"
only: only:
+2 -2
View File
@@ -9,6 +9,7 @@ poetry shell
pre-commit install pre-commit install
python manage.py migrate python manage.py migrate
bin/run bin/run
cp .env-default .env
# in another terminal # in another terminal
cd frontend cd frontend
@@ -22,9 +23,8 @@ npm start
## TODO ## TODO
- update emails - improve product listing (light purple background)
- Stats: products over time - Stats: products over time
- Light on dark colors
- inventory views - inventory views
View File
File renamed without changes.
+1 -1
View File
@@ -1,6 +1,6 @@
from django.contrib import admin from django.contrib import admin
from main.models import * from bbs.models import *
class ProductAdmin(admin.ModelAdmin): class ProductAdmin(admin.ModelAdmin):
View File
File renamed without changes.
+3 -1
View File
@@ -1,3 +1,5 @@
from typing import List
import strawberry import strawberry
@@ -17,6 +19,6 @@ class FieldError(Error):
@strawberry.type @strawberry.type
class FormErrors: class FormErrors:
fields: list[FieldError] fields: List[FieldError]
message: str = "" message: str = ""
error: bool = True error: bool = True
File renamed without changes.
@@ -1,3 +1,5 @@
from typing import List
import strawberry import strawberry
from django.conf import settings from django.conf import settings
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
@@ -5,10 +7,10 @@ from django.db import transaction
from django.shortcuts import get_object_or_404 from django.shortcuts import get_object_or_404
from strawberry import ID from strawberry import ID
from main import models from bbs import models
from main.api.errors import Error, FieldError, FormErrors from bbs.api.errors import Error, FieldError, FormErrors
from main.api.types.inventory import Transaction from bbs.api.types.inventory import Transaction
from main.api.types.shop import Order from bbs.api.types.shop import Order
UserModel = get_user_model() UserModel = get_user_model()
@@ -34,7 +36,7 @@ CreateOrderResponse = strawberry.union("CreateOrderResponse", [CreateOrderSucces
@strawberry.mutation @strawberry.mutation
@transaction.atomic @transaction.atomic
def create_order(customer_id: ID, order_lines: list[OrderLineInput], is_external: bool = False) -> CreateOrderResponse: def create_order(customer_id: ID, order_lines: List[OrderLineInput], is_external: bool = False) -> CreateOrderResponse:
# clean and validate # clean and validate
try: try:
user = UserModel.objects.get(pk=customer_id) user = UserModel.objects.get(pk=customer_id)
@@ -4,8 +4,8 @@ from django.contrib.auth import get_user_model
from django.db import transaction from django.db import transaction
from strawberry_django import auto from strawberry_django import auto
from main import models from bbs import models
from main.api.types.users import User from bbs.api.types.users import User
DjangoUser = get_user_model() DjangoUser = get_user_model()
File renamed without changes.
+11
View File
@@ -0,0 +1,11 @@
from typing import List
import strawberry
import strawberry_django
from bbs.api.types.inventory import Transaction, TransactionOrdering
@strawberry.type
class InventoryQueries:
transaction_list: List[Transaction] = strawberry_django.field(pagination=True, order=TransactionOrdering)
@@ -1,15 +1,17 @@
from typing import List
import strawberry import strawberry
import strawberry_django import strawberry_django
from main import models from bbs import models
from main.api.types.shop import Order, OrderOrdering, OrderStats, OrderStatsByTime, Point, Product, ProductOrderStat from bbs.api.types.shop import Order, OrderOrdering, OrderStats, OrderStatsByTime, Point, Product, ProductOrderStat
@strawberry.type @strawberry.type
class ShopQueries: class ShopQueries:
all_products: list[Product] = strawberry_django.field() all_products: List[Product] = strawberry_django.field()
all_orders: list[Order] = strawberry_django.field() all_orders: List[Order] = strawberry_django.field()
order_list: list[Order] = strawberry_django.field(pagination=True, order=OrderOrdering) order_list: List[Order] = strawberry_django.field(pagination=True, order=OrderOrdering)
@strawberry.field @strawberry.field
def order_stats(self) -> OrderStats: def order_stats(self) -> OrderStats:
@@ -24,7 +26,7 @@ class ShopQueries:
) )
@strawberry.field @strawberry.field
def product_stats(self) -> list[ProductOrderStat]: def product_stats(self) -> List[ProductOrderStat]:
return [ return [
ProductOrderStat(product_name=stat["product_name"], data=[Point(**p) for p in stat["data"]]) ProductOrderStat(product_name=stat["product_name"], data=[Point(**p) for p in stat["data"]])
for stat in models.OrderLine.objects.product_order_stats().as_series() for stat in models.OrderLine.objects.product_order_stats().as_series()
@@ -1,14 +1,16 @@
from typing import List
import strawberry import strawberry
import strawberry_django import strawberry_django
from django.contrib.auth.models import User as DjangoUser from django.contrib.auth.models import User as DjangoUser
from strawberry import ID from strawberry import ID
from main.api.types.users import User from bbs.api.types.users import User
@strawberry.type @strawberry.type
class UsersQueries: class UsersQueries:
all_users: list[User] = strawberry_django.field() all_users: List[User] = strawberry_django.field()
@strawberry.field @strawberry.field
def user(self, user_id: ID) -> User: def user(self, user_id: ID) -> User:
+20
View File
@@ -0,0 +1,20 @@
import strawberry
from bbs.api.mutations.shop import ShopMutations
from bbs.api.mutations.users import UsersMutations
from bbs.api.queries.inventory import InventoryQueries
from bbs.api.queries.shop import ShopQueries
from bbs.api.queries.users import UsersQueries
@strawberry.type
class Query(InventoryQueries, ShopQueries, UsersQueries):
pass
@strawberry.type
class Mutation(ShopMutations, UsersMutations):
pass
schema = strawberry.Schema(query=Query, mutation=Mutation)
File renamed without changes.
@@ -1,8 +1,8 @@
import strawberry_django import strawberry_django
from strawberry_django import auto from strawberry_django import auto
from main import models from bbs import models
from main.api.types.users import User from bbs.api.types.users import User
@strawberry_django.ordering.order(models.Transaction) @strawberry_django.ordering.order(models.Transaction)
@@ -1,9 +1,11 @@
from typing import List
import strawberry import strawberry
import strawberry_django import strawberry_django
from strawberry_django import auto from strawberry_django import auto
from main import models from bbs import models
from main.api.types.utils import JSONScalar from bbs.api.types.utils import JSONScalar
@strawberry_django.filters.filter(models.Product) @strawberry_django.filters.filter(models.Product)
@@ -31,10 +33,10 @@ class OrderOrdering:
@strawberry_django.type(models.Order) @strawberry_django.type(models.Order)
class Order: class Order:
id: auto id: auto
customer: strawberry.LazyType["User", "main.api.types.users"] customer: strawberry.LazyType["User", "bbs.api.types.users"]
order_sum: auto order_sum: auto
created: auto created: auto
orderlines: list["OrderLine"] orderlines: List["OrderLine"]
is_external: bool is_external: bool
@@ -55,10 +57,10 @@ class OrderStatsByTime:
@strawberry.type @strawberry.type
class OrderStats: class OrderStats:
yearly: list[OrderStatsByTime] yearly: List[OrderStatsByTime]
monthly: list[OrderStatsByTime] monthly: List[OrderStatsByTime]
daily: list[OrderStatsByTime] daily: List[OrderStatsByTime]
hourly: list[OrderStatsByTime] hourly: List[OrderStatsByTime]
@strawberry.type @strawberry.type
@@ -70,4 +72,4 @@ class Point:
@strawberry.type @strawberry.type
class ProductOrderStat: class ProductOrderStat:
product_name: str product_name: str
data: list[Point] data: List[Point]
@@ -1,12 +1,13 @@
from datetime import datetime from datetime import datetime
from typing import List
import strawberry import strawberry
import strawberry_django import strawberry_django
from django.contrib.auth import get_user_model from django.contrib.auth import get_user_model
from strawberry_django import auto from strawberry_django import auto
from main import models from bbs import models
from main.api.types.shop import Point, ProductOrderStat from bbs.api.types.shop import Point, ProductOrderStat
UserModel = get_user_model() UserModel = get_user_model()
@@ -27,11 +28,11 @@ class UserProfile:
order_sum_total: float order_sum_total: float
@strawberry.field() @strawberry.field()
def top_months(self) -> list[TopMonth]: def top_months(self) -> List[TopMonth]:
return [TopMonth(**month) for month in self.top_months()] return [TopMonth(**month) for month in self.top_months()]
@strawberry.field() @strawberry.field()
def product_order_stats(self) -> list[ProductOrderStat]: def product_order_stats(self) -> List[ProductOrderStat]:
return [ return [
ProductOrderStat(product_name=stat["product_name"], data=[Point(**p) for p in stat["data"]]) ProductOrderStat(product_name=stat["product_name"], data=[Point(**p) for p in stat["data"]])
for stat in self.product_order_stats().as_series() for stat in self.product_order_stats().as_series()
File renamed without changes.
+1 -1
View File
@@ -2,7 +2,7 @@ from django.apps import AppConfig
class BBSAppConfig(AppConfig): class BBSAppConfig(AppConfig):
name = "main" name = "bbs"
verbose_name = "Biceps Bar System" verbose_name = "Biceps Bar System"
def ready(self): def ready(self):
+1 -1
View File
@@ -1,6 +1,6 @@
from django import forms from django import forms
from main.models import InventoryTransaction, Product from bbs.models import InventoryTransaction, Product
class InventoryTransactionForm(forms.ModelForm): class InventoryTransactionForm(forms.ModelForm):
File renamed without changes.
File renamed without changes.
@@ -1,10 +1,10 @@
from pathlib import Path from pathlib import Path
from django.conf import settings
from django.core.management import BaseCommand, CommandParser from django.core.management import BaseCommand, CommandParser
from strawberry.printer import print_schema from strawberry.printer import print_schema
from bbssite.settings import BASE_DIR from bbs.api.schema import schema as schema_symbol
from main.api.schema import schema as schema_symbol
class Command(BaseCommand): class Command(BaseCommand):
@@ -16,7 +16,7 @@ class Command(BaseCommand):
def handle(self, *args, filename=None, **options): def handle(self, *args, filename=None, **options):
schema_output = print_schema(schema_symbol) schema_output = print_schema(schema_symbol)
if filename != "-": if filename != "-":
with Path(BASE_DIR, filename).open("w+") as fp: with Path(settings.BASE_DIR, filename).open("w+") as fp:
fp.write(schema_output) fp.write(schema_output)
self.stdout.write(self.style.SUCCESS("OK")) self.stdout.write(self.style.SUCCESS("OK"))
else: else:
@@ -1,6 +1,6 @@
from django.core.management.base import BaseCommand, CommandParser from django.core.management.base import BaseCommand, CommandParser
from main.models import UserProfile from bbs.models import UserProfile
class Command(BaseCommand): class Command(BaseCommand):
@@ -72,7 +72,7 @@ class Migration(migrations.Migration):
models.ForeignKey( models.ForeignKey(
on_delete=models.CASCADE, on_delete=models.CASCADE,
related_name="orderlines", related_name="orderlines",
to="main.Order", to="bbs.Order",
), ),
), ),
], ],
@@ -157,7 +157,7 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name="orderline", model_name="orderline",
name="product", name="product",
field=models.ForeignKey(on_delete=models.CASCADE, related_name="orderlines", to="main.Product"), field=models.ForeignKey(on_delete=models.CASCADE, related_name="orderlines", to="bbs.Product"),
), ),
migrations.AddField( migrations.AddField(
model_name="inventorytransaction", model_name="inventorytransaction",
@@ -166,7 +166,7 @@ class Migration(migrations.Migration):
on_delete=models.CASCADE, on_delete=models.CASCADE,
related_name="transactions", related_name="transactions",
verbose_name="Produkt", verbose_name="Produkt",
to="main.Product", to="bbs.Product",
), ),
), ),
migrations.AddField( migrations.AddField(
File renamed without changes.
+3 -3
View File
@@ -146,8 +146,8 @@ class OrderLineManager(models.QuerySet):
class OrderLine(models.Model): class OrderLine(models.Model):
order = models.ForeignKey("main.Order", on_delete=models.CASCADE, related_name="orderlines") order = models.ForeignKey("bbs.Order", on_delete=models.CASCADE, related_name="orderlines")
product = models.ForeignKey("main.Product", on_delete=models.CASCADE, related_name="orderlines") product = models.ForeignKey("bbs.Product", on_delete=models.CASCADE, related_name="orderlines")
amount = models.IntegerField() amount = models.IntegerField()
unit_price = models.FloatField(verbose_name="Enhetspris") unit_price = models.FloatField(verbose_name="Enhetspris")
@@ -184,7 +184,7 @@ class InventoryTransaction(models.Model):
related_name="inventory_transactions", related_name="inventory_transactions",
) )
product = models.ForeignKey( product = models.ForeignKey(
"main.Product", "bbs.Product",
on_delete=models.CASCADE, on_delete=models.CASCADE,
related_name="transactions", related_name="transactions",
verbose_name="Produkt", verbose_name="Produkt",
+3 -2
View File
@@ -72,7 +72,7 @@ MIDDLEWARE = [
"django.contrib.messages.middleware.MessageMiddleware", "django.contrib.messages.middleware.MessageMiddleware",
] ]
ROOT_URLCONF = "bbssite.urls" ROOT_URLCONF = "bbs.urls"
INSTALLED_APPS = [ INSTALLED_APPS = [
"django.contrib.auth", "django.contrib.auth",
@@ -85,9 +85,10 @@ INSTALLED_APPS = [
"django_extensions", "django_extensions",
"easy_thumbnails", "easy_thumbnails",
"corsheaders", "corsheaders",
"django_rename_app",
] ]
LOCAL_APPS = [ LOCAL_APPS = [
"main", "bbs",
] ]
INSTALLED_APPS += LOCAL_APPS INSTALLED_APPS += LOCAL_APPS
@@ -2,7 +2,7 @@ import pytest
from django.test import Client from django.test import Client
from strawberry.django.test import GraphQLTestClient from strawberry.django.test import GraphQLTestClient
from main.models import Order, Product from bbs.models import Order, Product
@pytest.fixture(name="gql_client") @pytest.fixture(name="gql_client")
Executable → Regular
+7 -4
View File
@@ -2,16 +2,19 @@ from django.conf import settings
from django.conf.urls.static import static from django.conf.urls.static import static
from django.contrib import admin from django.contrib import admin
from django.contrib.auth.views import LoginView, LogoutView from django.contrib.auth.views import LoginView, LogoutView
from django.urls import include, path, re_path from django.urls import path, re_path
from strawberry.django.views import GraphQLView from strawberry.django.views import GraphQLView
from main import urls as main_urls from bbs.api.schema import schema
from main.api.schema import schema from bbs.views.reports import inventory, inventory_add, report
admin.autodiscover() admin.autodiscover()
urlpatterns = [ urlpatterns = [
re_path(r"", include(main_urls)), # TODO: migrate reports to SPA
re_path(r"^inventory/$", inventory, name="inventory"),
re_path(r"^inventory/add/$", inventory_add, name="inventory-add"),
re_path(r"^report$", report, name="report"),
path("accounts/login/", LoginView.as_view(), name="django.contrib.auth.views.login"), path("accounts/login/", LoginView.as_view(), name="django.contrib.auth.views.login"),
path("accounts/logout/", LogoutView.as_view(), name="django.contrib.auth.views.logout"), path("accounts/logout/", LogoutView.as_view(), name="django.contrib.auth.views.logout"),
path("admin/", admin.site.urls), path("admin/", admin.site.urls),
View File
File renamed without changes.
File renamed without changes.
@@ -4,8 +4,8 @@ from itertools import groupby
from django.contrib import messages from django.contrib import messages
from django.shortcuts import redirect, render from django.shortcuts import redirect, render
from main.forms import DateRangeForm, InventoryTransactionForm from bbs.forms import DateRangeForm, InventoryTransactionForm
from main.models import InventoryTransaction, Order, Product, Transaction, UserProfile from bbs.models import InventoryTransaction, Order, Product, Transaction, UserProfile
def report(request): def report(request):
+1 -1
View File
@@ -2,6 +2,6 @@ import os
from django.core.wsgi import get_wsgi_application from django.core.wsgi import get_wsgi_application
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "bbssite.settings") os.environ.setdefault("DJANGO_SETTINGS_MODULE", "bbs.settings")
application = get_wsgi_application() application = get_wsgi_application()
+5
View File
@@ -0,0 +1,5 @@
#!/bin/bash
set -eu
cd frontend
npm i
npm run build
+10 -2
View File
@@ -1,14 +1,22 @@
#!/bin/bash #!/bin/bash
set -eu set -eu
export VITE_API_URL="https://bbs.neuf.no/api"
PROJECT_PATH=/var/www/neuf.no/bbs PROJECT_PATH=/var/www/neuf.no/bbs
PROCESS_NAME="$PROJECT.neuf.no" PROCESS_NAME="bbs.neuf.no"
echo "Deploying bbs to ${PROJECT_PATH}"
cd "$PROJECT_PATH" || exit 1 cd "$PROJECT_PATH" || exit 1
git pull git pull
# Backend
poetry install poetry install
umask 022; poetry run python manage.py collectstatic --noinput # Collect static umask 022; poetry run python manage.py collectstatic --noinput # Collect static
poetry run python manage.py migrate # Run DB migrations poetry run python manage.py migrate # Run DB migrations
# Reload # Frontend
bin/build-frontend
# Reload backend
echo "Reloading supervisor process ${PROCESS_NAME}"
sudo /usr/bin/supervisorctl pid "$PROCESS_NAME" | xargs kill -HUP sudo /usr/bin/supervisorctl pid "$PROCESS_NAME" | xargs kill -HUP
+1 -1
View File
@@ -1,6 +1,6 @@
#!/bin/bash #!/bin/bash
SRC_DIR="main bbssite" SRC_DIR="bbs"
black $SRC_DIR black $SRC_DIR
isort $SRC_DIR isort $SRC_DIR
+1 -1
View File
@@ -1,6 +1,6 @@
#!/bin/bash #!/bin/bash
SRC_DIR="main bbssite" SRC_DIR="bbs"
black --check $SRC_DIR black --check $SRC_DIR
isort --diff $SRC_DIR isort --diff $SRC_DIR
+3
View File
@@ -1 +1,4 @@
#!/bin/bash
cd frontend && npm run start cd frontend && npm run start
+3
View File
@@ -3,6 +3,9 @@
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/src/favicon.svg" /> <link rel="icon" type="image/svg+xml" href="/src/favicon.svg" />
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=IBM+Plex+Mono&display=swap" rel="stylesheet">
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>BBS</title> <title>BBS</title>
</head> </head>
+946 -981
View File
File diff suppressed because it is too large. Load diff
+21 -24
View File
@@ -16,51 +16,48 @@
"test": "jest" "test": "jest"
}, },
"dependencies": { "dependencies": {
"@apollo/client": "^3.5.8", "@apollo/client": "^3.5.10",
"@nivo/bar": "^0.79.1", "@nivo/bar": "^0.79.1",
"@nivo/line": "^0.79.1", "@nivo/line": "^0.79.1",
"@nivo/pie": "^0.79.1", "@nivo/pie": "^0.79.1",
"bootstrap": "^5.1.3",
"bootstrap-icons": "^1.8.1",
"clsx": "^1.1.1", "clsx": "^1.1.1",
"date-fns": "^2.28.0", "date-fns": "^2.28.0",
"graphql": "^16.3.0", "graphql": "^16.3.0",
"react": "^17.0.2", "react": "^17.0.2",
"react-dom": "^17.0.2", "react-dom": "^17.0.2",
"react-hook-form": "^7.27.1", "react-hook-form": "^7.30.0",
"react-router-dom": "^6.2.1", "react-router-dom": "^6.3.0",
"use-url-search-params": "^2.5.1" "use-url-search-params": "^2.5.1"
}, },
"devDependencies": { "devDependencies": {
"@graphql-codegen/cli": "^2.6.1", "@graphql-codegen/cli": "^2.6.2",
"@graphql-codegen/typed-document-node": "^2.2.3", "@graphql-codegen/typed-document-node": "^2.2.8",
"@graphql-codegen/typescript-operations": "^2.3.0", "@graphql-codegen/typescript-operations": "^2.3.5",
"@graphql-typed-document-node/core": "^3.1.1", "@graphql-typed-document-node/core": "^3.1.1",
"@types/bootstrap": "^5.1.9", "@types/react": "^17.0.44",
"@types/react": "^17.0.39", "@types/react-dom": "^17.0.15",
"@types/react-dom": "^17.0.11",
"@types/react-router-dom": "^5.3.3", "@types/react-router-dom": "^5.3.3",
"@typescript-eslint/eslint-plugin": "^5.11.0", "@typescript-eslint/eslint-plugin": "^5.19.0",
"@typescript-eslint/parser": "^5.11.0", "@typescript-eslint/parser": "^5.19.0",
"@vitejs/plugin-react": "^1.2.0", "@vitejs/plugin-react": "^1.3.1",
"@vitejs/plugin-react-refresh": "^1.3.6", "@vitejs/plugin-react-refresh": "^1.3.6",
"eslint": "^8.9.0", "eslint": "^8.13.0",
"eslint-config-prettier": "^8.3.0", "eslint-config-prettier": "^8.5.0",
"eslint-plugin-import": "^2.25.4", "eslint-plugin-import": "^2.26.0",
"eslint-plugin-prettier": "^4.0.0", "eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-react": "^7.28.0", "eslint-plugin-react": "^7.29.4",
"eslint-plugin-react-hooks": "^4.3.0", "eslint-plugin-react-hooks": "^4.4.0",
"jest": "^27.5.1", "jest": "^27.5.1",
"prettier": "^2.5.1", "prettier": "^2.6.2",
"stylelint": "^14.5.0", "stylelint": "^14.7.0",
"stylelint-config-idiomatic-order": "^8.1.0", "stylelint-config-idiomatic-order": "^8.1.0",
"stylelint-config-prettier": "^9.0.3", "stylelint-config-prettier": "^9.0.3",
"stylelint-config-recommended": "^7.0.0", "stylelint-config-recommended": "^7.0.0",
"stylelint-order": "^5.0.0", "stylelint-order": "^5.0.0",
"stylelint-prettier": "^2.0.0", "stylelint-prettier": "^2.0.0",
"typescript": "^4.5.5", "typescript": "^4.6.3",
"vite": "^2.8.1", "vite": "^2.9.5",
"vite-plugin-svgr": "^1.0.1" "vite-plugin-svgr": "^1.1.0"
}, },
"engines": { "engines": {
"node": ">=16", "node": ">=16",
+2 -1
View File
@@ -1,3 +1,4 @@
.app { .app {
margin: 16px; margin: 1rem auto;
max-width: 1220px;
} }
+6 -2
View File
@@ -1,23 +1,27 @@
import { ApolloProvider } from "@apollo/client"; import { ApolloProvider } from "@apollo/client";
import React from "react"; import React from "react";
import { BrowserRouter as Router } from "react-router-dom"; import { BrowserRouter as Router } from "react-router-dom";
import "bootstrap/dist/css/bootstrap.min.css";
import styles from "./App.module.css"; import styles from "./App.module.css";
import { Routes } from "./Routes"; import { Routes } from "./Routes";
import client from "./client"; import client from "./client";
import { Footer } from "./components/Footer";
import { Header } from "./components/Header"; import { Header } from "./components/Header";
import { Splash as Splash } from "./components/Splash";
import { Toast } from "./components/Toast"; import { Toast } from "./components/Toast";
import { ToastProvider } from "./components/ToastProvider"; import { ToastProvider } from "./components/ToastProvider";
import { Wireframe } from "./components/Wireframe";
const App: React.FC = () => ( const App = (): JSX.Element => (
<ApolloProvider client={client}> <ApolloProvider client={client}>
<Router> <Router>
<ToastProvider> <ToastProvider>
<Header /> <Header />
<div className={styles.app}> <div className={styles.app}>
<Routes /> <Routes />
<Wireframe />
</div> </div>
<Footer />
<Toast /> <Toast />
</ToastProvider> </ToastProvider>
</Router> </Router>
+3 -1
View File
@@ -1,6 +1,7 @@
import React from "react"; import React from "react";
import { Route, Routes as ReactRouterRoutes } from "react-router-dom"; import { Route, Routes as ReactRouterRoutes } from "react-router-dom";
import { Splash } from "./components/Splash";
import { UserCreate } from "./features/auth/UserCreate"; import { UserCreate } from "./features/auth/UserCreate";
import { Deposit } from "./features/deposit/Deposit"; import { Deposit } from "./features/deposit/Deposit";
import { Log } from "./features/log/Log"; import { Log } from "./features/log/Log";
@@ -11,7 +12,7 @@ import { Stats } from "./features/stats/Stats";
import { UserDetail } from "./features/users/UserDetail"; import { UserDetail } from "./features/users/UserDetail";
import { UserList } from "./features/users/UserList"; import { UserList } from "./features/users/UserList";
export const Routes: React.FC = () => ( export const Routes = (): JSX.Element => (
<ReactRouterRoutes> <ReactRouterRoutes>
<Route path="/deposit" element={<Deposit />} /> <Route path="/deposit" element={<Deposit />} />
<Route path="/log" element={<Log />} /> <Route path="/log" element={<Log />} />
@@ -22,5 +23,6 @@ export const Routes: React.FC = () => (
<Route path="/users/create" element={<UserCreate />} /> <Route path="/users/create" element={<UserCreate />} />
<Route path="/user/:userId" element={<UserDetail />} /> <Route path="/user/:userId" element={<UserDetail />} />
<Route path="/" element={<Register />} /> <Route path="/" element={<Register />} />
<Route path="*" element={<Splash brandText="404" noOverlay />} />
</ReactRouterRoutes> </ReactRouterRoutes>
); );
Binary file not shown.
Binary file not shown.
+1 -1
View File
@@ -1,7 +1,7 @@
import { ApolloClient, InMemoryCache } from "@apollo/client"; import { ApolloClient, InMemoryCache } from "@apollo/client";
const client = new ApolloClient({ const client = new ApolloClient({
uri: import.meta.env.VITE_API_URL || "http://localhost:8000/graphql/", uri: `${import.meta.env.VITE_API_URL}/graphql/`,
cache: new InMemoryCache(), cache: new InMemoryCache(),
}); });
+22
View File
@@ -0,0 +1,22 @@
.footer {
position: absolute;
right: 0;
bottom: 1rem;
left: 0;
height: 20px;
color: rgba(255, 255, 255, 0.5);
font-size: 0.8rem;
text-align: center;
}
.heart {
color: var(--brand);
}
.brick {
position: relative;
top: 7px;
display: inline-block;
width: 20px;
height: 20px;
}
+15
View File
@@ -0,0 +1,15 @@
import React from "react";
import { ReactComponent as Brick } from "../assets/brick.svg";
import styles from "./Footer.module.css";
export const Footer = (): JSX.Element => {
return (
<footer className={styles.footer}>
Made with <span className={styles.heart}>❤</span> by{" "}
<span title="EDB-gjengen">
<Brick className={styles.brick} />
</span>
</footer>
);
};
+90 -9
View File
@@ -1,13 +1,94 @@
.logo { .header {
position: relative; margin: 1rem auto;
top: -1px; max-width: 1220px;
display: inline-block;
width: 24px;
height: 24px;
margin: 0 8px;
} }
.navIcon { .nav {
display: flex;
height: 5rem;
align-items: center;
text-transform: uppercase;
}
.navLink:after {
position: absolute;
right: 12px;
left: 14px;
display: block;
height: 1px;
background-color: rgba(243, 33, 61, 0.4);
box-shadow: 0 0 4px rgba(243, 33, 61, 0.4), 0 0 11px rgba(243, 33, 61, 0.4), 0 0 19px rgba(243, 33, 61, 0.4);
content: "";
transform: rotate(-2.5deg);
}
.navLink:hover:after {
background-color: var(--brand);
box-shadow: 0 0 4px var(--brand), 0 0 11px var(--brand), 0 0 19px var(--brand), 0 0 30px var(--brand);
}
.logoNav {
margin-left: 8px;
}
.navItems {
display: flex;
align-items: center;
padding: 0;
margin: 0;
list-style: none;
}
.navLink {
position: relative; position: relative;
top: -2px; padding: 0.4rem 0.8rem;
border: 1px solid transparent;
transition: color 0.1s ease-in-out;
}
.navLink:hover {
color: white;
}
.active.active {
color: white;
}
/* FIXME: Duplicates Splash styles */
.brand {
padding-left: 1rem;
color: var(--brand);
font-family: Rocket Rinder, monospace;
font-size: 3rem;
font-style: italic;
letter-spacing: 2px;
line-height: 1;
text-shadow: 0 0 4px rgba(243, 33, 61, 0.8), 0 0 11px rgba(243, 33, 61, 0.8), 0 0 19px rgba(243, 33, 61, 0.8);
}
.subtitle {
position: relative;
left: -8px;
animation: flicker 1.5s infinite alternate;
color: var(--interlace);
font-family: Rocket Clouds DEMO, serif;
font-size: 10px;
font-weight: bold;
line-height: 1;
transform: rotate(-6deg);
}
@keyframes flicker {
0%,
18%,
22%,
25%,
53%,
57%,
100% {
text-shadow: 0 0 4px rgba(121, 152, 238, 0.8), 0 0 11px rgba(121, 152, 238, 0.8), 0 0 19px rgba(121, 152, 238, 0.8);
}
20%,
24%,
55% {
text-shadow: none;
}
} }
+37 -72
View File
@@ -1,85 +1,50 @@
import { ReactComponent as BarChart } from "bootstrap-icons/icons/bar-chart.svg"; import clsx from "clsx";
import { ReactComponent as CardList } from "bootstrap-icons/icons/card-list.svg";
import { ReactComponent as House } from "bootstrap-icons/icons/house.svg";
import { ReactComponent as PeopleIcon } from "bootstrap-icons/icons/people.svg";
import React from "react"; import React from "react";
import { NavLink } from "react-router-dom"; import { NavLink } from "react-router-dom";
import { ReactComponent as Logo } from "../assets/brick.svg";
import styles from "./Header.module.css"; import styles from "./Header.module.css";
const ADMIN_URL = import.meta.env.VITE_API_URL const ADMIN_URL = `${import.meta.env.VITE_API_URL}/admin/`;
? `${import.meta.env.VITE_API_URL}/admin`
: "http://localhost:8000/admin/";
const navLinkStyles = ({ isActive }: { isActive: boolean }) => (isActive ? "nav-link active" : "nav-link"); const navLinkStyles = ({ isActive }: { isActive: boolean }) => clsx(styles.navLink, { [styles.active]: isActive });
export const Header: React.FC = () => { export const Header = (): JSX.Element => {
return ( return (
<header> <header className={styles.header}>
<nav className="navbar navbar-dark bg-dark navbar-expand-md"> <nav className={styles.nav}>
<NavLink to="/" className="navbar-brand"> <NavLink to="/" className={styles.logoNav}>
<span className={styles.logo}> <div className={styles.brand}>BBS</div>
<Logo /> <div className={styles.subtitle}>Biceps Bar System</div>
</span>
Biceps Bar System - BBS
</NavLink> </NavLink>
<button <ul className={styles.navItems}>
className="navbar-toggler" <li>
type="button" <NavLink to="/" className={navLinkStyles} end>
data-bs-toggle="collapse" Kjøp drikke
data-bs-target="#navbarNav" </NavLink>
aria-controls="navbarNav" </li>
aria-expanded="false" <li>
aria-label="Toggle navigation" <NavLink to="/deposit" className={navLinkStyles}>
> Sett inn penger
<span className="navbar-toggler-icon" /> </NavLink>
</button> </li>
<div className="collapse navbar-collapse" id="navbarNav"> <li>
<ul className="navbar-nav"> <NavLink to="/log" className={navLinkStyles}>
<li className="nav-item"> Logg
<NavLink to="/" className={navLinkStyles} end> </NavLink>
<span className={styles.navIcon}> </li>
<House /> <li>
</span>{" "} <NavLink to="/stats" className={navLinkStyles}>
Kjøp drikke Statistikk
</NavLink> </NavLink>
</li> </li>
<li className="nav-item">
<NavLink to="/deposit" className={navLinkStyles}>
Sett inn penger
</NavLink>
</li>
<li className="nav-item">
<NavLink to="/log" className={navLinkStyles}>
<span className={styles.navIcon}>
<CardList />
</span>{" "}
Logg
</NavLink>
</li>
<li className="nav-item">
<NavLink to="/stats" className={navLinkStyles}>
<span className={styles.navIcon}>
<BarChart />
</span>{" "}
Statistikk
</NavLink>
</li>
<li className="nav-item"> <li>
<NavLink to="/users" className={navLinkStyles}> <NavLink to="/users" className={navLinkStyles}>
<span className={styles.navIcon}> Folk
<PeopleIcon /> </NavLink>
</span>{" "} </li>
Folk <li>
</NavLink> <a href={ADMIN_URL} className={styles.navLink}>
</li>
</ul>
</div>
<ul className="navbar-nav">
<li className="nav-item">
<a href={ADMIN_URL} className="nav-link">
Admin Admin
</a> </a>
</li> </li>
@@ -1,57 +0,0 @@
.product {
display: flex;
overflow: hidden;
flex-direction: column;
align-items: center;
justify-content: center;
border: 4px solid transparent;
background: transparent;
border-radius: 8px;
transition: background-color 0.1s ease-out, border 0.1s ease-out;
}
.product:hover {
border-color: rgba(25, 135, 84, 0.6);
background: rgba(0, 0, 0, 0.1);
}
.active {
border-color: rgba(25, 135, 84, 0.6);
background: rgba(25, 135, 84, 0.2);
}
.active:hover {
background: rgba(25, 135, 84, 0.2);
}
.productText {
position: absolute;
right: 0;
bottom: 0;
left: 0;
padding: 4px;
background: rgba(0, 0, 0, 0.6);
border-radius: 8px;
color: white;
font-weight: bold;
}
.productImage {
width: 140px;
height: 140px;
object-fit: contain;
}
.noProductImage {
width: 140px;
height: 140px;
}
.amount {
position: absolute;
top: 0px;
left: 0px;
padding: 4px;
background: rgba(0, 0, 0, 0.6);
border-radius: 8px;
color: white;
font-size: 2rem;
font-weight: bold;
line-height: 1;
}
.wrap {
position: relative;
}
-26
View File
@@ -1,26 +0,0 @@
import clsx from "clsx";
import React, { MouseEventHandler } from "react";
import { Product } from "../types";
import styles from "./ProductCard.module.css";
type ProductCardProps = {
product: Omit<Product, "userCounts">;
onProduct: MouseEventHandler;
active: boolean;
amount: number;
};
export const ProductCard: React.FC<ProductCardProps> = ({ product, onProduct, amount, active = false }) => {
const classes = clsx(styles.product, { [styles.active]: active });
return (
<button className={classes} type="button" onClick={onProduct}>
<div className={styles.wrap}>
<span className={styles.amount}>{amount}</span>
{product.imageUrl && <img src={product.imageUrl} className={styles.productImage} alt={product.name} />}
{!product.imageUrl && <div className={styles.noProductImage} />}
<span className={styles.productText}>{product.name}</span>
</div>
</button>
);
};
+68
View File
@@ -0,0 +1,68 @@
.splash {
top: 0;
right: 0;
bottom: 0;
left: 0;
display: grid;
place-items: center;
text-align: center;
}
.overlay {
position: fixed;
}
.wrap {
position: relative;
}
.sun {
width: clamp(100px, 65vw, 600px);
height: clamp(100px, 65vw, 600px);
margin: 0 auto;
background: linear-gradient(var(--sun) 50%, var(--sun-end) 90%);
border-radius: 50% 50%;
box-shadow: 0 0 4px var(--sun), 0 0 11px var(--sun), 0 0 19px var(--sun), 0 0 30px var(--sun);
}
.brand {
position: relative;
left: -0.6rem;
padding-top: 3rem;
color: var(--brand);
font-family: Rocket Rinder, monospace;
font-size: clamp(20px, 20vw, 200px);
font-style: italic;
letter-spacing: 1rem;
line-height: 1;
text-shadow: 0 0 4px rgba(243, 33, 61, 0.8), 0 0 11px rgba(243, 33, 61, 0.8), 0 0 19px rgba(243, 33, 61, 0.8);
}
.subtitle {
position: relative;
bottom: 0.5rem;
left: 0rem;
animation: flicker 1.5s infinite alternate;
color: var(--interlace);
font-family: Rocket Clouds DEMO, serif;
font-size: clamp(20px, 5vw, 50px);
font-weight: bold;
line-height: 1;
transform: rotate(-6deg);
}
@keyframes flicker {
0%,
18%,
22%,
25%,
53%,
57%,
100% {
text-shadow: 0 0 4px rgba(121, 152, 238, 0.8), 0 0 11px rgba(121, 152, 238, 0.8), 0 0 19px rgba(121, 152, 238, 0.8);
}
20%,
24%,
55% {
text-shadow: none;
}
}
+19
View File
@@ -0,0 +1,19 @@
import clsx from "clsx";
import React from "react";
import styles from "./Splash.module.css";
interface SplashProps {
brandText?: string;
noOverlay?: boolean;
}
export const Splash = ({ brandText = "BBS", noOverlay = false }: SplashProps): JSX.Element => (
<div className={clsx(styles.splash, { [styles.overlay]: !noOverlay })}>
<div className={styles.wrap}>
<div className={styles.sun}></div>
<div className={styles.brand}>{brandText}</div>
<div className={styles.subtitle}>Biceps Bar System</div>
</div>
</div>
);
+23
View File
@@ -2,4 +2,27 @@
position: absolute; position: absolute;
bottom: 32px; bottom: 32px;
left: 32px; left: 32px;
padding: 0.6rem 1.2rem;
border-radius: 4px;
opacity: 0;
pointer-events: none;
transition: opacity 0.1s ease-in-out;
}
.visible {
opacity: 1;
pointer-events: initial;
}
.primary {
background: linear-gradient(var(--sun) 30%, var(--sun-end) 100%);
box-shadow: 0 0 4px var(--sun), 0 0 11px var(--sun), 0 0 19px var(--sun);
}
.danger {
background: var(--brand);
box-shadow: 0 0 4px rgba(243, 33, 61, 0.8), 0 0 11px rgba(243, 33, 61, 0.8), 0 0 19px rgba(243, 33, 61, 0.8);
}
.close {
display: none;
} }
+7 -30
View File
@@ -1,41 +1,18 @@
import { Toast as BSToast } from "bootstrap";
import clsx from "clsx"; import clsx from "clsx";
import React, { useEffect, useRef } from "react"; import React from "react";
import styles from "./Toast.module.css"; import styles from "./Toast.module.css";
import { useToast } from "./ToastProvider"; import { useToast } from "./ToastProvider";
export const Toast: React.FC = () => { export const Toast = (): JSX.Element => {
const ref = useRef<HTMLInputElement>(null); const { showToast, message, background, visible } = useToast();
const { visible, showToast, message, background } = useToast();
const backgroundColor = `bg-${background}`; const classes = clsx([styles[background], styles.toast], { [styles.visible]: visible });
const classes = clsx("toast align-items-center text-white bg-primary border-0", backgroundColor, styles.toast);
useEffect(() => {
if (!ref.current) return;
const bsToast = BSToast.getInstance(ref.current) || new BSToast(ref.current);
if (visible) {
bsToast?.show();
} else if (!visible) {
bsToast?.hide();
}
return () => bsToast?.hide();
}, [visible]);
return ( return (
<div ref={ref} className={classes} role="alert" aria-live="assertive" aria-atomic="true"> <div className={classes} role="alert" aria-live="assertive" aria-atomic="true">
<div className="d-flex"> <div>{message}</div>
<div className="toast-body">{message}</div> <button type="button" aria-label="Close" onClick={() => showToast(message)} className={styles.close} />
<button
type="button"
className="btn-close btn-close-white me-2 m-auto"
data-bs-dismiss="toast"
aria-label="Close"
onClick={() => showToast(message)}
/>
</div>
</div> </div>
); );
}; };
+10 -8
View File
@@ -1,21 +1,24 @@
import React, { ReactNode, useContext, useState } from "react"; import React, { ReactNode, useContext, useState } from "react";
type Background = "primary" | "danger";
interface ToastProps { interface ToastProps {
visible: boolean; visible: boolean;
message: string; message: string;
showToast: (message: string, background?: string) => void; showToast: (message: string, background?: Background) => void;
hide: () => void; hide: () => void;
background: string; background: Background;
} }
const defaultState = { const defaultState = {
// FIXME: revert
visible: false, visible: false,
message: "", message: "",
// eslint-disable-next-line @typescript-eslint/no-empty-function // eslint-disable-next-line @typescript-eslint/no-empty-function
showToast: () => {}, showToast: () => {},
// eslint-disable-next-line @typescript-eslint/no-empty-function // eslint-disable-next-line @typescript-eslint/no-empty-function
hide: () => {}, hide: () => {},
background: "primary", background: "primary" as Background,
}; };
const ToastContext = React.createContext<ToastProps>(defaultState); const ToastContext = React.createContext<ToastProps>(defaultState);
@@ -23,19 +26,18 @@ const ToastContext = React.createContext<ToastProps>(defaultState);
type ToastProviderProps = { type ToastProviderProps = {
children: ReactNode; children: ReactNode;
}; };
export const ToastProvider: React.FC<ToastProviderProps> = ({ children }) => { export const ToastProvider = ({ children }: ToastProviderProps): JSX.Element => {
const [visible, setVisible] = useState(defaultState.visible); const [visible, setVisible] = useState(defaultState.visible);
const [message, setMessage] = useState(""); const [message, setMessage] = useState(defaultState.message);
const [background, setBackground] = useState(""); const [background, setBackground] = useState(defaultState.background);
const showToast = (newMessage: string, background = "primary") => { const showToast = (newMessage: string, background: Background = "primary") => {
setMessage(newMessage); setMessage(newMessage);
setBackground(background); setBackground(background);
if (!visible) { if (!visible) {
setVisible(true); setVisible(true);
setTimeout(() => { setTimeout(() => {
setVisible(false); setVisible(false);
setMessage("");
}, 3000); }, 3000);
} }
}; };
@@ -1,34 +0,0 @@
.user {
display: flex;
flex-direction: column;
align-items: center;
padding: 4px;
border: 3px solid transparent;
background: transparent;
border-radius: 8px;
transition: background-color 0.1s ease-out, border 0.1s ease-out;
}
.user:hover {
border-color: rgba(25, 135, 84, 0.6);
background: rgba(0, 0, 0, 0.1);
}
.active {
border-color: rgba(25, 135, 84, 0.6);
background: rgba(25, 135, 84, 0.2);
}
.active:hover {
background: rgba(25, 135, 84, 0.2);
}
.avatar {
width: 50px;
height: 50px;
border-radius: 50px;
}
.avatar svg {
width: 50px;
height: 50px;
color: #ccc;
}
.name {
font-weight: bold;
}
-31
View File
@@ -1,31 +0,0 @@
import { ReactComponent as PersonFill } from "bootstrap-icons/icons/person-fill.svg";
import clsx from "clsx";
import React, { MouseEventHandler } from "react";
import { AllUsersQuery } from "../types";
import styles from "./UserCard.module.css";
type UserCardProps = {
user: AllUsersQuery["allUsers"][0] | null;
onUser: MouseEventHandler;
active: boolean;
};
/** If user is null, then it's the special external user */
export const UserCard: React.FC<UserCardProps> = ({ user, onUser, active = false }) => {
const classes = clsx(styles.user, { [styles.active]: active });
return (
<button className={classes} onClick={onUser} type="button">
{user?.profile?.image && (
<img src={user.profile?.image} alt={`Profile for ${user.firstName}`} className={styles.avatar} />
)}
{!user?.profile?.image && (
<span className={styles.avatar}>
<PersonFill />
</span>
)}
<span className={styles.name}>{user?.firstName || "Ekstern"}</span>
<span className={styles.balance}>{user?.profile?.balance || ""}</span>
</button>
);
};
@@ -0,0 +1,38 @@
.wireframe {
position: absolute;
bottom: calc(22% - 512px);
left: calc(50% - 512px);
width: 1024px;
height: 1024px;
display: grid;
grid-template-columns: repeat(8, 128px);
animation-iteration-count: infinite;
animation-name: isometric-rotate;
animation-duration: 10s;
animation-timing-function: linear;
pointer-events: none;
z-index: -1;
}
.cell {
border-bottom: 2px solid var(--sun);
border-right: 2px solid var(--sun);
}
.cell:nth-child(8n) {
border-right: none;
}
.cell:nth-child(n+57) {
border-bottom: none;
}
@keyframes isometric-rotate {
0% {
transform: rotateX(70deg) rotateY(0deg) rotateZ(-45deg);
}
100% {
transform: rotateX(70deg) rotateY(0deg) rotateZ(-405deg);
}
}
+11
View File
@@ -0,0 +1,11 @@
import React from "react";
import styles from "./Wireframe.module.css";
export const Wireframe = () => (
<div className={styles.wireframe}>
{[...Array(64)].map((_, idx) => (
<div key={idx} className={styles.cell} />
))}
</div>
);
@@ -0,0 +1,5 @@
.form {
display: grid;
max-width: 500px;
gap: 1rem;
}
+16 -15
View File
@@ -3,6 +3,7 @@ import React from "react";
import { SubmitHandler, useForm } from "react-hook-form"; import { SubmitHandler, useForm } from "react-hook-form";
import { CreateUserDocument } from "../../types"; import { CreateUserDocument } from "../../types";
import styles from "./UserCreate.module.css";
type FormValues = { type FormValues = {
firstName: string; firstName: string;
@@ -30,20 +31,20 @@ export const UserCreate = () => {
}; };
return ( return (
<form onSubmit={handleSubmit(onSubmit)}> <div>
<label>Fornavn</label> <h2>Opprett ny</h2>
<input {...register("firstName", { required: true })} /> <form onSubmit={handleSubmit(onSubmit)} className={styles.form}>
{errors.firstName && <span>This field is required</span>} <label>Fornavn</label>
<br /> <input {...register("firstName", { required: true })} />
<label>Etternavn</label> {errors.firstName && <span>This field is required</span>}
<input {...register("lastName", { required: true })} /> <label>Etternavn</label>
{errors.lastName && <span>This field is required</span>} <input {...register("lastName", { required: true })} />
<br /> {errors.lastName && <span>This field is required</span>}
<label>E-post</label> <label>E-post</label>
<input type="email" {...register("email", { required: true })} /> <input type="email" {...register("email", { required: true })} />
{errors.email && <span>This field is required</span>} {errors.email && <span>This field is required</span>}
<br /> <input type="submit" disabled={creating} className="btn-primary" />
<input type="submit" disabled={creating} /> </form>
</form> </div>
); );
}; };
@@ -1,13 +1,17 @@
.userList { .userList {
display: grid; display: grid;
height: 400px; height: 800px;
grid-template-columns: repeat(12, 100px); padding-top: 2rem;
grid-template-columns: repeat(5, 220px);
overflow-y: auto; overflow-y: auto;
row-gap: 16px; column-gap: 16px;
row-gap: 24px;
} }
.amountSection { .amountSection {
margin-top: 32px; margin-top: 32px;
} }
.checkout { .checkout {
margin-top: 32px; margin-top: 32px;
} }
+5 -5
View File
@@ -1,10 +1,10 @@
import React from "react"; import React from "react";
import { UserCard } from "../../components/UserCard"; import { UserCard } from "../users/UserCard";
import styles from "./Deposit.module.css"; import styles from "./Deposit.module.css";
import { useDeposit } from "./useDeposit"; import { useDeposit } from "./useDeposit";
export const Deposit: React.FC = () => { export const Deposit = (): JSX.Element => {
const { const {
loading, loading,
selectedUser, selectedUser,
@@ -33,7 +33,7 @@ export const Deposit: React.FC = () => {
placeholder="Filtrér på navn" placeholder="Filtrér på navn"
/> />
{userQuery !== "" && ( {userQuery !== "" && (
<button className="btn btn-link" type="button" onClick={() => setUserQuery("")}> <button type="button" onClick={() => setUserQuery("")}>
Vis alle Vis alle
</button> </button>
)} )}
@@ -55,10 +55,10 @@ export const Deposit: React.FC = () => {
<input name="amount" value={amount} onChange={(event) => setAmount(event.target.value)} placeholder="0" /> <input name="amount" value={amount} onChange={(event) => setAmount(event.target.value)} placeholder="0" />
</section> </section>
<section className={styles.checkout}> <section className={styles.checkout}>
<button type="button" className="btn btn-primary btn-lg" onClick={onSubmit}> <button type="button" onClick={onSubmit} className="primary">
Sett inn Sett inn
</button>{" "} </button>{" "}
<button type="button" className="btn btn-outline-secondary btn-lg" onClick={reset}> <button type="button" onClick={reset}>
Start på nytt Start på nytt
</button> </button>
</section> </section>
+27
View File
@@ -3,3 +3,30 @@
gap: 1rem; gap: 1rem;
grid-template-columns: repeat(2, 1fr); grid-template-columns: repeat(2, 1fr);
} }
.disabled,
.disabled a:link {
color: rgba(121, 152, 238, 0.5);
}
.pagination {
display: flex;
align-items: flex-end;
padding: 0;
margin: 0;
margin-top: 1.5rem;
gap: 0 1rem;
list-style: none;
}
.pagination a {
padding: 0.4rem 0.8rem;
border: 1px solid var(--border);
margin-top: 1rem;
border-radius: 4px;
}
.pagination a:hover {
color: white;
}
.disabled a:hover {
color: rgba(121, 152, 238, 0.5);
}
+16 -20
View File
@@ -34,7 +34,7 @@ export const Log = () => {
<div className={styles.log}> <div className={styles.log}>
<div> <div>
<h2>Ordre</h2> <h2>Ordre</h2>
<table className="table"> <table>
<thead> <thead>
<tr> <tr>
<th scope="col">Når</th> <th scope="col">Når</th>
@@ -61,29 +61,27 @@ export const Log = () => {
</tbody> </tbody>
</table> </table>
<nav aria-label="Order pagination"> <nav aria-label="Order pagination">
<ul className="pagination"> <ul className={styles.pagination}>
<li className={clsx("page-item", { disabled: orderOffset <= 0 })}> <li className={clsx({ [styles.disabled]: orderOffset <= 0 })}>
<a <a
href="#" href="#"
className="page-link"
onClick={(e) => { onClick={(e) => {
e.preventDefault(); e.preventDefault();
setParams({ orderOffset: orderOffset - PAGE_SIZE }); setParams({ orderOffset: Math.max(0, orderOffset - PAGE_SIZE) });
}} }}
> >
Previous ⬅
</a> </a>
</li> </li>
<li className="page-item"> <li>
<a <a
href="#" href="#"
className="page-link"
onClick={(e) => { onClick={(e) => {
e.preventDefault(); e.preventDefault();
setParams({ orderOffset: orderOffset + PAGE_SIZE }); setParams({ orderOffset: Math.max(0, orderOffset + PAGE_SIZE) });
}} }}
> >
Next ➡
</a> </a>
</li> </li>
</ul> </ul>
@@ -91,7 +89,7 @@ export const Log = () => {
</div> </div>
<div> <div>
<h2>Transaksjoner</h2> <h2>Transaksjoner</h2>
<table className="table"> <table>
<thead> <thead>
<tr> <tr>
<th scope="col">Når</th> <th scope="col">Når</th>
@@ -112,29 +110,27 @@ export const Log = () => {
</tbody> </tbody>
</table> </table>
<nav aria-label="Transaction pagination"> <nav aria-label="Transaction pagination">
<ul className="pagination"> <ul className={styles.pagination}>
<li className={clsx("page-item", { disabled: transactionOffset <= 0 })}> <li className={clsx({ [styles.disabled]: transactionOffset <= 0 })}>
<a <a
href="#" href="#"
className="page-link"
onClick={(e) => { onClick={(e) => {
e.preventDefault(); e.preventDefault();
setParams({ transactionOffset: transactionOffset - PAGE_SIZE }); setParams({ transactionOffset: Math.max(0, transactionOffset - PAGE_SIZE) });
}} }}
> >
Previous ⬅
</a> </a>
</li> </li>
<li className="page-item"> <li>
<a <a
href="#" href="#"
className="page-link"
onClick={(e) => { onClick={(e) => {
e.preventDefault(); e.preventDefault();
setParams({ transactionOffset: transactionOffset + PAGE_SIZE }); setParams({ transactionOffset: Math.max(transactionOffset + PAGE_SIZE) });
}} }}
> >
Next ➡
</a> </a>
</li> </li>
</ul> </ul>
@@ -0,0 +1,51 @@
.product {
display: flex;
border: none;
background: rgb(35, 15, 41);
border-radius: 4px;
transition: background-color 0.1s ease-out, border 0.1s ease-out;
position: relative;
align-items: flex-start;
padding: 1rem;
}
.product:hover,
.active.product {
background-color: var(--background);
box-shadow: 0 0 4px var(--background), 0 0 11px var(--background), 0 0 19px var(--background);
}
.productText {
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
font-size: 1.2rem;
text-shadow: none;
color: white;
}
.amount {
position: absolute;
z-index: 1;
top: 0;
bottom: 0;
right: 0;
padding: 12px 12px 8px 8px;
border-left: 1px solid transparent;
color: transparent;
font-size: 2rem;
font-weight: bold;
line-height: 1;
background-color: transparent;
border-top-right-radius: 4px;
border-bottom-right-radius: 4px;
display: none;
}
.active .amount {
display: block;
color: var(--background);
text-shadow: none;
border-left-color: rgb(35, 15, 41);
background-color: var(--sun-end);
}
@@ -0,0 +1,22 @@
import clsx from "clsx";
import React, { MouseEventHandler } from "react";
import { Product } from "../../types";
import styles from "./ProductCard.module.css";
type ProductCardProps = {
product: Omit<Product, "userCounts">;
onProduct: MouseEventHandler;
active: boolean;
amount: number;
};
export const ProductCard = ({ product, onProduct, amount, active = false }: ProductCardProps): JSX.Element => {
const classes = clsx(styles.product, { [styles.active]: active });
return (
<button className={classes} type="button" onClick={onProduct} title={product.name}>
<span className={styles.amount}>{amount}</span>
<span className={styles.productText}>{product.name}</span>
</button>
);
};
@@ -1,14 +1,21 @@
.userList { .userList {
display: grid; display: grid;
grid-template-columns: repeat(12, 128px); grid-template-columns: repeat(5, 220px);
row-gap: 16px; column-gap: 16px;
row-gap: 24px;
}
.showAll {
margin-top: 1rem;
} }
.productList { .productList {
display: grid; display: grid;
grid-template-columns: repeat(8, 150px); grid-template-columns: repeat(5, 220px);
row-gap: 16px; column-gap: 16px;
row-gap: 24px;
} }
.productListSection { .productListSection {
margin-top: 2rem; margin-top: 2rem;
} }
@@ -22,7 +29,6 @@
min-width: 200px; min-width: 200px;
padding: 4px 8px; padding: 4px 8px;
margin-right: 8px; margin-right: 8px;
background: black;
color: white; color: white;
font-size: 1.8rem; font-size: 1.8rem;
} }
+6 -6
View File
@@ -1,14 +1,14 @@
import React from "react"; import React from "react";
import { ProductCard } from "../../components/ProductCard";
import { UserCard } from "../../components/UserCard";
import { OrderLineInput, Product } from "../../types"; import { OrderLineInput, Product } from "../../types";
import { UserCard } from "../users/UserCard";
import { ProductCard } from "./ProductCard";
import styles from "./Register.module.css"; import styles from "./Register.module.css";
import { useRegister } from "./useRegister"; import { useRegister } from "./useRegister";
const USER_EXTERNAL = "external"; const USER_EXTERNAL = "external";
export const Register: React.FC = () => { export const Register = (): JSX.Element => {
const { const {
users, users,
products, products,
@@ -48,7 +48,7 @@ export const Register: React.FC = () => {
/> />
))} ))}
</div> </div>
<button type="button" className="btn btn-link" onClick={() => setShowAll(!showAll)}> <button type="button" onClick={() => setShowAll(!showAll)} className={styles.showAll}>
{showAll ? "Skjul gamliser" : "Vis alle"} {showAll ? "Skjul gamliser" : "Vis alle"}
</button> </button>
</section> </section>
@@ -70,10 +70,10 @@ export const Register: React.FC = () => {
<span className={styles.total}> <span className={styles.total}>
Totalt: <span>{total}</span> Totalt: <span>{total}</span>
</span> </span>
<button type="button" className="btn btn-primary btn-lg" onClick={onSubmit}> <button type="button" onClick={onSubmit} className="primary">
Kjøp Kjøp
</button>{" "} </button>{" "}
<button type="button" className="btn btn-outline-secondary btn-lg" onClick={reset}> <button type="button" onClick={reset}>
Start på nytt Start på nytt
</button> </button>
</section> </section>
+21 -1
View File
@@ -4,6 +4,26 @@
gap: 1rem; gap: 1rem;
grid-template-columns: repeat(3, 1fr); grid-template-columns: repeat(3, 1fr);
} }
.statsNav { .statsNav {
margin-bottom: 1rem; margin-bottom: 1rem;
} }
.statsNav ul {
display: flex;
padding: 0;
margin: 0;
gap: 0 1rem;
list-style: none;
}
.statsNav a {
padding: 0.4rem 0.8rem;
border: 1px solid transparent;
}
.active.active {
color: white;
}
.statsNav a:hover {
border: 1px solid var(--border);
border-radius: 4px;
}
+11 -9
View File
@@ -1,34 +1,36 @@
import clsx from "clsx";
import React from "react"; import React from "react";
import { NavLink } from "react-router-dom"; import { NavLink } from "react-router-dom";
import styles from "./Stats.module.css"; import styles from "./Stats.module.css";
const navLinkStyles = ({ isActive }: { isActive: boolean }) => (isActive ? "nav-link active" : "nav-link");
const navLinkStyles = ({ isActive }: { isActive: boolean }) => clsx(styles.navLink, { [styles.active]: isActive });
export const StatsNav = () => ( export const StatsNav = () => (
<div className={styles.statsNav}> <nav className={styles.statsNav}>
<ul className="nav nav-tabs"> <ul>
<li className="nav-item"> <li>
<NavLink className={navLinkStyles} to="/stats" end> <NavLink className={navLinkStyles} to="/stats" end>
Oversikt Oversikt
</NavLink> </NavLink>
</li> </li>
<li className="nav-item"> <li>
<NavLink className={navLinkStyles} to="/stats/products"> <NavLink className={navLinkStyles} to="/stats/products">
Produkter Produkter
</NavLink> </NavLink>
</li> </li>
<li className="nav-item"> <li>
<NavLink className={navLinkStyles} to="/stats/orders"> <NavLink className={navLinkStyles} to="/stats/orders">
Ordre Ordre
</NavLink> </NavLink>
</li> </li>
<li className="nav-item"> <li>
<NavLink className="nav-link disabled" to="/stats/users" title="TODO"> <NavLink className="disabled" to="/stats/users" title="TODO">
Folk Folk
</NavLink> </NavLink>
</li> </li>
</ul> </ul>
</div> </nav>
); );
export const StatsView = ({ children, loading }: { children: React.ReactNode; loading: boolean }) => ( export const StatsView = ({ children, loading }: { children: React.ReactNode; loading: boolean }) => (
@@ -0,0 +1,61 @@
.user {
display: flex;
align-items: center;
padding: 0;
border: none;
border-left: 4px solid transparent;
background: rgb(35, 15, 41);
border-radius: 4px;
color: var(--text);
transition: background-color 0.1s ease-out, border 0.1s ease-out;
max-height: 82px;
}
.user:hover {
cursor: pointer;
}
.user:hover,
.active.user {
background-color: var(--background);
box-shadow: 0 0 4px var(--background), 0 0 11px var(--background), 0 0 19px var(--background);
}
.active.user {
border-left: 4px solid var(--sun)
}
.avatar {
margin-left: 1rem;
width: 50px;
height: 50px;
clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%);
}
.avatarWrap {
filter: drop-shadow(0 0 4px var(--background)) drop-shadow(0 0 11px var(--background))
drop-shadow(0 0 19px var(--background));
}
.noAvatar {
margin-left: 1rem;
display: block;
width: 50px;
height: 50px;
background-color: black;
clip-path: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%);
content: "";
}
.meta {
display: flex;
padding: 1rem;
flex-direction: column;
align-items: flex-start;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
font-size: 1.2rem;
text-shadow: none;
}
.balance {
color: var(--sun)
}
+34
View File
@@ -0,0 +1,34 @@
import clsx from "clsx";
import React, { MouseEventHandler } from "react";
import { AllUsersQuery } from "../../types";
import styles from "./UserCard.module.css";
type UserCardProps = {
user: AllUsersQuery["allUsers"][0] | null;
onUser: MouseEventHandler;
active: boolean;
};
/** If user is null, then it's the special external user */
export const UserCard = ({ user, onUser, active = false }: UserCardProps): JSX.Element => {
const classes = clsx(styles.user, { [styles.active]: active });
return (
<button className={classes} onClick={onUser} type="button" title={user?.firstName || "Ekstern"}>
<div className={styles.avatarWrap}>
{user?.profile?.image && (
<img src={user.profile?.image} alt={`Profile for ${user.firstName}`} className={styles.avatar} />
)}
{!user?.profile?.image && (
<span className={styles.avatar}>
<span className={styles.noAvatar} />
</span>
)}
</div>
<div className={styles.meta}>
{user?.firstName || "Ekstern"}
<span className={styles.balance}>{user?.profile?.balance || ""}</span>
</div>
</button>
);
};
+10 -10
View File
@@ -30,26 +30,26 @@ export const UserDetail = () => {
<h1> <h1>
{profile.image && <img src={profile.image} alt={`${user.firstName}s profile picture`} />} {user.firstName} {profile.image && <img src={profile.image} alt={`${user.firstName}s profile picture`} />} {user.firstName}
</h1> </h1>
<div className="card"> <div>
<div className="card-header">Stats</div> <div>Stats</div>
<ul className="list-group list-group-flush"> <ul>
<li className="list-group-item"> <li>
Saldo: <strong>{profile.balance.toLocaleString("no")} kr</strong> Saldo: <strong>{profile.balance.toLocaleString("no")} kr</strong>
</li> </li>
<li className="list-group-item"> <li>
Siste kjøp: <strong>{formatTime(profile.lastPurchaseDate)}</strong> Siste kjøp: <strong>{formatTime(profile.lastPurchaseDate)}</strong>
</li> </li>
<li className="list-group-item"> <li>
Kjøp totalt: <strong>{profile.orderSumTotal.toLocaleString("no")} kr</strong> Kjøp totalt: <strong>{profile.orderSumTotal.toLocaleString("no")} kr</strong>
</li> </li>
</ul> </ul>
</div> </div>
<br /> <br />
<div className="card"> <div>
<div className="card-header">Top 5 måneder</div> <div>Top 5 måneder</div>
<ul className="list-group list-group-flush"> <ul>
{profile.topMonths.map((topMonth) => ( {profile.topMonths.map((topMonth) => (
<li key={topMonth.period} className="list-group-item"> <li key={topMonth.period}>
{formatMonth(topMonth.period)}: <strong>{topMonth.count}</strong> kjøp på totalt{" "} {formatMonth(topMonth.period)}: <strong>{topMonth.count}</strong> kjøp på totalt{" "}
<strong>{topMonth.sum} kr</strong> <strong>{topMonth.sum} kr</strong>
</li> </li>
+3 -2
View File
@@ -15,7 +15,8 @@ export const UserList = () => {
const users = showAll ? allUsers : allUsers.filter((u) => activeUserFilter(u)); const users = showAll ? allUsers : allUsers.filter((u) => activeUserFilter(u));
return ( return (
<div> <div>
<button type="button" className="btn btn-link" onClick={() => setShowAll(!showAll)}> <h2>Folk</h2>
<button type="button" onClick={() => setShowAll(!showAll)}>
{showAll ? "Skjul gamliser" : "Vis alle"} {showAll ? "Skjul gamliser" : "Vis alle"}
</button> </button>
<ul> <ul>
@@ -28,7 +29,7 @@ export const UserList = () => {
))} ))}
</ul> </ul>
<br /> <br />
<Link to="/users/create" className="btn btn-primary"> <Link to="/users/create" className="btn-primary">
Opprett ny Opprett ny
</Link> </Link>
</div> </div>
+145
View File
@@ -0,0 +1,145 @@
/* Custom props */
:root {
--brand: rgb(243, 33, 61);
--background: rgb(70, 30, 82);
--text: rgb(255, 255, 255);
--sun: rgb(221, 81, 127);
--sun-end: rgb(230, 142, 54);
--border: rgb(85, 109, 200);
--interlace: rgb(121, 152, 238);
--font-family: "IBM Plex Mono", monospace;
}
/* Resets */
html,
body {
width: 100%;
height: 100%;
padding: 0;
margin: 0;
}
html {
box-sizing: border-box;
}
*,
*:before,
*:after {
box-sizing: inherit;
}
/* Body and interitable styles */
body {
background: linear-gradient(rgba(0, 0, 0, 0.8) 60%, var(--background) 100%);
background-color: var(--background);
color: var(--text);
font-family: var(--font-family);
text-shadow: 0 0 4px currentColor, 0 0 11px currentColor, 0 0 19px currentColor;
}
/* Links */
a:link {
color: var(--interlace);
text-decoration: none;
}
a:visited {
color: var(--interlace);
}
a:active {
color: var(--interlace);
}
a:hover {
color: var(--text);
}
h1,
h2,
h3,
h4,
h5 {
color: var(--sun);
}
a.btn-primary {
padding: 0.4rem 1rem;
border: 1px solid transparent;
background: linear-gradient(var(--sun) 30%, var(--sun-end) 100%);
border-radius: 4px;
box-shadow: 0 0 4px var(--sun), 0 0 11px var(--sun), 0 0 19px var(--sun);
color: var(--text);
}
/* Buttons */
button {
padding: 0.4rem 1rem;
border: 1px solid var(--border);
background: transparent;
border-radius: 4px;
color: var(--border);
font-family: var(--font-family);
text-shadow: 0 0 4px currentColor, 0 0 11px currentColor, 0 0 19px currentColor;
}
button:hover {
color: var(--text);
cursor: pointer;
}
button.primary {
border: 1px solid transparent;
background: linear-gradient(var(--sun) 30%, var(--sun-end) 100%);
box-shadow: 0 0 4px var(--sun), 0 0 11px var(--sun), 0 0 19px var(--sun);
color: var(--text);
}
/* Inputs */
input {
padding: 0.4rem 0.4rem;
border: 1px solid var(--border);
background: transparent;
border-radius: 4px;
color: var(--text);
font-family: var(--font-family);
font-size: 1.5rem;
text-shadow: 0 0 4px currentColor, 0 0 11px currentColor, 0 0 19px currentColor;
}
input:focus {
background: rgba(85, 109, 200, 0.1);
outline: none;
}
input.btn-primary {
padding: 0.4rem 1rem;
border: 1px solid transparent;
background: linear-gradient(var(--sun) 30%, var(--sun-end) 100%);
border-radius: 4px;
box-shadow: 0 0 4px var(--sun), 0 0 11px var(--sun), 0 0 19px var(--sun);
color: var(--text);
}
::placeholder {
color: var(--border);
}
/* Tables */
table {
border: 1px solid var(--border);
border-collapse: collapse;
border-spacing: 0;
}
td,
th {
padding: 0.5rem 0.8rem;
border: 1px solid var(--border);
}
/* Fonts*/
@font-face {
font-family: "Rocket Clouds DEMO";
font-style: normal;
font-weight: normal;
src: url("assets/rocket-clouds.ttf") format("truetype");
}
@font-face {
font-family: "Rocket Rinder";
font-style: normal;
font-weight: normal;
src: url("assets/rocket-rinder.ttf") format("truetype");
}
+2
View File
@@ -3,6 +3,8 @@ import ReactDOM from "react-dom";
import App from "./App"; import App from "./App";
import "./global.css";
ReactDOM.render( ReactDOM.render(
<React.StrictMode> <React.StrictMode>
<App /> <App />
+1 -2
View File
@@ -4,11 +4,10 @@ import svgr from "vite-plugin-svgr";
// https://vitejs.dev/config/ // https://vitejs.dev/config/
export default defineConfig({ export default defineConfig({
base: process.env.NODE_ENV !== "production" ? "/" : "/static/modern/",
plugins: [svgr()], plugins: [svgr()],
define: { define: {
__DEV__: process.env.NODE_ENV !== "production", __DEV__: process.env.NODE_ENV !== "production",
}, },
envDir: "..", envDir: "..",
build: { outDir: "../static/modern", emptyOutDir: true }, build: { outDir: "../static", emptyOutDir: false },
}); });
-9
View File
@@ -1,9 +0,0 @@
import strawberry
import strawberry_django
from main.api.types.inventory import Transaction, TransactionOrdering
@strawberry.type
class InventoryQueries:
transaction_list: list[Transaction] = strawberry_django.field(pagination=True, order=TransactionOrdering)
-20
View File
@@ -1,20 +0,0 @@
import strawberry
from main.api.mutations.shop import ShopMutations
from main.api.mutations.users import UsersMutations
from main.api.queries.inventory import InventoryQueries
from main.api.queries.shop import ShopQueries
from main.api.queries.users import UsersQueries
@strawberry.type
class Query(InventoryQueries, ShopQueries, UsersQueries):
pass
@strawberry.type
class Mutation(ShopMutations, UsersMutations):
pass
schema = strawberry.Schema(query=Query, mutation=Mutation)
-11
View File
@@ -1,11 +0,0 @@
from django.urls import re_path
from main.views.reports import inventory, inventory_add, report
urlpatterns = [
# TODO: migrate to SPA
# Reports
re_path(r"^inventory/$", inventory, name="inventory"),
re_path(r"^inventory/add/$", inventory_add, name="inventory-add"),
re_path(r"^report$", report, name="report"),
]
View File
Whitespace-only changes.
+1 -1
View File
@@ -3,7 +3,7 @@ import os
import sys import sys
if __name__ == "__main__": if __name__ == "__main__":
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "bbssite.settings") os.environ.setdefault("DJANGO_SETTINGS_MODULE", "bbs.settings")
from django.core.management import execute_from_command_line from django.core.management import execute_from_command_line
Generated
+294 -49
View File
@@ -31,6 +31,25 @@ docs = ["furo", "sphinx", "zope.interface", "sphinx-notfound-page"]
tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "cloudpickle"] tests = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "zope.interface", "cloudpickle"]
tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "cloudpickle"] tests_no_zope = ["coverage[toml] (>=5.0.2)", "hypothesis", "pympler", "pytest (>=4.3.0)", "six", "mypy", "pytest-mypy-plugins", "cloudpickle"]
[[package]]
name = "backports.cached-property"
version = "1.0.1"
description = "cached_property() - computed once per instance, cached as attribute"
category = "main"
optional = false
python-versions = ">=3.6.0"
[[package]]
name = "backports.zoneinfo"
version = "0.2.1"
description = "Backport of the standard library zoneinfo module"
category = "main"
optional = false
python-versions = ">=3.6"
[package.extras]
tzdata = ["tzdata"]
[[package]] [[package]]
name = "black" name = "black"
version = "22.1.0" version = "22.1.0"
@@ -53,14 +72,6 @@ d = ["aiohttp (>=3.7.4)"]
jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"]
uvloop = ["uvloop (>=0.15.2)"] uvloop = ["uvloop (>=0.15.2)"]
[[package]]
name = "cached-property"
version = "1.5.2"
description = "A decorator for caching properties in classes."
category = "main"
optional = false
python-versions = "*"
[[package]] [[package]]
name = "certifi" name = "certifi"
version = "2021.10.8" version = "2021.10.8"
@@ -96,6 +107,22 @@ category = "main"
optional = false optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*" python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
[[package]]
name = "cssselect2"
version = "0.4.1"
description = "cssselect2"
category = "main"
optional = false
python-versions = ">=3.6"
[package.dependencies]
tinycss2 = "*"
webencodings = "*"
[package.extras]
doc = ["sphinx", "sphinx-rtd-theme"]
test = ["pytest", "pytest-cov", "pytest-flake8", "pytest-isort", "coverage"]
[[package]] [[package]]
name = "distlib" name = "distlib"
version = "0.3.4" version = "0.3.4"
@@ -106,16 +133,17 @@ python-versions = "*"
[[package]] [[package]]
name = "django" name = "django"
version = "3.2.12" version = "4.0.2"
description = "A high-level Python Web framework that encourages rapid development and clean, pragmatic design." description = "A high-level Python web framework that encourages rapid development and clean, pragmatic design."
category = "main" category = "main"
optional = false optional = false
python-versions = ">=3.6" python-versions = ">=3.8"
[package.dependencies] [package.dependencies]
asgiref = ">=3.3.2,<4" asgiref = ">=3.4.1,<4"
pytz = "*" "backports.zoneinfo" = {version = "*", markers = "python_version < \"3.9\""}
sqlparse = ">=0.2.2" sqlparse = ">=0.2.2"
tzdata = {version = "*", markers = "sys_platform == \"win32\""}
[package.extras] [package.extras]
argon2 = ["argon2-cffi (>=19.1.0)"] argon2 = ["argon2-cffi (>=19.1.0)"]
@@ -143,17 +171,30 @@ python-versions = ">=3.6"
[package.dependencies] [package.dependencies]
Django = ">=2.2" Django = ">=2.2"
[[package]]
name = "django-rename-app"
version = "0.1.3"
description = "A Django Management Command to rename existing Django Applications."
category = "main"
optional = false
python-versions = "*"
[package.dependencies]
django = "*"
[[package]] [[package]]
name = "easy-thumbnails" name = "easy-thumbnails"
version = "2.7.2" version = "2.8.1"
description = "Easy thumbnails for Django" description = "Easy thumbnails for Django"
category = "main" category = "main"
optional = false optional = false
python-versions = ">=3.5" python-versions = ">=3.6"
[package.dependencies] [package.dependencies]
django = ">=1.11,<4.0" django = ">=2.2"
pillow = "*" pillow = "*"
reportlab = "*"
svglib = "*"
[[package]] [[package]]
name = "filelock" name = "filelock"
@@ -222,6 +263,20 @@ requirements_deprecated_finder = ["pipreqs", "pip-api"]
colors = ["colorama (>=0.4.3,<0.5.0)"] colors = ["colorama (>=0.4.3,<0.5.0)"]
plugins = ["setuptools"] plugins = ["setuptools"]
[[package]]
name = "lxml"
version = "4.8.0"
description = "Powerful and Pythonic XML processing library combining libxml2/libxslt with the ElementTree API."
category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, != 3.4.*"
[package.extras]
cssselect = ["cssselect (>=0.7)"]
html5 = ["html5lib"]
htmlsoup = ["beautifulsoup4"]
source = ["Cython (>=0.29.7)"]
[[package]] [[package]]
name = "mypy-extensions" name = "mypy-extensions"
version = "0.4.3" version = "0.4.3"
@@ -275,7 +330,7 @@ python-versions = ">=3.7"
[[package]] [[package]]
name = "platformdirs" name = "platformdirs"
version = "2.5.0" version = "2.5.1"
description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"."
category = "dev" category = "dev"
optional = false optional = false
@@ -398,14 +453,6 @@ python-versions = "*"
[package.dependencies] [package.dependencies]
six = ">=1.4.0" six = ">=1.4.0"
[[package]]
name = "pytz"
version = "2021.3"
description = "World timezone definitions, modern and historical"
category = "main"
optional = false
python-versions = "*"
[[package]] [[package]]
name = "pyyaml" name = "pyyaml"
version = "6.0" version = "6.0"
@@ -414,6 +461,20 @@ category = "dev"
optional = false optional = false
python-versions = ">=3.6" python-versions = ">=3.6"
[[package]]
name = "reportlab"
version = "3.6.7"
description = "The Reportlab Toolkit"
category = "main"
optional = false
python-versions = ">=3.6, <4"
[package.dependencies]
pillow = ">=4.0.0"
[package.extras]
rlpycairo = ["rlPyCairo (>=0.0.5)"]
[[package]] [[package]]
name = "sentinel" name = "sentinel"
version = "0.3.0" version = "0.3.0"
@@ -427,7 +488,7 @@ varname = ["varname (>=0.1)"]
[[package]] [[package]]
name = "sentry-sdk" name = "sentry-sdk"
version = "1.4.3" version = "1.5.5"
description = "Python client for Sentry (https://sentry.io)" description = "Python client for Sentry (https://sentry.io)"
category = "main" category = "main"
optional = false optional = false
@@ -449,6 +510,7 @@ flask = ["flask (>=0.11)", "blinker (>=1.1)"]
httpx = ["httpx (>=0.16.0)"] httpx = ["httpx (>=0.16.0)"]
pure_eval = ["pure-eval", "executing", "asttokens"] pure_eval = ["pure-eval", "executing", "asttokens"]
pyspark = ["pyspark (>=2.4.4)"] pyspark = ["pyspark (>=2.4.4)"]
quart = ["quart (>=0.16.1)", "blinker (>=1.1)"]
rq = ["rq (>=0.6)"] rq = ["rq (>=0.6)"]
sanic = ["sanic (>=0.8)"] sanic = ["sanic (>=0.8)"]
sqlalchemy = ["sqlalchemy (>=1.2)"] sqlalchemy = ["sqlalchemy (>=1.2)"]
@@ -472,14 +534,14 @@ python-versions = ">=3.5"
[[package]] [[package]]
name = "strawberry-graphql" name = "strawberry-graphql"
version = "0.93.23" version = "0.97.0"
description = "A library for creating GraphQL APIs" description = "A library for creating GraphQL APIs"
category = "main" category = "main"
optional = false optional = false
python-versions = ">=3.7,<4.0" python-versions = ">=3.7,<4.0"
[package.dependencies] [package.dependencies]
cached-property = ">=1.5.2,<2.0.0" "backports.cached-property" = ">=1.0.1,<2.0.0"
click = ">=7.0,<9.0" click = ">=7.0,<9.0"
graphql-core = ">=3.1.0,<3.2.0" graphql-core = ">=3.1.0,<3.2.0"
pygments = ">=2.3,<3.0" pygments = ">=2.3,<3.0"
@@ -490,7 +552,7 @@ typing_extensions = ">=3.7.4,<5.0.0"
[package.extras] [package.extras]
asgi = ["starlette (>=0.13.6,<0.17.0)"] asgi = ["starlette (>=0.13.6,<0.17.0)"]
debug-server = ["starlette (>=0.13.6,<0.17.0)", "uvicorn (>=0.11.6,<0.17.0)"] debug-server = ["starlette (>=0.13.6,<0.17.0)", "uvicorn (>=0.11.6,<0.18.0)"]
django = ["Django (>=2.2,<4)", "Django (>=2.2,<5)", "asgiref (>=3.2,<4.0)"] django = ["Django (>=2.2,<4)", "Django (>=2.2,<5)", "asgiref (>=3.2,<4.0)"]
flask = ["flask (>=1.1,<2.0)"] flask = ["flask (>=1.1,<2.0)"]
opentelemetry = ["opentelemetry-api (<2)", "opentelemetry-sdk (<2)"] opentelemetry = ["opentelemetry-api (<2)", "opentelemetry-sdk (<2)"]
@@ -512,6 +574,35 @@ python-versions = ">=3.7,<4.0"
Django = ">=3.0" Django = ">=3.0"
strawberry-graphql = ">=0.69.0" strawberry-graphql = ">=0.69.0"
[[package]]
name = "svglib"
version = "1.2.1"
description = "A pure-Python library for reading and converting SVG"
category = "main"
optional = false
python-versions = ">=3.7"
[package.dependencies]
cssselect2 = ">=0.2.0"
lxml = "*"
reportlab = "*"
tinycss2 = ">=0.6.0"
[[package]]
name = "tinycss2"
version = "1.1.1"
description = "A tiny CSS parser"
category = "main"
optional = false
python-versions = ">=3.6"
[package.dependencies]
webencodings = ">=0.4"
[package.extras]
doc = ["sphinx", "sphinx-rtd-theme"]
test = ["pytest", "pytest-cov", "pytest-flake8", "pytest-isort", "coverage"]
[[package]] [[package]]
name = "toml" name = "toml"
version = "0.10.2" version = "0.10.2"
@@ -536,6 +627,14 @@ category = "main"
optional = false optional = false
python-versions = ">=3.6" python-versions = ">=3.6"
[[package]]
name = "tzdata"
version = "2021.5"
description = "Provider of IANA time zone data"
category = "main"
optional = false
python-versions = ">=2"
[[package]] [[package]]
name = "urllib3" name = "urllib3"
version = "1.26.8" version = "1.26.8"
@@ -567,10 +666,18 @@ six = ">=1.9.0,<2"
docs = ["proselint (>=0.10.2)", "sphinx (>=3)", "sphinx-argparse (>=0.2.5)", "sphinx-rtd-theme (>=0.4.3)", "towncrier (>=21.3)"] docs = ["proselint (>=0.10.2)", "sphinx (>=3)", "sphinx-argparse (>=0.2.5)", "sphinx-rtd-theme (>=0.4.3)", "towncrier (>=21.3)"]
testing = ["coverage (>=4)", "coverage-enable-subprocess (>=1)", "flaky (>=3)", "pytest (>=4)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.1)", "pytest-mock (>=2)", "pytest-randomly (>=1)", "pytest-timeout (>=1)", "packaging (>=20.0)"] testing = ["coverage (>=4)", "coverage-enable-subprocess (>=1)", "flaky (>=3)", "pytest (>=4)", "pytest-env (>=0.6.2)", "pytest-freezegun (>=0.4.1)", "pytest-mock (>=2)", "pytest-randomly (>=1)", "pytest-timeout (>=1)", "packaging (>=20.0)"]
[[package]]
name = "webencodings"
version = "0.5.1"
description = "Character encoding aliases for legacy web content"
category = "main"
optional = false
python-versions = "*"
[metadata] [metadata]
lock-version = "1.1" lock-version = "1.1"
python-versions = "^3.9" python-versions = "~3.8"
content-hash = "3fa14cb7bc28f810082b1aa4604a74060ec4ea32e63864f91a005aa37ea73c8c" content-hash = "7d014e036d7cdddaa215ab1b17e7aaff3a3d4bbf34804fb52e1047bbe00607f4"
[metadata.files] [metadata.files]
asgiref = [ asgiref = [
@@ -585,6 +692,28 @@ attrs = [
{file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"}, {file = "attrs-21.4.0-py2.py3-none-any.whl", hash = "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4"},
{file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"}, {file = "attrs-21.4.0.tar.gz", hash = "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd"},
] ]
"backports.cached-property" = [
{file = "backports.cached-property-1.0.1.tar.gz", hash = "sha256:1a5ef1e750f8bc7d0204c807aae8e0f450c655be0cf4b30407a35fd4bb27186c"},
{file = "backports.cached_property-1.0.1-py3-none-any.whl", hash = "sha256:687b5fe14be40aadcf547cae91337a1fdb84026046a39370274e54d3fe4fb4f9"},
]
"backports.zoneinfo" = [
{file = "backports.zoneinfo-0.2.1-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:da6013fd84a690242c310d77ddb8441a559e9cb3d3d59ebac9aca1a57b2e18bc"},
{file = "backports.zoneinfo-0.2.1-cp36-cp36m-manylinux1_i686.whl", hash = "sha256:89a48c0d158a3cc3f654da4c2de1ceba85263fafb861b98b59040a5086259722"},
{file = "backports.zoneinfo-0.2.1-cp36-cp36m-manylinux1_x86_64.whl", hash = "sha256:1c5742112073a563c81f786e77514969acb58649bcdf6cdf0b4ed31a348d4546"},
{file = "backports.zoneinfo-0.2.1-cp36-cp36m-win32.whl", hash = "sha256:e8236383a20872c0cdf5a62b554b27538db7fa1bbec52429d8d106effbaeca08"},
{file = "backports.zoneinfo-0.2.1-cp36-cp36m-win_amd64.whl", hash = "sha256:8439c030a11780786a2002261569bdf362264f605dfa4d65090b64b05c9f79a7"},
{file = "backports.zoneinfo-0.2.1-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:f04e857b59d9d1ccc39ce2da1021d196e47234873820cbeaad210724b1ee28ac"},
{file = "backports.zoneinfo-0.2.1-cp37-cp37m-manylinux1_i686.whl", hash = "sha256:17746bd546106fa389c51dbea67c8b7c8f0d14b5526a579ca6ccf5ed72c526cf"},
{file = "backports.zoneinfo-0.2.1-cp37-cp37m-manylinux1_x86_64.whl", hash = "sha256:5c144945a7752ca544b4b78c8c41544cdfaf9786f25fe5ffb10e838e19a27570"},
{file = "backports.zoneinfo-0.2.1-cp37-cp37m-win32.whl", hash = "sha256:e55b384612d93be96506932a786bbcde5a2db7a9e6a4bb4bffe8b733f5b9036b"},
{file = "backports.zoneinfo-0.2.1-cp37-cp37m-win_amd64.whl", hash = "sha256:a76b38c52400b762e48131494ba26be363491ac4f9a04c1b7e92483d169f6582"},
{file = "backports.zoneinfo-0.2.1-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:8961c0f32cd0336fb8e8ead11a1f8cd99ec07145ec2931122faaac1c8f7fd987"},
{file = "backports.zoneinfo-0.2.1-cp38-cp38-manylinux1_i686.whl", hash = "sha256:e81b76cace8eda1fca50e345242ba977f9be6ae3945af8d46326d776b4cf78d1"},
{file = "backports.zoneinfo-0.2.1-cp38-cp38-manylinux1_x86_64.whl", hash = "sha256:7b0a64cda4145548fed9efc10322770f929b944ce5cee6c0dfe0c87bf4c0c8c9"},
{file = "backports.zoneinfo-0.2.1-cp38-cp38-win32.whl", hash = "sha256:1b13e654a55cd45672cb54ed12148cd33628f672548f373963b0bff67b217328"},
{file = "backports.zoneinfo-0.2.1-cp38-cp38-win_amd64.whl", hash = "sha256:4a0f800587060bf8880f954dbef70de6c11bbe59c673c3d818921f042f9954a6"},
{file = "backports.zoneinfo-0.2.1.tar.gz", hash = "sha256:fadbfe37f74051d024037f223b8e001611eac868b5c5b06144ef4d8b799862f2"},
]
black = [ black = [
{file = "black-22.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1297c63b9e1b96a3d0da2d85d11cd9bf8664251fd69ddac068b98dc4f34f73b6"}, {file = "black-22.1.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:1297c63b9e1b96a3d0da2d85d11cd9bf8664251fd69ddac068b98dc4f34f73b6"},
{file = "black-22.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2ff96450d3ad9ea499fc4c60e425a1439c2120cbbc1ab959ff20f7c76ec7e866"}, {file = "black-22.1.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:2ff96450d3ad9ea499fc4c60e425a1439c2120cbbc1ab959ff20f7c76ec7e866"},
@@ -610,10 +739,6 @@ black = [
{file = "black-22.1.0-py3-none-any.whl", hash = "sha256:3524739d76b6b3ed1132422bf9d82123cd1705086723bc3e235ca39fd21c667d"}, {file = "black-22.1.0-py3-none-any.whl", hash = "sha256:3524739d76b6b3ed1132422bf9d82123cd1705086723bc3e235ca39fd21c667d"},
{file = "black-22.1.0.tar.gz", hash = "sha256:a7c0192d35635f6fc1174be575cb7915e92e5dd629ee79fdaf0dcfa41a80afb5"}, {file = "black-22.1.0.tar.gz", hash = "sha256:a7c0192d35635f6fc1174be575cb7915e92e5dd629ee79fdaf0dcfa41a80afb5"},
] ]
cached-property = [
{file = "cached-property-1.5.2.tar.gz", hash = "sha256:9fa5755838eecbb2d234c3aa390bd80fbd3ac6b6869109bfc1b499f7bd89a130"},
{file = "cached_property-1.5.2-py2.py3-none-any.whl", hash = "sha256:df4f613cf7ad9a588cc381aaf4a512d26265ecebd5eb9e1ba12f1319eb85a6a0"},
]
certifi = [ certifi = [
{file = "certifi-2021.10.8-py2.py3-none-any.whl", hash = "sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569"}, {file = "certifi-2021.10.8-py2.py3-none-any.whl", hash = "sha256:d62a0163eb4c2344ac042ab2bdf75399a71a2d8c7d47eac2e2ee91b9d6339569"},
{file = "certifi-2021.10.8.tar.gz", hash = "sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872"}, {file = "certifi-2021.10.8.tar.gz", hash = "sha256:78884e7c1d4b00ce3cea67b44566851c4343c120abd683433ce934a68ea58872"},
@@ -630,13 +755,17 @@ colorama = [
{file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"}, {file = "colorama-0.4.4-py2.py3-none-any.whl", hash = "sha256:9f47eda37229f68eee03b24b9748937c7dc3868f906e8ba69fbcbdd3bc5dc3e2"},
{file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"}, {file = "colorama-0.4.4.tar.gz", hash = "sha256:5941b2b48a20143d2267e95b1c2a7603ce057ee39fd88e7329b0c292aa16869b"},
] ]
cssselect2 = [
{file = "cssselect2-0.4.1-py3-none-any.whl", hash = "sha256:2f4a9f20965367bae459e3bb42561f7927e0cfe5b7ea1692757cf67ef5d7dace"},
{file = "cssselect2-0.4.1.tar.gz", hash = "sha256:93fbb9af860e95dd40bf18c3b2b6ed99189a07c0f29ba76f9c5be71344664ec8"},
]
distlib = [ distlib = [
{file = "distlib-0.3.4-py2.py3-none-any.whl", hash = "sha256:6564fe0a8f51e734df6333d08b8b94d4ea8ee6b99b5ed50613f731fd4089f34b"}, {file = "distlib-0.3.4-py2.py3-none-any.whl", hash = "sha256:6564fe0a8f51e734df6333d08b8b94d4ea8ee6b99b5ed50613f731fd4089f34b"},
{file = "distlib-0.3.4.zip", hash = "sha256:e4b58818180336dc9c529bfb9a0b58728ffc09ad92027a3f30b7cd91e3458579"}, {file = "distlib-0.3.4.zip", hash = "sha256:e4b58818180336dc9c529bfb9a0b58728ffc09ad92027a3f30b7cd91e3458579"},
] ]
django = [ django = [
{file = "Django-3.2.12-py3-none-any.whl", hash = "sha256:9b06c289f9ba3a8abea16c9c9505f25107809fb933676f6c891ded270039d965"}, {file = "Django-4.0.2-py3-none-any.whl", hash = "sha256:996495c58bff749232426c88726d8cd38d24c94d7c1d80835aafffa9bc52985a"},
{file = "Django-3.2.12.tar.gz", hash = "sha256:9772e6935703e59e993960832d66a614cf0233a1c5123bc6224ecc6ad69e41e2"}, {file = "Django-4.0.2.tar.gz", hash = "sha256:110fb58fb12eca59e072ad59fc42d771cd642dd7a2f2416582aa9da7a8ef954a"},
] ]
django-cors-headers = [ django-cors-headers = [
{file = "django-cors-headers-3.11.0.tar.gz", hash = "sha256:eb98389bf7a2afc5d374806af4a9149697e3a6955b5a2dc2bf049f7d33647456"}, {file = "django-cors-headers-3.11.0.tar.gz", hash = "sha256:eb98389bf7a2afc5d374806af4a9149697e3a6955b5a2dc2bf049f7d33647456"},
@@ -646,9 +775,13 @@ django-extensions = [
{file = "django-extensions-3.1.5.tar.gz", hash = "sha256:28e1e1bf49f0e00307ba574d645b0af3564c981a6dfc87209d48cb98f77d0b1a"}, {file = "django-extensions-3.1.5.tar.gz", hash = "sha256:28e1e1bf49f0e00307ba574d645b0af3564c981a6dfc87209d48cb98f77d0b1a"},
{file = "django_extensions-3.1.5-py3-none-any.whl", hash = "sha256:9238b9e016bb0009d621e05cf56ea8ce5cce9b32e91ad2026996a7377ca28069"}, {file = "django_extensions-3.1.5-py3-none-any.whl", hash = "sha256:9238b9e016bb0009d621e05cf56ea8ce5cce9b32e91ad2026996a7377ca28069"},
] ]
django-rename-app = [
{file = "django_rename_app-0.1.3-py2.py3-none-any.whl", hash = "sha256:bba31d33de4614124e2cd2f22fcea07b2affbbb2cf877a26893e0c15465fbfab"},
{file = "django_rename_app-0.1.3.tar.gz", hash = "sha256:195d2fa09eec8ed57577f7bf57a3c6944bdb6447fe47c07be0e575fb2e325742"},
]
easy-thumbnails = [ easy-thumbnails = [
{file = "easy-thumbnails-2.7.2.tar.gz", hash = "sha256:a7dd9cf97efaf70ba5d76484a962f08ba65b31f1681bc417257743650e9e8a8a"}, {file = "easy-thumbnails-2.8.1.tar.gz", hash = "sha256:1a283fe8a3569c3feab4605e8279929d75c85c1151b2fd627f95b1863b5fc6c2"},
{file = "easy_thumbnails-2.7.2-py3-none-any.whl", hash = "sha256:9d7ed56a9b57aba7a8d0e03dc3b8507ca51aab5e0ed0bc705c3261ff1f659192"}, {file = "easy_thumbnails-2.8.1-py3-none-any.whl", hash = "sha256:5f59f722508469d482d8d76b99c7b9e6c1abbce19aefd5932dc6e1b9eacd2473"},
] ]
filelock = [ filelock = [
{file = "filelock-3.6.0-py3-none-any.whl", hash = "sha256:f8314284bfffbdcfa0ff3d7992b023d4c628ced6feb957351d4c48d059f56bc0"}, {file = "filelock-3.6.0-py3-none-any.whl", hash = "sha256:f8314284bfffbdcfa0ff3d7992b023d4c628ced6feb957351d4c48d059f56bc0"},
@@ -674,6 +807,69 @@ isort = [
{file = "isort-5.10.1-py3-none-any.whl", hash = "sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7"}, {file = "isort-5.10.1-py3-none-any.whl", hash = "sha256:6f62d78e2f89b4500b080fe3a81690850cd254227f27f75c3a0c491a1f351ba7"},
{file = "isort-5.10.1.tar.gz", hash = "sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951"}, {file = "isort-5.10.1.tar.gz", hash = "sha256:e8443a5e7a020e9d7f97f1d7d9cd17c88bcb3bc7e218bf9cf5095fe550be2951"},
] ]
lxml = [
{file = "lxml-4.8.0-cp27-cp27m-macosx_10_14_x86_64.whl", hash = "sha256:e1ab2fac607842ac36864e358c42feb0960ae62c34aa4caaf12ada0a1fb5d99b"},
{file = "lxml-4.8.0-cp27-cp27m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:28d1af847786f68bec57961f31221125c29d6f52d9187c01cd34dc14e2b29430"},
{file = "lxml-4.8.0-cp27-cp27m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:b92d40121dcbd74831b690a75533da703750f7041b4bf951befc657c37e5695a"},
{file = "lxml-4.8.0-cp27-cp27m-win32.whl", hash = "sha256:e01f9531ba5420838c801c21c1b0f45dbc9607cb22ea2cf132844453bec863a5"},
{file = "lxml-4.8.0-cp27-cp27m-win_amd64.whl", hash = "sha256:6259b511b0f2527e6d55ad87acc1c07b3cbffc3d5e050d7e7bcfa151b8202df9"},
{file = "lxml-4.8.0-cp27-cp27mu-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1010042bfcac2b2dc6098260a2ed022968dbdfaf285fc65a3acf8e4eb1ffd1bc"},
{file = "lxml-4.8.0-cp27-cp27mu-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:fa56bb08b3dd8eac3a8c5b7d075c94e74f755fd9d8a04543ae8d37b1612dd170"},
{file = "lxml-4.8.0-cp310-cp310-macosx_10_15_x86_64.whl", hash = "sha256:31ba2cbc64516dcdd6c24418daa7abff989ddf3ba6d3ea6f6ce6f2ed6e754ec9"},
{file = "lxml-4.8.0-cp310-cp310-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:31499847fc5f73ee17dbe1b8e24c6dafc4e8d5b48803d17d22988976b0171f03"},
{file = "lxml-4.8.0-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:5f7d7d9afc7b293147e2d506a4596641d60181a35279ef3aa5778d0d9d9123fe"},
{file = "lxml-4.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:a3c5f1a719aa11866ffc530d54ad965063a8cbbecae6515acbd5f0fae8f48eaa"},
{file = "lxml-4.8.0-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:6268e27873a3d191849204d00d03f65c0e343b3bcb518a6eaae05677c95621d1"},
{file = "lxml-4.8.0-cp310-cp310-win32.whl", hash = "sha256:330bff92c26d4aee79c5bc4d9967858bdbe73fdbdbacb5daf623a03a914fe05b"},
{file = "lxml-4.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:b2582b238e1658c4061ebe1b4df53c435190d22457642377fd0cb30685cdfb76"},
{file = "lxml-4.8.0-cp35-cp35m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a2bfc7e2a0601b475477c954bf167dee6d0f55cb167e3f3e7cefad906e7759f6"},
{file = "lxml-4.8.0-cp35-cp35m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:a1547ff4b8a833511eeaceacbcd17b043214fcdb385148f9c1bc5556ca9623e2"},
{file = "lxml-4.8.0-cp35-cp35m-win32.whl", hash = "sha256:a9f1c3489736ff8e1c7652e9dc39f80cff820f23624f23d9eab6e122ac99b150"},
{file = "lxml-4.8.0-cp35-cp35m-win_amd64.whl", hash = "sha256:530f278849031b0eb12f46cca0e5db01cfe5177ab13bd6878c6e739319bae654"},
{file = "lxml-4.8.0-cp36-cp36m-macosx_10_14_x86_64.whl", hash = "sha256:078306d19a33920004addeb5f4630781aaeabb6a8d01398045fcde085091a169"},
{file = "lxml-4.8.0-cp36-cp36m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:86545e351e879d0b72b620db6a3b96346921fa87b3d366d6c074e5a9a0b8dadb"},
{file = "lxml-4.8.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:24f5c5ae618395ed871b3d8ebfcbb36e3f1091fd847bf54c4de623f9107942f3"},
{file = "lxml-4.8.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:bbab6faf6568484707acc052f4dfc3802bdb0cafe079383fbaa23f1cdae9ecd4"},
{file = "lxml-4.8.0-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:7993232bd4044392c47779a3c7e8889fea6883be46281d45a81451acfd704d7e"},
{file = "lxml-4.8.0-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:6d6483b1229470e1d8835e52e0ff3c6973b9b97b24cd1c116dca90b57a2cc613"},
{file = "lxml-4.8.0-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:ad4332a532e2d5acb231a2e5d33f943750091ee435daffca3fec0a53224e7e33"},
{file = "lxml-4.8.0-cp36-cp36m-win32.whl", hash = "sha256:db3535733f59e5605a88a706824dfcb9bd06725e709ecb017e165fc1d6e7d429"},
{file = "lxml-4.8.0-cp36-cp36m-win_amd64.whl", hash = "sha256:5f148b0c6133fb928503cfcdfdba395010f997aa44bcf6474fcdd0c5398d9b63"},
{file = "lxml-4.8.0-cp37-cp37m-macosx_10_14_x86_64.whl", hash = "sha256:8a31f24e2a0b6317f33aafbb2f0895c0bce772980ae60c2c640d82caac49628a"},
{file = "lxml-4.8.0-cp37-cp37m-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:719544565c2937c21a6f76d520e6e52b726d132815adb3447ccffbe9f44203c4"},
{file = "lxml-4.8.0-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:c0b88ed1ae66777a798dc54f627e32d3b81c8009967c63993c450ee4cbcbec15"},
{file = "lxml-4.8.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:fa9b7c450be85bfc6cd39f6df8c5b8cbd76b5d6fc1f69efec80203f9894b885f"},
{file = "lxml-4.8.0-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e9f84ed9f4d50b74fbc77298ee5c870f67cb7e91dcdc1a6915cb1ff6a317476c"},
{file = "lxml-4.8.0-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:1d650812b52d98679ed6c6b3b55cbb8fe5a5460a0aef29aeb08dc0b44577df85"},
{file = "lxml-4.8.0-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:80bbaddf2baab7e6de4bc47405e34948e694a9efe0861c61cdc23aa774fcb141"},
{file = "lxml-4.8.0-cp37-cp37m-win32.whl", hash = "sha256:6f7b82934c08e28a2d537d870293236b1000d94d0b4583825ab9649aef7ddf63"},
{file = "lxml-4.8.0-cp37-cp37m-win_amd64.whl", hash = "sha256:e1fd7d2fe11f1cb63d3336d147c852f6d07de0d0020d704c6031b46a30b02ca8"},
{file = "lxml-4.8.0-cp38-cp38-macosx_10_14_x86_64.whl", hash = "sha256:5045ee1ccd45a89c4daec1160217d363fcd23811e26734688007c26f28c9e9e7"},
{file = "lxml-4.8.0-cp38-cp38-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:0c1978ff1fd81ed9dcbba4f91cf09faf1f8082c9d72eb122e92294716c605428"},
{file = "lxml-4.8.0-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:52cbf2ff155b19dc4d4100f7442f6a697938bf4493f8d3b0c51d45568d5666b5"},
{file = "lxml-4.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:ce13d6291a5f47c1c8dbd375baa78551053bc6b5e5c0e9bb8e39c0a8359fd52f"},
{file = "lxml-4.8.0-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e11527dc23d5ef44d76fef11213215c34f36af1608074561fcc561d983aeb870"},
{file = "lxml-4.8.0-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:60d2f60bd5a2a979df28ab309352cdcf8181bda0cca4529769a945f09aba06f9"},
{file = "lxml-4.8.0-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:62f93eac69ec0f4be98d1b96f4d6b964855b8255c345c17ff12c20b93f247b68"},
{file = "lxml-4.8.0-cp38-cp38-win32.whl", hash = "sha256:20b8a746a026017acf07da39fdb10aa80ad9877046c9182442bf80c84a1c4696"},
{file = "lxml-4.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:891dc8f522d7059ff0024cd3ae79fd224752676447f9c678f2a5c14b84d9a939"},
{file = "lxml-4.8.0-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:b6fc2e2fb6f532cf48b5fed57567ef286addcef38c28874458a41b7837a57807"},
{file = "lxml-4.8.0-cp39-cp39-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:74eb65ec61e3c7c019d7169387d1b6ffcfea1b9ec5894d116a9a903636e4a0b1"},
{file = "lxml-4.8.0-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.manylinux_2_24_aarch64.whl", hash = "sha256:627e79894770783c129cc5e89b947e52aa26e8e0557c7e205368a809da4b7939"},
{file = "lxml-4.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:545bd39c9481f2e3f2727c78c169425efbfb3fbba6e7db4f46a80ebb249819ca"},
{file = "lxml-4.8.0-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5a58d0b12f5053e270510bf12f753a76aaf3d74c453c00942ed7d2c804ca845c"},
{file = "lxml-4.8.0-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.whl", hash = "sha256:ec4b4e75fc68da9dc0ed73dcdb431c25c57775383fec325d23a770a64e7ebc87"},
{file = "lxml-4.8.0-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:5804e04feb4e61babf3911c2a974a5b86f66ee227cc5006230b00ac6d285b3a9"},
{file = "lxml-4.8.0-cp39-cp39-win32.whl", hash = "sha256:aa0cf4922da7a3c905d000b35065df6184c0dc1d866dd3b86fd961905bbad2ea"},
{file = "lxml-4.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:dd10383f1d6b7edf247d0960a3db274c07e96cf3a3fc7c41c8448f93eac3fb1c"},
{file = "lxml-4.8.0-pp37-pypy37_pp73-macosx_10_14_x86_64.whl", hash = "sha256:2403a6d6fb61c285969b71f4a3527873fe93fd0abe0832d858a17fe68c8fa507"},
{file = "lxml-4.8.0-pp37-pypy37_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:986b7a96228c9b4942ec420eff37556c5777bfba6758edcb95421e4a614b57f9"},
{file = "lxml-4.8.0-pp37-pypy37_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:6fe4ef4402df0250b75ba876c3795510d782def5c1e63890bde02d622570d39e"},
{file = "lxml-4.8.0-pp38-pypy38_pp73-macosx_10_14_x86_64.whl", hash = "sha256:f10ce66fcdeb3543df51d423ede7e238be98412232fca5daec3e54bcd16b8da0"},
{file = "lxml-4.8.0-pp38-pypy38_pp73-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_24_i686.whl", hash = "sha256:730766072fd5dcb219dd2b95c4c49752a54f00157f322bc6d71f7d2a31fecd79"},
{file = "lxml-4.8.0-pp38-pypy38_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.manylinux_2_24_x86_64.whl", hash = "sha256:8b99ec73073b37f9ebe8caf399001848fced9c08064effdbfc4da2b5a8d07b93"},
{file = "lxml-4.8.0.tar.gz", hash = "sha256:f63f62fc60e6228a4ca9abae28228f35e1bd3ce675013d1dfb828688d50c6e23"},
]
mypy-extensions = [ mypy-extensions = [
{file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"}, {file = "mypy_extensions-0.4.3-py2.py3-none-any.whl", hash = "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d"},
{file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"}, {file = "mypy_extensions-0.4.3.tar.gz", hash = "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"},
@@ -735,8 +931,8 @@ pillow = [
{file = "Pillow-9.0.1.tar.gz", hash = "sha256:6c8bc8238a7dfdaf7a75f5ec5a663f4173f8c367e5a39f87e720495e1eed75fa"}, {file = "Pillow-9.0.1.tar.gz", hash = "sha256:6c8bc8238a7dfdaf7a75f5ec5a663f4173f8c367e5a39f87e720495e1eed75fa"},
] ]
platformdirs = [ platformdirs = [
{file = "platformdirs-2.5.0-py3-none-any.whl", hash = "sha256:30671902352e97b1eafd74ade8e4a694782bd3471685e78c32d0fdfd3aa7e7bb"}, {file = "platformdirs-2.5.1-py3-none-any.whl", hash = "sha256:bcae7cab893c2d310a711b70b24efb93334febe65f8de776ee320b517471e227"},
{file = "platformdirs-2.5.0.tar.gz", hash = "sha256:8ec11dfba28ecc0715eb5fb0147a87b1bf325f349f3da9aab2cd6b50b96b692b"}, {file = "platformdirs-2.5.1.tar.gz", hash = "sha256:7535e70dfa32e84d4b34996ea99c5e432fa29a708d0f4e394bbcb2a8faa4f16d"},
] ]
pluggy = [ pluggy = [
{file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"},
@@ -773,10 +969,6 @@ python-dateutil = [
python-multipart = [ python-multipart = [
{file = "python-multipart-0.0.5.tar.gz", hash = "sha256:f7bb5f611fc600d15fa47b3974c8aa16e93724513b49b5f95c81e6624c83fa43"}, {file = "python-multipart-0.0.5.tar.gz", hash = "sha256:f7bb5f611fc600d15fa47b3974c8aa16e93724513b49b5f95c81e6624c83fa43"},
] ]
pytz = [
{file = "pytz-2021.3-py2.py3-none-any.whl", hash = "sha256:3672058bc3453457b622aab7a1c3bfd5ab0bdae451512f6cf25f64ed37f5b87c"},
{file = "pytz-2021.3.tar.gz", hash = "sha256:acad2d8b20a1af07d4e4c9d2e9285c5ed9104354062f275f3fcd88dcef4f1326"},
]
pyyaml = [ pyyaml = [
{file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"}, {file = "PyYAML-6.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53"},
{file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"}, {file = "PyYAML-6.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c"},
@@ -812,13 +1004,51 @@ pyyaml = [
{file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"}, {file = "PyYAML-6.0-cp39-cp39-win_amd64.whl", hash = "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c"},
{file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"}, {file = "PyYAML-6.0.tar.gz", hash = "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2"},
] ]
reportlab = [
{file = "reportlab-3.6.7-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:fd5bc5d0b56df4cafb70b5ba8ec0bb19921574d2f99fbf623c3a215fcaa79aba"},
{file = "reportlab-3.6.7-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:dd4d53e5d09f9912df86746330148d739ad837424383ea0b99486ac38e5edaa2"},
{file = "reportlab-3.6.7-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b6d72e9f2d5cf20378b1c6a8ea3589728496744677332f0688dc415a8a5c91b7"},
{file = "reportlab-3.6.7-cp310-cp310-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:3d402cd8cdd1b9b8631b6d7811c6bb788aa3caa3f38b300e9d4a5e12276d8863"},
{file = "reportlab-3.6.7-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b91d2952ca4264c145927303ec3c03cdf6b0b035688077b514ba37e8683c21ec"},
{file = "reportlab-3.6.7-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:80c9cdfed6b4bcca9dfd904d1d139b98975cd9ccb2558588307a0d53b5b6c8b4"},
{file = "reportlab-3.6.7-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1890f197f12f3c734bafc21cbf9ebe18164e780164997a0d717187db2a46adc"},
{file = "reportlab-3.6.7-cp310-cp310-win32.whl", hash = "sha256:4432abb4c61d914cbac22baa9172efe8036074cea539a12f3b37134055d38ccb"},
{file = "reportlab-3.6.7-cp310-cp310-win_amd64.whl", hash = "sha256:47fa377b24044514807d539780c58bc5cec91fa4ea8700f3c150c61de358d91a"},
{file = "reportlab-3.6.7-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:232ff75d29a1a5b3e8aa321eaec8e541afaf9704fd2e059127b816df3f2296f4"},
{file = "reportlab-3.6.7-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bf2e6e5b4d58ad1220228f5620064e13a53148775c28dcbe1c552f3f7e359389"},
{file = "reportlab-3.6.7-cp37-cp37m-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:27088f0f10639f8ca27222264910c816aa3b089d590b4a1c01e9d1b0935835df"},
{file = "reportlab-3.6.7-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8faa40a3f74bbf932bcda30ea8258511a3b6194460202a4720c1e4b64acb338"},
{file = "reportlab-3.6.7-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ec67e5bdf6cb0f3b410b1bfb75a41aeb39f89ffedd1fd5165fa87833ecbb48dd"},
{file = "reportlab-3.6.7-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:439f3b2eca084aad48f6231253a33469c8eac9fcd924d80b157cbe6b77699931"},
{file = "reportlab-3.6.7-cp37-cp37m-win32.whl", hash = "sha256:6bb42e672c1866a52412888cc07a91589a28a922100770c1f53569d3e7e3ad8f"},
{file = "reportlab-3.6.7-cp37-cp37m-win_amd64.whl", hash = "sha256:0e308e91d995beb35f0cef8c7c16d6c10c22ae937929c345f745caa6d371b7a9"},
{file = "reportlab-3.6.7-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:449a03741f10454d64afc55180eeac91bb22f3560332c7e25df3dc7e884cbf22"},
{file = "reportlab-3.6.7-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:3bda030877d3b683e4fb72fd114a6dad1e5b1ba83928ae914af5e0171d5bcff9"},
{file = "reportlab-3.6.7-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b7e5b0372f15331d806dd5847c4ec1f737b02438579a7e2dd798e9bf8c27c83e"},
{file = "reportlab-3.6.7-cp38-cp38-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:2b1360785b73a52613712df48600d62788e0fb3ebd6e47fa52fb0dd498430893"},
{file = "reportlab-3.6.7-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:db9462b443d9f0834d997039fc2648336ada3141dc96b146bcde85c81a6b3224"},
{file = "reportlab-3.6.7-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:401b72f70fcac83c52853d17b0c1f1f9d93c6ceaafdd94b3c0e7b0bdfecca182"},
{file = "reportlab-3.6.7-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:1e28735a9a01124637d637a8513ffec74a34e5fdd3c3af595eb3e0217525e8e0"},
{file = "reportlab-3.6.7-cp38-cp38-win32.whl", hash = "sha256:a137df1507414115e3f0c8821514f450d9f9c973f2821dfa112ed797464fc93d"},
{file = "reportlab-3.6.7-cp38-cp38-win_amd64.whl", hash = "sha256:5013b0468712dc9fbd9ace297821d27677abf427ac32fe2b537d8876bfb41c65"},
{file = "reportlab-3.6.7-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:891b2ad614b43d911f70bcdefe2366db8ca7e85e952ffa96255975c1636d46e4"},
{file = "reportlab-3.6.7-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:fe319e0891e20fb04f038926ace02cf87f34fd223c478437572b5ed2b821f8af"},
{file = "reportlab-3.6.7-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:51c0c5c0611c9b6b3d1aae0037efb303c57da6d1349830b83915a92bd0f5f246"},
{file = "reportlab-3.6.7-cp39-cp39-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e703d862c1c85b5e377e3cf2b33763303deb80e799ad301e99d09ec81feecdb1"},
{file = "reportlab-3.6.7-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a13824c9363393ead2ed72907ecc40250c6d3489963627bd34e437e5accea2a1"},
{file = "reportlab-3.6.7-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:51b222f2315d34a7201b1ea5d843715a2625107838de52979406d4dd5ab2f7f6"},
{file = "reportlab-3.6.7-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:ea5ca026695210cc3364328a9490b2f29ce97b99614950df8fb11b2680641008"},
{file = "reportlab-3.6.7-cp39-cp39-win32.whl", hash = "sha256:d5f5ab22f71433a665f50b24cced62298626753536a50db0c769c2fee07038e6"},
{file = "reportlab-3.6.7-cp39-cp39-win_amd64.whl", hash = "sha256:06c8dbaa26c2bf63e7fcddb86f232ddaf7c57141072407d22b4e8cbfcc5348e4"},
{file = "reportlab-3.6.7.tar.gz", hash = "sha256:d035054724278129aba2a55c3edd6111b07e625ad1ff845e3ba2a5e4e203bbc8"},
]
sentinel = [ sentinel = [
{file = "sentinel-0.3.0-py3-none-any.whl", hash = "sha256:bd8710dd26752039c668604f6be2aaf741b56f7811c5924a4dcdfd74359244f3"}, {file = "sentinel-0.3.0-py3-none-any.whl", hash = "sha256:bd8710dd26752039c668604f6be2aaf741b56f7811c5924a4dcdfd74359244f3"},
{file = "sentinel-0.3.0.tar.gz", hash = "sha256:f28143aa4716dbc8f6193f5682176a3c33cd26aaae05d9ecf66c186a9887cc2d"}, {file = "sentinel-0.3.0.tar.gz", hash = "sha256:f28143aa4716dbc8f6193f5682176a3c33cd26aaae05d9ecf66c186a9887cc2d"},
] ]
sentry-sdk = [ sentry-sdk = [
{file = "sentry-sdk-1.4.3.tar.gz", hash = "sha256:b9844751e40710e84a457c5bc29b21c383ccb2b63d76eeaad72f7f1c808c8828"}, {file = "sentry-sdk-1.5.5.tar.gz", hash = "sha256:98fd155fa5d5fec1dbabed32a1a4ae2705f1edaa5dae4e7f7b62a384ba30e759"},
{file = "sentry_sdk-1.4.3-py2.py3-none-any.whl", hash = "sha256:c091cc7115ff25fe3a0e410dbecd7a996f81a3f6137d2272daef32d6c3cfa6dc"}, {file = "sentry_sdk-1.5.5-py2.py3-none-any.whl", hash = "sha256:3817274fba2498c8ebf6b896ee98ac916c5598706340573268c07bf2bb30d831"},
] ]
six = [ six = [
{file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"}, {file = "six-1.16.0-py2.py3-none-any.whl", hash = "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254"},
@@ -829,13 +1059,20 @@ sqlparse = [
{file = "sqlparse-0.4.2.tar.gz", hash = "sha256:0c00730c74263a94e5a9919ade150dfc3b19c574389985446148402998287dae"}, {file = "sqlparse-0.4.2.tar.gz", hash = "sha256:0c00730c74263a94e5a9919ade150dfc3b19c574389985446148402998287dae"},
] ]
strawberry-graphql = [ strawberry-graphql = [
{file = "strawberry-graphql-0.93.23.tar.gz", hash = "sha256:061b1c54c69dd7e11cb2b7e7b474392e4b32943789e7ac0b47954fb965999d31"}, {file = "strawberry-graphql-0.97.0.tar.gz", hash = "sha256:0bd486111166da1d6e48a2c95de437096ca2ba28e2b2963b64a93136c54bef02"},
{file = "strawberry_graphql-0.93.23-py3-none-any.whl", hash = "sha256:b2a9a4af0a491e1626aafc420e484f551f89eacb13c0e48de9ab5dd8f7e5ca34"}, {file = "strawberry_graphql-0.97.0-py3-none-any.whl", hash = "sha256:2f304822c6ecc866e07f11618fbdf826676ab61af5f757794805ee1853504603"},
] ]
strawberry-graphql-django = [ strawberry-graphql-django = [
{file = "strawberry-graphql-django-0.2.5.tar.gz", hash = "sha256:483d97c24ce440a1e3cb92ec3e1fdc0cf534d9afa79600396b0a27a5d9d97793"}, {file = "strawberry-graphql-django-0.2.5.tar.gz", hash = "sha256:483d97c24ce440a1e3cb92ec3e1fdc0cf534d9afa79600396b0a27a5d9d97793"},
{file = "strawberry_graphql_django-0.2.5-py3-none-any.whl", hash = "sha256:dee8011bf074c618b7ddab0a92d0832b0ceea2dcb69ee422003a98e4a28c14d6"}, {file = "strawberry_graphql_django-0.2.5-py3-none-any.whl", hash = "sha256:dee8011bf074c618b7ddab0a92d0832b0ceea2dcb69ee422003a98e4a28c14d6"},
] ]
svglib = [
{file = "svglib-1.2.1.tar.gz", hash = "sha256:c77a0702fafd367c0fdca08ca1b7e1ee10058bde3bae252f49a3836e51e54519"},
]
tinycss2 = [
{file = "tinycss2-1.1.1-py3-none-any.whl", hash = "sha256:fe794ceaadfe3cf3e686b22155d0da5780dd0e273471a51846d0a02bc204fec8"},
{file = "tinycss2-1.1.1.tar.gz", hash = "sha256:b2e44dd8883c360c35dd0d1b5aad0b610e5156c2cb3b33434634e539ead9d8bf"},
]
toml = [ toml = [
{file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"}, {file = "toml-0.10.2-py2.py3-none-any.whl", hash = "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b"},
{file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"}, {file = "toml-0.10.2.tar.gz", hash = "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f"},
@@ -848,6 +1085,10 @@ typing-extensions = [
{file = "typing_extensions-4.1.1-py3-none-any.whl", hash = "sha256:21c85e0fe4b9a155d0799430b0ad741cdce7e359660ccbd8b530613e8df88ce2"}, {file = "typing_extensions-4.1.1-py3-none-any.whl", hash = "sha256:21c85e0fe4b9a155d0799430b0ad741cdce7e359660ccbd8b530613e8df88ce2"},
{file = "typing_extensions-4.1.1.tar.gz", hash = "sha256:1a9462dcc3347a79b1f1c0271fbe79e844580bb598bafa1ed208b94da3cdcd42"}, {file = "typing_extensions-4.1.1.tar.gz", hash = "sha256:1a9462dcc3347a79b1f1c0271fbe79e844580bb598bafa1ed208b94da3cdcd42"},
] ]
tzdata = [
{file = "tzdata-2021.5-py2.py3-none-any.whl", hash = "sha256:3eee491e22ebfe1e5cfcc97a4137cd70f092ce59144d81f8924a844de05ba8f5"},
{file = "tzdata-2021.5.tar.gz", hash = "sha256:68dbe41afd01b867894bbdfd54fa03f468cfa4f0086bfb4adcd8de8f24f3ee21"},
]
urllib3 = [ urllib3 = [
{file = "urllib3-1.26.8-py2.py3-none-any.whl", hash = "sha256:000ca7f471a233c2251c6c7023ee85305721bfdf18621ebff4fd17a8653427ed"}, {file = "urllib3-1.26.8-py2.py3-none-any.whl", hash = "sha256:000ca7f471a233c2251c6c7023ee85305721bfdf18621ebff4fd17a8653427ed"},
{file = "urllib3-1.26.8.tar.gz", hash = "sha256:0e7c33d9a63e7ddfcb86780aac87befc2fbddf46c58dbb487e0855f7ceec283c"}, {file = "urllib3-1.26.8.tar.gz", hash = "sha256:0e7c33d9a63e7ddfcb86780aac87befc2fbddf46c58dbb487e0855f7ceec283c"},
@@ -856,3 +1097,7 @@ virtualenv = [
{file = "virtualenv-20.13.1-py2.py3-none-any.whl", hash = "sha256:45e1d053cad4cd453181ae877c4ffc053546ae99e7dd049b9ff1d9be7491abf7"}, {file = "virtualenv-20.13.1-py2.py3-none-any.whl", hash = "sha256:45e1d053cad4cd453181ae877c4ffc053546ae99e7dd049b9ff1d9be7491abf7"},
{file = "virtualenv-20.13.1.tar.gz", hash = "sha256:e0621bcbf4160e4e1030f05065c8834b4e93f4fcc223255db2a823440aca9c14"}, {file = "virtualenv-20.13.1.tar.gz", hash = "sha256:e0621bcbf4160e4e1030f05065c8834b4e93f4fcc223255db2a823440aca9c14"},
] ]
webencodings = [
{file = "webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78"},
{file = "webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923"},
]
+7 -6
View File
@@ -1,5 +1,5 @@
[tool.pytest.ini_options] [tool.pytest.ini_options]
DJANGO_SETTINGS_MODULE = "bbssite.settings" DJANGO_SETTINGS_MODULE = "bbs.settings"
[tool.black] [tool.black]
line-length = 120 line-length = 120
@@ -17,17 +17,18 @@ authors = ["Nikolai R Kristiansen <nikolaik@gmail.com>"]
license = "MIT" license = "MIT"
[tool.poetry.dependencies] [tool.poetry.dependencies]
python = "^3.9" python = "~3.8"
django-extensions = "^3.1.3" django-extensions = "^3.1.3"
Django = "^3.2.8" Django = "^4.0.0"
easy-thumbnails = "2.7.2" easy-thumbnails = "^2.8.1"
gunicorn = "^20.1.0" gunicorn = "^20.1.0"
mysqlclient = "^2.0.3" mysqlclient = "^2.0.3"
Pillow = "^9.0.0" Pillow = "^9.0.0"
sentry-sdk = "1.4.3" sentry-sdk = "^1.5.5"
strawberry-graphql = "^0.93.1" strawberry-graphql = "^0.97.0"
strawberry-graphql-django = "^0.2.5" strawberry-graphql-django = "^0.2.5"
django-cors-headers = "^3.10.0" django-cors-headers = "^3.10.0"
django-rename-app = "^0.1.3"
[tool.poetry.dev-dependencies] [tool.poetry.dev-dependencies]
pytest = "*" pytest = "*"