Merge remote-tracking branch 'refs/remotes/origin/development' into development
# Conflicts: # pyproject.toml # uv.lock
This commit is contained in:
2
.idea/TeamForge.iml
generated
2
.idea/TeamForge.iml
generated
@@ -16,7 +16,7 @@
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<excludeFolder url="file://$MODULE_DIR$/.venv" />
|
||||
</content>
|
||||
<orderEntry type="jdk" jdkName="uv (TeamForge) (2)" jdkType="Python SDK" />
|
||||
<orderEntry type="jdk" jdkName="uv (TeamForge)" jdkType="Python SDK" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
<component name="TemplatesService">
|
||||
|
||||
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
5
.idea/codeStyles/codeStyleConfig.xml
generated
Normal file
@@ -0,0 +1,5 @@
|
||||
<component name="ProjectCodeStyleConfiguration">
|
||||
<state>
|
||||
<option name="PREFERRED_PROJECT_CODE_STYLE" value="Default" />
|
||||
</state>
|
||||
</component>
|
||||
6
.idea/misc.xml
generated
6
.idea/misc.xml
generated
@@ -3,5 +3,9 @@
|
||||
<component name="Black">
|
||||
<option name="sdkName" value="uv (TeamForge) (2)" />
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="uv (TeamForge) (2)" project-jdk-type="Python SDK" />
|
||||
<component name="ProjectRootManager" version="2" project-jdk-name="uv (TeamForge)" project-jdk-type="Python SDK" />
|
||||
<component name="RuffConfiguration">
|
||||
<option name="enabled" value="true" />
|
||||
<option name="sdkName" value="uv (TeamForge)" />
|
||||
</component>
|
||||
</project>
|
||||
2
Procfile.tailwind
Normal file
2
Procfile.tailwind
Normal file
@@ -0,0 +1,2 @@
|
||||
django: python manage.py runserver
|
||||
tailwind: python manage.py tailwind start
|
||||
@@ -12,6 +12,10 @@ https://docs.djangoproject.com/en/6.0/ref/settings/
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from decouple import Csv, config
|
||||
from dj_database_url import parse as db_url
|
||||
from django.template.context_processors import static
|
||||
|
||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||
|
||||
@@ -20,64 +24,70 @@ BASE_DIR = Path(__file__).resolve().parent.parent
|
||||
# See https://docs.djangoproject.com/en/6.0/howto/deployment/checklist/
|
||||
|
||||
# SECURITY WARNING: keep the secret key used in production secret!
|
||||
SECRET_KEY = 'django-insecure-v)ykw+=v51x^t=^rc8g-zi(j&7zjaf3opik-zy=ug27l4zcfhe'
|
||||
SECRET_KEY = config("DJANGO_SECRET_KEY")
|
||||
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = True
|
||||
DEBUG = config("DJANGO_DEBUG", default=False, cast=bool)
|
||||
|
||||
ALLOWED_HOSTS = []
|
||||
ALLOWED_HOSTS = config("DJANGO_ALLOWED_HOSTS", default="", cast=Csv())
|
||||
INTERNAL_IPS = ["127.0.0.1"]
|
||||
|
||||
CSRF_TRUSTED_ORIGINS = config("DJANGO_CSRF_TRUSTED_ORIGINS", default="", cast=Csv())
|
||||
|
||||
# Application definition
|
||||
|
||||
INSTALLED_APPS = [
|
||||
'django.contrib.admin',
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
"django.contrib.admin",
|
||||
"django.contrib.auth",
|
||||
"django.contrib.contenttypes",
|
||||
"django.contrib.sessions",
|
||||
"django.contrib.messages",
|
||||
"django.contrib.staticfiles",
|
||||
"constance",
|
||||
"tailwind",
|
||||
"django_filters",
|
||||
"rules.apps.AutodiscoverRulesConfig",
|
||||
"theme.apps.ThemeConfig", # Tailwind theme app
|
||||
"members.apps.MembersConfig",
|
||||
"backend.apps.BackendConfig",
|
||||
]
|
||||
|
||||
MIDDLEWARE = [
|
||||
'django.middleware.security.SecurityMiddleware',
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
"django.middleware.security.SecurityMiddleware",
|
||||
"django.contrib.sessions.middleware.SessionMiddleware",
|
||||
"django.middleware.common.CommonMiddleware",
|
||||
"django.middleware.csrf.CsrfViewMiddleware",
|
||||
"django.contrib.auth.middleware.AuthenticationMiddleware",
|
||||
"django.contrib.messages.middleware.MessageMiddleware",
|
||||
"django.middleware.clickjacking.XFrameOptionsMiddleware",
|
||||
]
|
||||
|
||||
ROOT_URLCONF = 'TeamForge.urls'
|
||||
ROOT_URLCONF = "TeamForge.urls"
|
||||
|
||||
TEMPLATES = [
|
||||
{
|
||||
'BACKEND': 'django.template.backends.django.DjangoTemplates',
|
||||
'DIRS': [BASE_DIR / 'templates']
|
||||
,
|
||||
'APP_DIRS': True,
|
||||
'OPTIONS': {
|
||||
'context_processors': [
|
||||
'django.template.context_processors.request',
|
||||
'django.contrib.auth.context_processors.auth',
|
||||
'django.contrib.messages.context_processors.messages',
|
||||
"BACKEND": "django.template.backends.django.DjangoTemplates",
|
||||
"DIRS": [BASE_DIR / "templates"],
|
||||
"APP_DIRS": True,
|
||||
"OPTIONS": {
|
||||
"context_processors": [
|
||||
"constance.context_processors.config",
|
||||
"django.template.context_processors.request",
|
||||
"django.contrib.auth.context_processors.auth",
|
||||
"django.contrib.messages.context_processors.messages",
|
||||
],
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
WSGI_APPLICATION = 'TeamForge.wsgi.application'
|
||||
WSGI_APPLICATION = "TeamForge.wsgi.application"
|
||||
|
||||
|
||||
# Database
|
||||
# https://docs.djangoproject.com/en/6.0/ref/settings/#databases
|
||||
|
||||
DATABASES = {
|
||||
'default': {
|
||||
'ENGINE': 'django.db.backends.sqlite3',
|
||||
'NAME': BASE_DIR / 'db.sqlite3',
|
||||
}
|
||||
"default": config("DJANGO_DATABASE_URL", default="sqlite:///db.sqlite3", cast=db_url),
|
||||
}
|
||||
|
||||
|
||||
@@ -86,16 +96,16 @@ DATABASES = {
|
||||
|
||||
AUTH_PASSWORD_VALIDATORS = [
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator',
|
||||
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
|
||||
"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator',
|
||||
"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
|
||||
},
|
||||
{
|
||||
'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
|
||||
"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
|
||||
},
|
||||
]
|
||||
|
||||
@@ -103,9 +113,9 @@ AUTH_PASSWORD_VALIDATORS = [
|
||||
# Internationalization
|
||||
# https://docs.djangoproject.com/en/6.0/topics/i18n/
|
||||
|
||||
LANGUAGE_CODE = 'en-us'
|
||||
LANGUAGE_CODE = "en-us"
|
||||
|
||||
TIME_ZONE = 'UTC'
|
||||
TIME_ZONE = config("DJANGO_TIME_ZONE", default="Europe/Brussels", cast=str)
|
||||
|
||||
USE_I18N = True
|
||||
|
||||
@@ -115,4 +125,29 @@ USE_TZ = True
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/6.0/howto/static-files/
|
||||
|
||||
STATIC_URL = 'static/'
|
||||
STATIC_URL = "static/"
|
||||
STATIC_ROOT = BASE_DIR / "staticfiles"
|
||||
STATICFILES_DIRS = [BASE_DIR / "static"]
|
||||
|
||||
MEDIA_URL = "media/"
|
||||
MEDIA_ROOT = BASE_DIR / "media"
|
||||
|
||||
AUTHENTICATION_BACKENDS = [
|
||||
"rules.permissions.ObjectPermissionBackend",
|
||||
"django.contrib.auth.backends.ModelBackend",
|
||||
]
|
||||
|
||||
CONSTANCE_BACKEND = "constance.backends.database.DatabaseBackend"
|
||||
CONSTANCE_CONFIG = {
|
||||
"TF_CLUB_NAME": (config("TF_CLUB_NAME", default="TeamForge", cast=str), "Club Name", str),
|
||||
"TF_CLUB_LOGO": (config("TF_CLUB_LOGO", default="teamforge/logo.png", cast=str), "Club Logo", str),
|
||||
"TF_CLUB_HOME": (config("TF_CLUB_HOME", default="TeamForge Home", cast=str), "Club Location", str),
|
||||
"TF_DEFAULT_SEASON_MONTH": (config("TF_DEFAULT_SEASON_MONTH", default=8, cast=int), "Default season start month", int),
|
||||
"TF_DEFAULT_SEASON_DAY": (config("TF_DEFAULT_SEASON_DAY", default=1, cast=int), "Default season start day", int),
|
||||
"TF_DEFAULT_SEASON_DURATION": (config("TF_DEFAULT_SEASON_DURATION", default="1y", cast=str), "Default season duration", str),
|
||||
}
|
||||
|
||||
PHONENUMBER_DEFAULT_FORMAT = "INTERNATIONAL"
|
||||
PHONENUMBER_DEFAULT_REGION = config("CM_CLUB_COUNTRY_CODE", default="BE", cast=str)
|
||||
|
||||
TAILWIND_APP_NAME = "theme"
|
||||
|
||||
@@ -15,8 +15,9 @@ Including another URLconf
|
||||
2. Add a URL to urlpatterns: path('blog/', include('blog.urls'))
|
||||
"""
|
||||
from django.contrib import admin
|
||||
from django.urls import path
|
||||
from django.urls import include, path
|
||||
|
||||
urlpatterns = [
|
||||
path('backend/', include('backend.urls')),
|
||||
path('admin/', admin.site.urls),
|
||||
]
|
||||
|
||||
0
backend/__init__.py
Normal file
0
backend/__init__.py
Normal file
5
backend/apps.py
Normal file
5
backend/apps.py
Normal file
@@ -0,0 +1,5 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class BackendConfig(AppConfig):
|
||||
name = "backend"
|
||||
0
backend/members/__init__.py
Normal file
0
backend/members/__init__.py
Normal file
12
backend/members/urls.py
Normal file
12
backend/members/urls.py
Normal file
@@ -0,0 +1,12 @@
|
||||
from django.urls import include, path
|
||||
|
||||
from .views import MemberAddView, MemberDeleteView, MemberEditView, MemberListView, MemberLoadView
|
||||
|
||||
app_name = "members"
|
||||
urlpatterns = [
|
||||
path("", MemberListView.as_view(), name="list"),
|
||||
# path("add/", MemberAddView.as_view(), name="add"),
|
||||
# path("<int:pk>/edit/", MemberEditView.as_view(), name="edit"),
|
||||
# path("<int:pk>/delete/", MemberDeleteView.as_view(), name="delete"),
|
||||
# path("load/", MemberLoadView.as_view(), name="load"),
|
||||
]
|
||||
34
backend/members/views.py
Normal file
34
backend/members/views.py
Normal file
@@ -0,0 +1,34 @@
|
||||
from typing import Any
|
||||
|
||||
from django.contrib import messages
|
||||
from django.http import HttpResponse, HttpResponseRedirect
|
||||
from django.urls import reverse_lazy
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django_filters.views import FilterView
|
||||
from rules.contrib.views import PermissionRequiredMixin
|
||||
|
||||
from members.filters import MemberFilter
|
||||
from members.models import Member
|
||||
|
||||
|
||||
class MemberListView(PermissionRequiredMixin, FilterView):
|
||||
filterset_class = MemberFilter
|
||||
paginate_by = 50
|
||||
permission_denied_message = _("You do not have permission to view this page.")
|
||||
permission_required = "members.view_member"
|
||||
|
||||
def handle_no_permission(self) -> HttpResponseRedirect:
|
||||
messages.error(self.request, self.get_permission_denied_message())
|
||||
return HttpResponseRedirect(reverse_lazy("backend:index"))
|
||||
|
||||
|
||||
class MemberAddView: ...
|
||||
|
||||
|
||||
class MemberEditView: ...
|
||||
|
||||
|
||||
class MemberDeleteView: ...
|
||||
|
||||
|
||||
class MemberLoadView: ...
|
||||
9
backend/urls.py
Normal file
9
backend/urls.py
Normal file
@@ -0,0 +1,9 @@
|
||||
from django.urls import include, path
|
||||
|
||||
from .views import index
|
||||
|
||||
app_name = "backend"
|
||||
urlpatterns = [
|
||||
path("", index, name="index"),
|
||||
path("members/", include("backend.members.urls")),
|
||||
]
|
||||
6
backend/views.py
Normal file
6
backend/views.py
Normal file
@@ -0,0 +1,6 @@
|
||||
from django.shortcuts import render
|
||||
|
||||
|
||||
# Create your views here.
|
||||
def index(request):
|
||||
return render(request, "backend/index.html")
|
||||
0
members/__init__.py
Normal file
0
members/__init__.py
Normal file
28
members/admin.py
Normal file
28
members/admin.py
Normal file
@@ -0,0 +1,28 @@
|
||||
from django.contrib import admin
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from .models import Member
|
||||
|
||||
|
||||
@admin.register(Member)
|
||||
class MemberAdmin(admin.ModelAdmin):
|
||||
def family_member_count(self, obj):
|
||||
return obj.family_members.count()
|
||||
|
||||
def show_user_is_active(self, obj):
|
||||
return obj.user.is_active
|
||||
|
||||
show_user_is_active.boolean = True
|
||||
show_user_is_active.short_description = _("active?")
|
||||
|
||||
list_display = ["__str__", "user__email", "show_user_is_active", "family_member_count", "birthday", "created", "updated"]
|
||||
date_hierarchy = "birthday"
|
||||
list_filter = ["user__is_superuser", "user__is_active"]
|
||||
readonly_fields = ["created", "updated", "access_token"]
|
||||
filter_horizontal = ["family_members"]
|
||||
raw_id_fields = ["user"]
|
||||
fieldsets = [
|
||||
("GENERAL INFORMATION", {"fields": ["user", "family_members", "birthday", "license", "access_token"]}),
|
||||
("CONTACT_INFORMATION", {"fields": ["phone_number", "emergency_phone_number"]}),
|
||||
("METADATA", {"fields": ["created", "updated"]}),
|
||||
]
|
||||
9
members/apps.py
Normal file
9
members/apps.py
Normal file
@@ -0,0 +1,9 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class MembersConfig(AppConfig):
|
||||
name = "members"
|
||||
|
||||
def ready(self):
|
||||
# noinspection PyUnusedImports
|
||||
import members.signals
|
||||
16
members/filters.py
Normal file
16
members/filters.py
Normal file
@@ -0,0 +1,16 @@
|
||||
import django_filters
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
|
||||
from .models import Member
|
||||
|
||||
|
||||
class MemberFilter(django_filters.FilterSet):
|
||||
user__first_name = django_filters.CharFilter(field_name="user__first_name", label=_("First name"))
|
||||
user__last_name = django_filters.CharFilter(field_name="user__last_name", label=_("Last name"))
|
||||
license = django_filters.CharFilter(label=_("License"), lookup_expr="icontains")
|
||||
|
||||
class Meta:
|
||||
model = Member
|
||||
fields = ["user__first_name", "user__last_name", "license"]
|
||||
|
||||
|
||||
48
members/migrations/0001_initial.py
Normal file
48
members/migrations/0001_initial.py
Normal file
@@ -0,0 +1,48 @@
|
||||
# Generated by Django 6.0 on 2026-01-03 11:53
|
||||
|
||||
import django.db.models.deletion
|
||||
import rules.contrib.models
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="Member",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.BigAutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
(
|
||||
"user",
|
||||
models.OneToOneField(
|
||||
on_delete=django.db.models.deletion.CASCADE,
|
||||
related_name="member",
|
||||
to=settings.AUTH_USER_MODEL,
|
||||
verbose_name="user",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={
|
||||
"verbose_name": "member",
|
||||
"verbose_name_plural": "members",
|
||||
"ordering": ["user__last_name", "user__first_name"],
|
||||
"permissions": [("member_manager", "Can manage members")],
|
||||
},
|
||||
bases=(rules.contrib.models.RulesModelMixin, models.Model),
|
||||
),
|
||||
]
|
||||
23
members/migrations/0002_member_family.py
Normal file
23
members/migrations/0002_member_family.py
Normal file
@@ -0,0 +1,23 @@
|
||||
# Generated by Django 6.0 on 2026-01-03 11:55
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("members", "0001_initial"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="member",
|
||||
name="family",
|
||||
field=models.ManyToManyField(
|
||||
blank=True,
|
||||
related_name="family",
|
||||
to="members.member",
|
||||
verbose_name="family",
|
||||
),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,36 @@
|
||||
# Generated by Django 6.0 on 2026-01-03 12:00
|
||||
|
||||
import django.utils.timezone
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("members", "0002_member_family"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="member",
|
||||
name="created",
|
||||
field=models.DateTimeField(
|
||||
auto_now_add=True,
|
||||
default=django.utils.timezone.now,
|
||||
verbose_name="created",
|
||||
),
|
||||
preserve_default=False,
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="member",
|
||||
name="updated",
|
||||
field=models.DateTimeField(auto_now=True, verbose_name="updated"),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name="member",
|
||||
name="family",
|
||||
field=models.ManyToManyField(
|
||||
blank=True, to="members.member", verbose_name="family"
|
||||
),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,55 @@
|
||||
# Generated by Django 6.0 on 2026-01-03 12:12
|
||||
|
||||
import phonenumber_field.modelfields
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("members", "0003_member_created_member_updated_alter_member_family"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name="member",
|
||||
name="access_token",
|
||||
field=models.CharField(
|
||||
blank=True, max_length=255, null=True, verbose_name="access token"
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="member",
|
||||
name="birthday",
|
||||
field=models.DateField(blank=True, null=True, verbose_name="birthday"),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="member",
|
||||
name="emergency_phone_number",
|
||||
field=phonenumber_field.modelfields.PhoneNumberField(
|
||||
blank=True,
|
||||
max_length=128,
|
||||
null=True,
|
||||
region=None,
|
||||
verbose_name="emergency phone number",
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="member",
|
||||
name="license",
|
||||
field=models.CharField(
|
||||
blank=True, max_length=20, null=True, verbose_name="license"
|
||||
),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="member",
|
||||
name="phone_number",
|
||||
field=phonenumber_field.modelfields.PhoneNumberField(
|
||||
blank=True,
|
||||
max_length=128,
|
||||
null=True,
|
||||
region=None,
|
||||
verbose_name="phone number",
|
||||
),
|
||||
),
|
||||
]
|
||||
@@ -0,0 +1,24 @@
|
||||
# Generated by Django 6.0 on 2026-01-03 12:20
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("members", "0004_member_access_token_member_birthday_and_more"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name="member",
|
||||
name="family",
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name="member",
|
||||
name="family_members",
|
||||
field=models.ManyToManyField(
|
||||
blank=True, to="members.member", verbose_name="family members"
|
||||
),
|
||||
),
|
||||
]
|
||||
0
members/migrations/__init__.py
Normal file
0
members/migrations/__init__.py
Normal file
46
members/models.py
Normal file
46
members/models.py
Normal file
@@ -0,0 +1,46 @@
|
||||
from django.conf import settings
|
||||
from django.db import models
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from phonenumber_field.modelfields import PhoneNumberField
|
||||
from rules import is_superuser
|
||||
from rules.contrib.models import RulesModel
|
||||
|
||||
from members.rules import is_member_manager
|
||||
|
||||
|
||||
class MemberManager(models.Manager):
|
||||
def get_queryset(self) -> models.QuerySet:
|
||||
return super().get_queryset().select_related("user")
|
||||
|
||||
|
||||
class Member(RulesModel):
|
||||
user = models.OneToOneField(settings.AUTH_USER_MODEL, on_delete=models.CASCADE, related_name="member", verbose_name=_("user"))
|
||||
family_members = models.ManyToManyField("self", symmetrical=True, blank=True, verbose_name=_("family members"))
|
||||
|
||||
birthday = models.DateField(_("birthday"), blank=True, null=True)
|
||||
license = models.CharField(_("license"), max_length=20, blank=True, null=True)
|
||||
|
||||
phone_number = PhoneNumberField(_("phone number"), blank=True, null=True)
|
||||
emergency_phone_number = PhoneNumberField(_("emergency phone number"), blank=True, null=True)
|
||||
|
||||
access_token = models.CharField(_("access token"), max_length=255, blank=True, null=True)
|
||||
|
||||
created = models.DateTimeField(auto_now_add=True, verbose_name=_("created"))
|
||||
updated = models.DateTimeField(auto_now=True, verbose_name=_("updated"))
|
||||
|
||||
objects = MemberManager()
|
||||
|
||||
class Meta:
|
||||
verbose_name = _("member")
|
||||
verbose_name_plural = _("members")
|
||||
ordering = ["user__last_name", "user__first_name"]
|
||||
permissions = [("member_manager", _("Can manage members"))]
|
||||
rules_permissions = {
|
||||
"add": is_superuser | is_member_manager,
|
||||
"change": is_superuser | is_member_manager,
|
||||
"delete": is_superuser | is_member_manager,
|
||||
"view": is_superuser | is_member_manager,
|
||||
}
|
||||
|
||||
def __str__(self):
|
||||
return self.user.get_full_name()
|
||||
9
members/rules.py
Normal file
9
members/rules.py
Normal file
@@ -0,0 +1,9 @@
|
||||
from typing import Optional
|
||||
|
||||
import rules
|
||||
from django.contrib.auth.models import AbstractUser
|
||||
|
||||
|
||||
@rules.predicate
|
||||
def is_member_manager(user: Optional[AbstractUser]) -> bool:
|
||||
return user.has_perm('members.member_manager')
|
||||
19
members/signals.py
Normal file
19
members/signals.py
Normal file
@@ -0,0 +1,19 @@
|
||||
import secrets
|
||||
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.db.models.signals import post_save
|
||||
from django.dispatch import receiver
|
||||
|
||||
from .models import Member
|
||||
|
||||
User = get_user_model()
|
||||
|
||||
|
||||
@receiver(post_save, sender=User)
|
||||
def create_member_profile(instance, *args, **kwargs) -> None:
|
||||
"""Creates a Member profile for a newly created user."""
|
||||
member, created = Member.objects.get_or_create(user=instance)
|
||||
|
||||
if not member.access_token:
|
||||
member.access_token = secrets.token_urlsafe(64)
|
||||
member.save(update_fields=["access_token"])
|
||||
30
members/tests.py
Normal file
30
members/tests.py
Normal file
@@ -0,0 +1,30 @@
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.contrib.auth.models import Permission
|
||||
from django.test import TestCase
|
||||
|
||||
User = get_user_model()
|
||||
|
||||
|
||||
class MembersTestCase(TestCase):
|
||||
def setUp(self):
|
||||
self.user_a = User.objects.create(username="user_a", first_name="User", last_name="A", email="user_a@test.com")
|
||||
|
||||
def testMemberName(self):
|
||||
self.assertEqual(str(self.user_a.member), "User A")
|
||||
|
||||
def testMemberCreation(self):
|
||||
self.assertTrue(hasattr(self.user_a, "member"))
|
||||
|
||||
def testMemberManager(self):
|
||||
self.assertFalse(self.user_a.has_perm("members.member_manager"))
|
||||
self.assertFalse(self.user_a.has_perm("members.add_member"))
|
||||
self.assertFalse(self.user_a.is_superuser)
|
||||
|
||||
member_manager_permission = Permission.objects.get(codename="member_manager")
|
||||
|
||||
self.user_a.user_permissions.add(member_manager_permission)
|
||||
self.user_a = User.objects.get(pk=self.user_a.pk)
|
||||
|
||||
self.assertTrue(self.user_a.has_perm("members.member_manager"))
|
||||
self.assertTrue(self.user_a.has_perm("members.add_member"))
|
||||
self.assertFalse(self.user_a.is_superuser)
|
||||
@@ -8,10 +8,9 @@ dependencies = [
|
||||
"django>=6.0",
|
||||
"django-constance>=4.3.4",
|
||||
"django-extensions>=4.1",
|
||||
"django-filter>=25.2",
|
||||
"django-phonenumber-field[phonenumbers]>=8.4.0",
|
||||
"django-polymorphic>=4.5.1",
|
||||
"django-tailwind[cookiecutter,honcho,reload]>=4.4.2",
|
||||
"pillow>=12.0.0",
|
||||
"django-tailwind[cookiecutter,honcho]>=4.4.2",
|
||||
"psycopg2-binary>=2.9.11",
|
||||
"python-decouple>=3.8",
|
||||
"rules>=3.5",
|
||||
@@ -22,3 +21,6 @@ dev = [
|
||||
"coverage>=7.13.1",
|
||||
"ruff>=0.14.10",
|
||||
]
|
||||
|
||||
[tool.ruff]
|
||||
line-length = 250
|
||||
|
||||
10663
static/css/all.css
Normal file
10663
static/css/all.css
Normal file
File diff suppressed because it is too large
Load Diff
9
static/css/all.min.css
vendored
Normal file
9
static/css/all.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
2219
static/css/brands.css
Normal file
2219
static/css/brands.css
Normal file
File diff suppressed because it is too large
Load Diff
6
static/css/brands.min.css
vendored
Normal file
6
static/css/brands.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
8361
static/css/fontawesome.css
vendored
Normal file
8361
static/css/fontawesome.css
vendored
Normal file
File diff suppressed because it is too large
Load Diff
8
static/css/fontawesome.min.css
vendored
Normal file
8
static/css/fontawesome.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
31
static/css/regular.css
Normal file
31
static/css/regular.css
Normal file
@@ -0,0 +1,31 @@
|
||||
/*!
|
||||
* Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||
* Copyright 2025 Fonticons, Inc.
|
||||
*/
|
||||
:root, :host {
|
||||
--fa-family-classic: "Font Awesome 7 Free";
|
||||
--fa-font-regular: normal 400 1em/1 var(--fa-family-classic);
|
||||
/* deprecated: this older custom property will be removed next major release */
|
||||
--fa-style-family-classic: var(--fa-family-classic);
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Font Awesome 7 Free";
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: block;
|
||||
src: url("../webfonts/fa-regular-400.woff2");
|
||||
}
|
||||
.far {
|
||||
--fa-family: var(--fa-family-classic);
|
||||
--fa-style: 400;
|
||||
}
|
||||
|
||||
.fa-classic {
|
||||
--fa-family: var(--fa-family-classic);
|
||||
}
|
||||
|
||||
.fa-regular {
|
||||
--fa-style: 400;
|
||||
}
|
||||
6
static/css/regular.min.css
vendored
Normal file
6
static/css/regular.min.css
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
/*!
|
||||
* Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||
* Copyright 2025 Fonticons, Inc.
|
||||
*/
|
||||
:host,:root{--fa-family-classic:"Font Awesome 7 Free";--fa-font-regular:normal 400 1em/1 var(--fa-family-classic);--fa-style-family-classic:var(--fa-family-classic)}@font-face{font-family:"Font Awesome 7 Free";font-style:normal;font-weight:400;font-display:block;src:url(../webfonts/fa-regular-400.woff2)}.far{--fa-style:400}.fa-classic,.far{--fa-family:var(--fa-family-classic)}.fa-regular{--fa-style:400}
|
||||
31
static/css/solid.css
Normal file
31
static/css/solid.css
Normal file
@@ -0,0 +1,31 @@
|
||||
/*!
|
||||
* Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||
* Copyright 2025 Fonticons, Inc.
|
||||
*/
|
||||
:root, :host {
|
||||
--fa-family-classic: "Font Awesome 7 Free";
|
||||
--fa-font-solid: normal 900 1em/1 var(--fa-family-classic);
|
||||
/* deprecated: this older custom property will be removed next major release */
|
||||
--fa-style-family-classic: var(--fa-family-classic);
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "Font Awesome 7 Free";
|
||||
font-style: normal;
|
||||
font-weight: 900;
|
||||
font-display: block;
|
||||
src: url("../webfonts/fa-solid-900.woff2");
|
||||
}
|
||||
.fas {
|
||||
--fa-family: var(--fa-family-classic);
|
||||
--fa-style: 900;
|
||||
}
|
||||
|
||||
.fa-classic {
|
||||
--fa-family: var(--fa-family-classic);
|
||||
}
|
||||
|
||||
.fa-solid {
|
||||
--fa-style: 900;
|
||||
}
|
||||
6
static/css/solid.min.css
vendored
Normal file
6
static/css/solid.min.css
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
/*!
|
||||
* Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||
* Copyright 2025 Fonticons, Inc.
|
||||
*/
|
||||
:host,:root{--fa-family-classic:"Font Awesome 7 Free";--fa-font-solid:normal 900 1em/1 var(--fa-family-classic);--fa-style-family-classic:var(--fa-family-classic)}@font-face{font-family:"Font Awesome 7 Free";font-style:normal;font-weight:900;font-display:block;src:url(../webfonts/fa-solid-900.woff2)}.fas{--fa-style:900}.fa-classic,.fas{--fa-family:var(--fa-family-classic)}.fa-solid{--fa-style:900}
|
||||
556
static/css/svg-with-js.css
Normal file
556
static/css/svg-with-js.css
Normal file
@@ -0,0 +1,556 @@
|
||||
/*!
|
||||
* Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||
* Copyright 2025 Fonticons, Inc.
|
||||
*/
|
||||
:root, :host {
|
||||
--fa-font-solid: normal 900 1em/1 "Font Awesome 7 Free";
|
||||
--fa-font-regular: normal 400 1em/1 "Font Awesome 7 Free";
|
||||
--fa-font-light: normal 300 1em/1 "Font Awesome 7 Pro";
|
||||
--fa-font-thin: normal 100 1em/1 "Font Awesome 7 Pro";
|
||||
--fa-font-duotone: normal 900 1em/1 "Font Awesome 7 Duotone";
|
||||
--fa-font-duotone-regular: normal 400 1em/1 "Font Awesome 7 Duotone";
|
||||
--fa-font-duotone-light: normal 300 1em/1 "Font Awesome 7 Duotone";
|
||||
--fa-font-duotone-thin: normal 100 1em/1 "Font Awesome 7 Duotone";
|
||||
--fa-font-brands: normal 400 1em/1 "Font Awesome 7 Brands";
|
||||
--fa-font-sharp-solid: normal 900 1em/1 "Font Awesome 7 Sharp";
|
||||
--fa-font-sharp-regular: normal 400 1em/1 "Font Awesome 7 Sharp";
|
||||
--fa-font-sharp-light: normal 300 1em/1 "Font Awesome 7 Sharp";
|
||||
--fa-font-sharp-thin: normal 100 1em/1 "Font Awesome 7 Sharp";
|
||||
--fa-font-sharp-duotone-solid: normal 900 1em/1 "Font Awesome 7 Sharp Duotone";
|
||||
--fa-font-sharp-duotone-regular: normal 400 1em/1 "Font Awesome 7 Sharp Duotone";
|
||||
--fa-font-sharp-duotone-light: normal 300 1em/1 "Font Awesome 7 Sharp Duotone";
|
||||
--fa-font-sharp-duotone-thin: normal 100 1em/1 "Font Awesome 7 Sharp Duotone";
|
||||
--fa-font-slab-regular: normal 400 1em/1 "Font Awesome 7 Slab";
|
||||
--fa-font-slab-press-regular: normal 400 1em/1 "Font Awesome 7 Slab Press";
|
||||
--fa-font-whiteboard-semibold: normal 600 1em/1 "Font Awesome 7 Whiteboard";
|
||||
--fa-font-thumbprint-light: normal 300 1em/1 "Font Awesome 7 Thumbprint";
|
||||
--fa-font-notdog-solid: normal 900 1em/1 "Font Awesome 7 Notdog";
|
||||
--fa-font-notdog-duo-solid: normal 900 1em/1 "Font Awesome 7 Notdog Duo";
|
||||
--fa-font-etch-solid: normal 900 1em/1 "Font Awesome 7 Etch";
|
||||
--fa-font-jelly-regular: normal 400 1em/1 "Font Awesome 7 Jelly";
|
||||
--fa-font-jelly-fill-regular: normal 400 1em/1 "Font Awesome 7 Jelly Fill";
|
||||
--fa-font-jelly-duo-regular: normal 400 1em/1 "Font Awesome 7 Jelly Duo";
|
||||
--fa-font-chisel-regular: normal 400 1em/1 "Font Awesome 7 Chisel";
|
||||
--fa-font-utility-semibold: normal 600 1em/1 "Font Awesome 7 Utility";
|
||||
--fa-font-utility-duo-semibold: normal 600 1em/1 "Font Awesome 7 Utility Duo";
|
||||
--fa-font-utility-fill-semibold: normal 600 1em/1 "Font Awesome 7 Utility Fill";
|
||||
}
|
||||
|
||||
.svg-inline--fa {
|
||||
box-sizing: content-box;
|
||||
display: var(--fa-display, inline-block);
|
||||
height: 1em;
|
||||
overflow: visible;
|
||||
vertical-align: -0.125em;
|
||||
width: var(--fa-width, 1.25em);
|
||||
}
|
||||
.svg-inline--fa.fa-2xs {
|
||||
vertical-align: 0.1em;
|
||||
}
|
||||
.svg-inline--fa.fa-xs {
|
||||
vertical-align: 0em;
|
||||
}
|
||||
.svg-inline--fa.fa-sm {
|
||||
vertical-align: -0.0714285714em;
|
||||
}
|
||||
.svg-inline--fa.fa-lg {
|
||||
vertical-align: -0.2em;
|
||||
}
|
||||
.svg-inline--fa.fa-xl {
|
||||
vertical-align: -0.25em;
|
||||
}
|
||||
.svg-inline--fa.fa-2xl {
|
||||
vertical-align: -0.3125em;
|
||||
}
|
||||
.svg-inline--fa.fa-pull-left,
|
||||
.svg-inline--fa .fa-pull-start {
|
||||
float: inline-start;
|
||||
margin-inline-end: var(--fa-pull-margin, 0.3em);
|
||||
}
|
||||
.svg-inline--fa.fa-pull-right,
|
||||
.svg-inline--fa .fa-pull-end {
|
||||
float: inline-end;
|
||||
margin-inline-start: var(--fa-pull-margin, 0.3em);
|
||||
}
|
||||
.svg-inline--fa.fa-li {
|
||||
width: var(--fa-li-width, 2em);
|
||||
inset-inline-start: calc(-1 * var(--fa-li-width, 2em));
|
||||
inset-block-start: 0.25em; /* syncing vertical alignment with Web Font rendering */
|
||||
}
|
||||
|
||||
.fa-layers-counter, .fa-layers-text {
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.fa-layers {
|
||||
display: inline-block;
|
||||
height: 1em;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
vertical-align: -0.125em;
|
||||
width: var(--fa-width, 1.25em);
|
||||
}
|
||||
.fa-layers .svg-inline--fa {
|
||||
inset: 0;
|
||||
margin: auto;
|
||||
position: absolute;
|
||||
transform-origin: center center;
|
||||
}
|
||||
|
||||
.fa-layers-text {
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
transform-origin: center center;
|
||||
}
|
||||
|
||||
.fa-layers-counter {
|
||||
background-color: var(--fa-counter-background-color, #ff253a);
|
||||
border-radius: var(--fa-counter-border-radius, 1em);
|
||||
box-sizing: border-box;
|
||||
color: var(--fa-inverse, #fff);
|
||||
line-height: var(--fa-counter-line-height, 1);
|
||||
max-width: var(--fa-counter-max-width, 5em);
|
||||
min-width: var(--fa-counter-min-width, 1.5em);
|
||||
overflow: hidden;
|
||||
padding: var(--fa-counter-padding, 0.25em 0.5em);
|
||||
right: var(--fa-right, 0);
|
||||
text-overflow: ellipsis;
|
||||
top: var(--fa-top, 0);
|
||||
transform: scale(var(--fa-counter-scale, 0.25));
|
||||
transform-origin: top right;
|
||||
}
|
||||
|
||||
.fa-layers-bottom-right {
|
||||
bottom: var(--fa-bottom, 0);
|
||||
right: var(--fa-right, 0);
|
||||
top: auto;
|
||||
transform: scale(var(--fa-layers-scale, 0.25));
|
||||
transform-origin: bottom right;
|
||||
}
|
||||
|
||||
.fa-layers-bottom-left {
|
||||
bottom: var(--fa-bottom, 0);
|
||||
left: var(--fa-left, 0);
|
||||
right: auto;
|
||||
top: auto;
|
||||
transform: scale(var(--fa-layers-scale, 0.25));
|
||||
transform-origin: bottom left;
|
||||
}
|
||||
|
||||
.fa-layers-top-right {
|
||||
top: var(--fa-top, 0);
|
||||
right: var(--fa-right, 0);
|
||||
transform: scale(var(--fa-layers-scale, 0.25));
|
||||
transform-origin: top right;
|
||||
}
|
||||
|
||||
.fa-layers-top-left {
|
||||
left: var(--fa-left, 0);
|
||||
right: auto;
|
||||
top: var(--fa-top, 0);
|
||||
transform: scale(var(--fa-layers-scale, 0.25));
|
||||
transform-origin: top left;
|
||||
}
|
||||
|
||||
.fa-1x {
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
.fa-2x {
|
||||
font-size: 2em;
|
||||
}
|
||||
|
||||
.fa-3x {
|
||||
font-size: 3em;
|
||||
}
|
||||
|
||||
.fa-4x {
|
||||
font-size: 4em;
|
||||
}
|
||||
|
||||
.fa-5x {
|
||||
font-size: 5em;
|
||||
}
|
||||
|
||||
.fa-6x {
|
||||
font-size: 6em;
|
||||
}
|
||||
|
||||
.fa-7x {
|
||||
font-size: 7em;
|
||||
}
|
||||
|
||||
.fa-8x {
|
||||
font-size: 8em;
|
||||
}
|
||||
|
||||
.fa-9x {
|
||||
font-size: 9em;
|
||||
}
|
||||
|
||||
.fa-10x {
|
||||
font-size: 10em;
|
||||
}
|
||||
|
||||
.fa-2xs {
|
||||
font-size: calc(10 / 16 * 1em); /* converts a 10px size into an em-based value that's relative to the scale's 16px base */
|
||||
line-height: calc(1 / 10 * 1em); /* sets the line-height of the icon back to that of it's parent */
|
||||
vertical-align: calc((6 / 10 - 0.375) * 1em); /* vertically centers the icon taking into account the surrounding text's descender */
|
||||
}
|
||||
|
||||
.fa-xs {
|
||||
font-size: calc(12 / 16 * 1em); /* converts a 12px size into an em-based value that's relative to the scale's 16px base */
|
||||
line-height: calc(1 / 12 * 1em); /* sets the line-height of the icon back to that of it's parent */
|
||||
vertical-align: calc((6 / 12 - 0.375) * 1em); /* vertically centers the icon taking into account the surrounding text's descender */
|
||||
}
|
||||
|
||||
.fa-sm {
|
||||
font-size: calc(14 / 16 * 1em); /* converts a 14px size into an em-based value that's relative to the scale's 16px base */
|
||||
line-height: calc(1 / 14 * 1em); /* sets the line-height of the icon back to that of it's parent */
|
||||
vertical-align: calc((6 / 14 - 0.375) * 1em); /* vertically centers the icon taking into account the surrounding text's descender */
|
||||
}
|
||||
|
||||
.fa-lg {
|
||||
font-size: calc(20 / 16 * 1em); /* converts a 20px size into an em-based value that's relative to the scale's 16px base */
|
||||
line-height: calc(1 / 20 * 1em); /* sets the line-height of the icon back to that of it's parent */
|
||||
vertical-align: calc((6 / 20 - 0.375) * 1em); /* vertically centers the icon taking into account the surrounding text's descender */
|
||||
}
|
||||
|
||||
.fa-xl {
|
||||
font-size: calc(24 / 16 * 1em); /* converts a 24px size into an em-based value that's relative to the scale's 16px base */
|
||||
line-height: calc(1 / 24 * 1em); /* sets the line-height of the icon back to that of it's parent */
|
||||
vertical-align: calc((6 / 24 - 0.375) * 1em); /* vertically centers the icon taking into account the surrounding text's descender */
|
||||
}
|
||||
|
||||
.fa-2xl {
|
||||
font-size: calc(32 / 16 * 1em); /* converts a 32px size into an em-based value that's relative to the scale's 16px base */
|
||||
line-height: calc(1 / 32 * 1em); /* sets the line-height of the icon back to that of it's parent */
|
||||
vertical-align: calc((6 / 32 - 0.375) * 1em); /* vertically centers the icon taking into account the surrounding text's descender */
|
||||
}
|
||||
|
||||
.fa-width-auto {
|
||||
--fa-width: auto;
|
||||
}
|
||||
|
||||
.fa-fw,
|
||||
.fa-width-fixed {
|
||||
--fa-width: 1.25em;
|
||||
}
|
||||
|
||||
.fa-ul {
|
||||
list-style-type: none;
|
||||
margin-inline-start: var(--fa-li-margin, 2.5em);
|
||||
padding-inline-start: 0;
|
||||
}
|
||||
.fa-ul > li {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.fa-li {
|
||||
inset-inline-start: calc(-1 * var(--fa-li-width, 2em));
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
width: var(--fa-li-width, 2em);
|
||||
line-height: inherit;
|
||||
}
|
||||
|
||||
/* Heads Up: Bordered Icons will not be supported in the future!
|
||||
- This feature will be deprecated in the next major release of Font Awesome (v8)!
|
||||
- You may continue to use it in this version *v7), but it will not be supported in Font Awesome v8.
|
||||
*/
|
||||
/* Notes:
|
||||
* --@{v.$css-prefix}-border-width = 1/16 by default (to render as ~1px based on a 16px default font-size)
|
||||
* --@{v.$css-prefix}-border-padding =
|
||||
** 3/16 for vertical padding (to give ~2px of vertical whitespace around an icon considering it's vertical alignment)
|
||||
** 4/16 for horizontal padding (to give ~4px of horizontal whitespace around an icon)
|
||||
*/
|
||||
.fa-border {
|
||||
border-color: var(--fa-border-color, #eee);
|
||||
border-radius: var(--fa-border-radius, 0.1em);
|
||||
border-style: var(--fa-border-style, solid);
|
||||
border-width: var(--fa-border-width, 0.0625em);
|
||||
box-sizing: var(--fa-border-box-sizing, content-box);
|
||||
padding: var(--fa-border-padding, 0.1875em 0.25em);
|
||||
}
|
||||
|
||||
.fa-pull-left,
|
||||
.fa-pull-start {
|
||||
float: inline-start;
|
||||
margin-inline-end: var(--fa-pull-margin, 0.3em);
|
||||
}
|
||||
|
||||
.fa-pull-right,
|
||||
.fa-pull-end {
|
||||
float: inline-end;
|
||||
margin-inline-start: var(--fa-pull-margin, 0.3em);
|
||||
}
|
||||
|
||||
.fa-beat {
|
||||
animation-name: fa-beat;
|
||||
animation-delay: var(--fa-animation-delay, 0s);
|
||||
animation-direction: var(--fa-animation-direction, normal);
|
||||
animation-duration: var(--fa-animation-duration, 1s);
|
||||
animation-iteration-count: var(--fa-animation-iteration-count, infinite);
|
||||
animation-timing-function: var(--fa-animation-timing, ease-in-out);
|
||||
}
|
||||
|
||||
.fa-bounce {
|
||||
animation-name: fa-bounce;
|
||||
animation-delay: var(--fa-animation-delay, 0s);
|
||||
animation-direction: var(--fa-animation-direction, normal);
|
||||
animation-duration: var(--fa-animation-duration, 1s);
|
||||
animation-iteration-count: var(--fa-animation-iteration-count, infinite);
|
||||
animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.28, 0.84, 0.42, 1));
|
||||
}
|
||||
|
||||
.fa-fade {
|
||||
animation-name: fa-fade;
|
||||
animation-delay: var(--fa-animation-delay, 0s);
|
||||
animation-direction: var(--fa-animation-direction, normal);
|
||||
animation-duration: var(--fa-animation-duration, 1s);
|
||||
animation-iteration-count: var(--fa-animation-iteration-count, infinite);
|
||||
animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.4, 0, 0.6, 1));
|
||||
}
|
||||
|
||||
.fa-beat-fade {
|
||||
animation-name: fa-beat-fade;
|
||||
animation-delay: var(--fa-animation-delay, 0s);
|
||||
animation-direction: var(--fa-animation-direction, normal);
|
||||
animation-duration: var(--fa-animation-duration, 1s);
|
||||
animation-iteration-count: var(--fa-animation-iteration-count, infinite);
|
||||
animation-timing-function: var(--fa-animation-timing, cubic-bezier(0.4, 0, 0.6, 1));
|
||||
}
|
||||
|
||||
.fa-flip {
|
||||
animation-name: fa-flip;
|
||||
animation-delay: var(--fa-animation-delay, 0s);
|
||||
animation-direction: var(--fa-animation-direction, normal);
|
||||
animation-duration: var(--fa-animation-duration, 1s);
|
||||
animation-iteration-count: var(--fa-animation-iteration-count, infinite);
|
||||
animation-timing-function: var(--fa-animation-timing, ease-in-out);
|
||||
}
|
||||
|
||||
.fa-shake {
|
||||
animation-name: fa-shake;
|
||||
animation-delay: var(--fa-animation-delay, 0s);
|
||||
animation-direction: var(--fa-animation-direction, normal);
|
||||
animation-duration: var(--fa-animation-duration, 1s);
|
||||
animation-iteration-count: var(--fa-animation-iteration-count, infinite);
|
||||
animation-timing-function: var(--fa-animation-timing, linear);
|
||||
}
|
||||
|
||||
.fa-spin {
|
||||
animation-name: fa-spin;
|
||||
animation-delay: var(--fa-animation-delay, 0s);
|
||||
animation-direction: var(--fa-animation-direction, normal);
|
||||
animation-duration: var(--fa-animation-duration, 2s);
|
||||
animation-iteration-count: var(--fa-animation-iteration-count, infinite);
|
||||
animation-timing-function: var(--fa-animation-timing, linear);
|
||||
}
|
||||
|
||||
.fa-spin-reverse {
|
||||
--fa-animation-direction: reverse;
|
||||
}
|
||||
|
||||
.fa-pulse,
|
||||
.fa-spin-pulse {
|
||||
animation-name: fa-spin;
|
||||
animation-direction: var(--fa-animation-direction, normal);
|
||||
animation-duration: var(--fa-animation-duration, 1s);
|
||||
animation-iteration-count: var(--fa-animation-iteration-count, infinite);
|
||||
animation-timing-function: var(--fa-animation-timing, steps(8));
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.fa-beat,
|
||||
.fa-bounce,
|
||||
.fa-fade,
|
||||
.fa-beat-fade,
|
||||
.fa-flip,
|
||||
.fa-pulse,
|
||||
.fa-shake,
|
||||
.fa-spin,
|
||||
.fa-spin-pulse {
|
||||
animation: none !important;
|
||||
transition: none !important;
|
||||
}
|
||||
}
|
||||
@keyframes fa-beat {
|
||||
0%, 90% {
|
||||
transform: scale(1);
|
||||
}
|
||||
45% {
|
||||
transform: scale(var(--fa-beat-scale, 1.25));
|
||||
}
|
||||
}
|
||||
@keyframes fa-bounce {
|
||||
0% {
|
||||
transform: scale(1, 1) translateY(0);
|
||||
}
|
||||
10% {
|
||||
transform: scale(var(--fa-bounce-start-scale-x, 1.1), var(--fa-bounce-start-scale-y, 0.9)) translateY(0);
|
||||
}
|
||||
30% {
|
||||
transform: scale(var(--fa-bounce-jump-scale-x, 0.9), var(--fa-bounce-jump-scale-y, 1.1)) translateY(var(--fa-bounce-height, -0.5em));
|
||||
}
|
||||
50% {
|
||||
transform: scale(var(--fa-bounce-land-scale-x, 1.05), var(--fa-bounce-land-scale-y, 0.95)) translateY(0);
|
||||
}
|
||||
57% {
|
||||
transform: scale(1, 1) translateY(var(--fa-bounce-rebound, -0.125em));
|
||||
}
|
||||
64% {
|
||||
transform: scale(1, 1) translateY(0);
|
||||
}
|
||||
100% {
|
||||
transform: scale(1, 1) translateY(0);
|
||||
}
|
||||
}
|
||||
@keyframes fa-fade {
|
||||
50% {
|
||||
opacity: var(--fa-fade-opacity, 0.4);
|
||||
}
|
||||
}
|
||||
@keyframes fa-beat-fade {
|
||||
0%, 100% {
|
||||
opacity: var(--fa-beat-fade-opacity, 0.4);
|
||||
transform: scale(1);
|
||||
}
|
||||
50% {
|
||||
opacity: 1;
|
||||
transform: scale(var(--fa-beat-fade-scale, 1.125));
|
||||
}
|
||||
}
|
||||
@keyframes fa-flip {
|
||||
50% {
|
||||
transform: rotate3d(var(--fa-flip-x, 0), var(--fa-flip-y, 1), var(--fa-flip-z, 0), var(--fa-flip-angle, -180deg));
|
||||
}
|
||||
}
|
||||
@keyframes fa-shake {
|
||||
0% {
|
||||
transform: rotate(-15deg);
|
||||
}
|
||||
4% {
|
||||
transform: rotate(15deg);
|
||||
}
|
||||
8%, 24% {
|
||||
transform: rotate(-18deg);
|
||||
}
|
||||
12%, 28% {
|
||||
transform: rotate(18deg);
|
||||
}
|
||||
16% {
|
||||
transform: rotate(-22deg);
|
||||
}
|
||||
20% {
|
||||
transform: rotate(22deg);
|
||||
}
|
||||
32% {
|
||||
transform: rotate(-12deg);
|
||||
}
|
||||
36% {
|
||||
transform: rotate(12deg);
|
||||
}
|
||||
40%, 100% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
}
|
||||
@keyframes fa-spin {
|
||||
0% {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
100% {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
.fa-rotate-90 {
|
||||
transform: rotate(90deg);
|
||||
}
|
||||
|
||||
.fa-rotate-180 {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
.fa-rotate-270 {
|
||||
transform: rotate(270deg);
|
||||
}
|
||||
|
||||
.fa-flip-horizontal {
|
||||
transform: scale(-1, 1);
|
||||
}
|
||||
|
||||
.fa-flip-vertical {
|
||||
transform: scale(1, -1);
|
||||
}
|
||||
|
||||
.fa-flip-both,
|
||||
.fa-flip-horizontal.fa-flip-vertical {
|
||||
transform: scale(-1, -1);
|
||||
}
|
||||
|
||||
.fa-rotate-by {
|
||||
transform: rotate(var(--fa-rotate-angle, 0));
|
||||
}
|
||||
|
||||
.svg-inline--fa .fa-primary {
|
||||
fill: var(--fa-primary-color, currentColor);
|
||||
opacity: var(--fa-primary-opacity, 1);
|
||||
}
|
||||
|
||||
.svg-inline--fa .fa-secondary {
|
||||
fill: var(--fa-secondary-color, currentColor);
|
||||
opacity: var(--fa-secondary-opacity, 0.4);
|
||||
}
|
||||
|
||||
.svg-inline--fa.fa-swap-opacity .fa-primary {
|
||||
opacity: var(--fa-secondary-opacity, 0.4);
|
||||
}
|
||||
|
||||
.svg-inline--fa.fa-swap-opacity .fa-secondary {
|
||||
opacity: var(--fa-primary-opacity, 1);
|
||||
}
|
||||
|
||||
.svg-inline--fa mask .fa-primary,
|
||||
.svg-inline--fa mask .fa-secondary {
|
||||
fill: black;
|
||||
}
|
||||
|
||||
.svg-inline--fa.fa-inverse {
|
||||
fill: var(--fa-inverse, #fff);
|
||||
}
|
||||
|
||||
.fa-stack {
|
||||
display: inline-block;
|
||||
height: 2em;
|
||||
line-height: 2em;
|
||||
position: relative;
|
||||
vertical-align: middle;
|
||||
width: 2.5em;
|
||||
}
|
||||
|
||||
.fa-inverse {
|
||||
color: var(--fa-inverse, #fff);
|
||||
}
|
||||
|
||||
.svg-inline--fa.fa-stack-1x {
|
||||
--fa-width: 1.25em;
|
||||
height: 1em;
|
||||
width: var(--fa-width);
|
||||
}
|
||||
.svg-inline--fa.fa-stack-2x {
|
||||
--fa-width: 2.5em;
|
||||
height: 2em;
|
||||
width: var(--fa-width);
|
||||
}
|
||||
|
||||
.fa-stack-1x,
|
||||
.fa-stack-2x {
|
||||
inset: 0;
|
||||
margin: auto;
|
||||
position: absolute;
|
||||
z-index: var(--fa-stack-z-index, auto);
|
||||
}
|
||||
6
static/css/svg-with-js.min.css
vendored
Normal file
6
static/css/svg-with-js.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
182
static/css/svg.css
Normal file
182
static/css/svg.css
Normal file
@@ -0,0 +1,182 @@
|
||||
/*!
|
||||
* Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||
* Copyright 2025 Fonticons, Inc.
|
||||
*/
|
||||
:root, :host {
|
||||
--fa-font-solid: normal 900 1em/1 "Font Awesome 7 Free";
|
||||
--fa-font-regular: normal 400 1em/1 "Font Awesome 7 Free";
|
||||
--fa-font-light: normal 300 1em/1 "Font Awesome 7 Pro";
|
||||
--fa-font-thin: normal 100 1em/1 "Font Awesome 7 Pro";
|
||||
--fa-font-duotone: normal 900 1em/1 "Font Awesome 7 Duotone";
|
||||
--fa-font-duotone-regular: normal 400 1em/1 "Font Awesome 7 Duotone";
|
||||
--fa-font-duotone-light: normal 300 1em/1 "Font Awesome 7 Duotone";
|
||||
--fa-font-duotone-thin: normal 100 1em/1 "Font Awesome 7 Duotone";
|
||||
--fa-font-brands: normal 400 1em/1 "Font Awesome 7 Brands";
|
||||
--fa-font-sharp-solid: normal 900 1em/1 "Font Awesome 7 Sharp";
|
||||
--fa-font-sharp-regular: normal 400 1em/1 "Font Awesome 7 Sharp";
|
||||
--fa-font-sharp-light: normal 300 1em/1 "Font Awesome 7 Sharp";
|
||||
--fa-font-sharp-thin: normal 100 1em/1 "Font Awesome 7 Sharp";
|
||||
--fa-font-sharp-duotone-solid: normal 900 1em/1 "Font Awesome 7 Sharp Duotone";
|
||||
--fa-font-sharp-duotone-regular: normal 400 1em/1 "Font Awesome 7 Sharp Duotone";
|
||||
--fa-font-sharp-duotone-light: normal 300 1em/1 "Font Awesome 7 Sharp Duotone";
|
||||
--fa-font-sharp-duotone-thin: normal 100 1em/1 "Font Awesome 7 Sharp Duotone";
|
||||
--fa-font-slab-regular: normal 400 1em/1 "Font Awesome 7 Slab";
|
||||
--fa-font-slab-press-regular: normal 400 1em/1 "Font Awesome 7 Slab Press";
|
||||
--fa-font-whiteboard-semibold: normal 600 1em/1 "Font Awesome 7 Whiteboard";
|
||||
--fa-font-thumbprint-light: normal 300 1em/1 "Font Awesome 7 Thumbprint";
|
||||
--fa-font-notdog-solid: normal 900 1em/1 "Font Awesome 7 Notdog";
|
||||
--fa-font-notdog-duo-solid: normal 900 1em/1 "Font Awesome 7 Notdog Duo";
|
||||
--fa-font-etch-solid: normal 900 1em/1 "Font Awesome 7 Etch";
|
||||
--fa-font-jelly-regular: normal 400 1em/1 "Font Awesome 7 Jelly";
|
||||
--fa-font-jelly-fill-regular: normal 400 1em/1 "Font Awesome 7 Jelly Fill";
|
||||
--fa-font-jelly-duo-regular: normal 400 1em/1 "Font Awesome 7 Jelly Duo";
|
||||
--fa-font-chisel-regular: normal 400 1em/1 "Font Awesome 7 Chisel";
|
||||
--fa-font-utility-semibold: normal 600 1em/1 "Font Awesome 7 Utility";
|
||||
--fa-font-utility-duo-semibold: normal 600 1em/1 "Font Awesome 7 Utility Duo";
|
||||
--fa-font-utility-fill-semibold: normal 600 1em/1 "Font Awesome 7 Utility Fill";
|
||||
}
|
||||
|
||||
.svg-inline--fa {
|
||||
box-sizing: content-box;
|
||||
display: var(--fa-display, inline-block);
|
||||
height: 1em;
|
||||
overflow: visible;
|
||||
vertical-align: -0.125em;
|
||||
width: var(--fa-width, 1.25em);
|
||||
}
|
||||
.svg-inline--fa.fa-2xs {
|
||||
vertical-align: 0.1em;
|
||||
}
|
||||
.svg-inline--fa.fa-xs {
|
||||
vertical-align: 0em;
|
||||
}
|
||||
.svg-inline--fa.fa-sm {
|
||||
vertical-align: -0.0714285714em;
|
||||
}
|
||||
.svg-inline--fa.fa-lg {
|
||||
vertical-align: -0.2em;
|
||||
}
|
||||
.svg-inline--fa.fa-xl {
|
||||
vertical-align: -0.25em;
|
||||
}
|
||||
.svg-inline--fa.fa-2xl {
|
||||
vertical-align: -0.3125em;
|
||||
}
|
||||
.svg-inline--fa.fa-li {
|
||||
width: var(--fa-li-width, 2em);
|
||||
inset-inline-start: calc(-1 * var(--fa-li-width, 2em));
|
||||
inset-block-start: 0.25em; /* syncing vertical alignment with Web Font rendering */
|
||||
}
|
||||
|
||||
.fa-layers-counter, .fa-layers-text {
|
||||
display: inline-block;
|
||||
position: absolute;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.fa-layers {
|
||||
display: inline-block;
|
||||
height: 1em;
|
||||
position: relative;
|
||||
text-align: center;
|
||||
vertical-align: -0.125em;
|
||||
width: var(--fa-width, 1.25em);
|
||||
}
|
||||
.fa-layers .svg-inline--fa {
|
||||
inset: 0;
|
||||
margin: auto;
|
||||
position: absolute;
|
||||
transform-origin: center center;
|
||||
}
|
||||
|
||||
.fa-layers-text {
|
||||
left: 50%;
|
||||
top: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
transform-origin: center center;
|
||||
}
|
||||
|
||||
.fa-layers-counter {
|
||||
background-color: var(--fa-counter-background-color, #ff253a);
|
||||
border-radius: var(--fa-counter-border-radius, 1em);
|
||||
box-sizing: border-box;
|
||||
color: var(--fa-inverse, #fff);
|
||||
line-height: var(--fa-counter-line-height, 1);
|
||||
max-width: var(--fa-counter-max-width, 5em);
|
||||
min-width: var(--fa-counter-min-width, 1.5em);
|
||||
overflow: hidden;
|
||||
padding: var(--fa-counter-padding, 0.25em 0.5em);
|
||||
right: var(--fa-right, 0);
|
||||
text-overflow: ellipsis;
|
||||
top: var(--fa-top, 0);
|
||||
transform: scale(var(--fa-counter-scale, 0.25));
|
||||
transform-origin: top right;
|
||||
}
|
||||
|
||||
.fa-layers-bottom-right {
|
||||
bottom: var(--fa-bottom, 0);
|
||||
right: var(--fa-right, 0);
|
||||
top: auto;
|
||||
transform: scale(var(--fa-layers-scale, 0.25));
|
||||
transform-origin: bottom right;
|
||||
}
|
||||
|
||||
.fa-layers-bottom-left {
|
||||
bottom: var(--fa-bottom, 0);
|
||||
left: var(--fa-left, 0);
|
||||
right: auto;
|
||||
top: auto;
|
||||
transform: scale(var(--fa-layers-scale, 0.25));
|
||||
transform-origin: bottom left;
|
||||
}
|
||||
|
||||
.fa-layers-top-right {
|
||||
top: var(--fa-top, 0);
|
||||
right: var(--fa-right, 0);
|
||||
transform: scale(var(--fa-layers-scale, 0.25));
|
||||
transform-origin: top right;
|
||||
}
|
||||
|
||||
.fa-layers-top-left {
|
||||
left: var(--fa-left, 0);
|
||||
right: auto;
|
||||
top: var(--fa-top, 0);
|
||||
transform: scale(var(--fa-layers-scale, 0.25));
|
||||
transform-origin: top left;
|
||||
}
|
||||
|
||||
.svg-inline--fa .fa-primary {
|
||||
fill: var(--fa-primary-color, currentColor);
|
||||
opacity: var(--fa-primary-opacity, 1);
|
||||
}
|
||||
|
||||
.svg-inline--fa .fa-secondary {
|
||||
fill: var(--fa-secondary-color, currentColor);
|
||||
opacity: var(--fa-secondary-opacity, 0.4);
|
||||
}
|
||||
|
||||
.svg-inline--fa.fa-swap-opacity .fa-primary {
|
||||
opacity: var(--fa-secondary-opacity, 0.4);
|
||||
}
|
||||
|
||||
.svg-inline--fa.fa-swap-opacity .fa-secondary {
|
||||
opacity: var(--fa-primary-opacity, 1);
|
||||
}
|
||||
|
||||
.svg-inline--fa mask .fa-primary,
|
||||
.svg-inline--fa mask .fa-secondary {
|
||||
fill: black;
|
||||
}
|
||||
|
||||
.svg-inline--fa.fa-inverse {
|
||||
fill: var(--fa-inverse, #fff);
|
||||
}
|
||||
|
||||
.fa-stack-1x,
|
||||
.fa-stack-2x {
|
||||
inset: 0;
|
||||
margin: auto;
|
||||
position: absolute;
|
||||
z-index: var(--fa-stack-z-index, auto);
|
||||
}
|
||||
6
static/css/svg.min.css
vendored
Normal file
6
static/css/svg.min.css
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
/*!
|
||||
* Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||
* Copyright 2025 Fonticons, Inc.
|
||||
*/
|
||||
:host,:root{--fa-font-solid:normal 900 1em/1 "Font Awesome 7 Free";--fa-font-regular:normal 400 1em/1 "Font Awesome 7 Free";--fa-font-light:normal 300 1em/1 "Font Awesome 7 Pro";--fa-font-thin:normal 100 1em/1 "Font Awesome 7 Pro";--fa-font-duotone:normal 900 1em/1 "Font Awesome 7 Duotone";--fa-font-duotone-regular:normal 400 1em/1 "Font Awesome 7 Duotone";--fa-font-duotone-light:normal 300 1em/1 "Font Awesome 7 Duotone";--fa-font-duotone-thin:normal 100 1em/1 "Font Awesome 7 Duotone";--fa-font-brands:normal 400 1em/1 "Font Awesome 7 Brands";--fa-font-sharp-solid:normal 900 1em/1 "Font Awesome 7 Sharp";--fa-font-sharp-regular:normal 400 1em/1 "Font Awesome 7 Sharp";--fa-font-sharp-light:normal 300 1em/1 "Font Awesome 7 Sharp";--fa-font-sharp-thin:normal 100 1em/1 "Font Awesome 7 Sharp";--fa-font-sharp-duotone-solid:normal 900 1em/1 "Font Awesome 7 Sharp Duotone";--fa-font-sharp-duotone-regular:normal 400 1em/1 "Font Awesome 7 Sharp Duotone";--fa-font-sharp-duotone-light:normal 300 1em/1 "Font Awesome 7 Sharp Duotone";--fa-font-sharp-duotone-thin:normal 100 1em/1 "Font Awesome 7 Sharp Duotone";--fa-font-slab-regular:normal 400 1em/1 "Font Awesome 7 Slab";--fa-font-slab-press-regular:normal 400 1em/1 "Font Awesome 7 Slab Press";--fa-font-whiteboard-semibold:normal 600 1em/1 "Font Awesome 7 Whiteboard";--fa-font-thumbprint-light:normal 300 1em/1 "Font Awesome 7 Thumbprint";--fa-font-notdog-solid:normal 900 1em/1 "Font Awesome 7 Notdog";--fa-font-notdog-duo-solid:normal 900 1em/1 "Font Awesome 7 Notdog Duo";--fa-font-etch-solid:normal 900 1em/1 "Font Awesome 7 Etch";--fa-font-jelly-regular:normal 400 1em/1 "Font Awesome 7 Jelly";--fa-font-jelly-fill-regular:normal 400 1em/1 "Font Awesome 7 Jelly Fill";--fa-font-jelly-duo-regular:normal 400 1em/1 "Font Awesome 7 Jelly Duo";--fa-font-chisel-regular:normal 400 1em/1 "Font Awesome 7 Chisel";--fa-font-utility-semibold:normal 600 1em/1 "Font Awesome 7 Utility";--fa-font-utility-duo-semibold:normal 600 1em/1 "Font Awesome 7 Utility Duo";--fa-font-utility-fill-semibold:normal 600 1em/1 "Font Awesome 7 Utility Fill"}.svg-inline--fa{box-sizing:content-box;display:var(--fa-display,inline-block);height:1em;overflow:visible;vertical-align:-.125em;width:var(--fa-width,1.25em)}.svg-inline--fa.fa-2xs{vertical-align:.1em}.svg-inline--fa.fa-xs{vertical-align:0}.svg-inline--fa.fa-sm{vertical-align:-.0714285714em}.svg-inline--fa.fa-lg{vertical-align:-.2em}.svg-inline--fa.fa-xl{vertical-align:-.25em}.svg-inline--fa.fa-2xl{vertical-align:-.3125em}.svg-inline--fa.fa-li{width:var(--fa-li-width,2em);inset-inline-start:calc(var(--fa-li-width, 2em)*-1);inset-block-start:.25em}.fa-layers-counter,.fa-layers-text{display:inline-block;position:absolute;text-align:center}.fa-layers{display:inline-block;height:1em;position:relative;text-align:center;vertical-align:-.125em;width:var(--fa-width,1.25em)}.fa-layers .svg-inline--fa{inset:0;margin:auto;position:absolute;transform-origin:center center}.fa-layers-text{left:50%;top:50%;transform:translate(-50%,-50%);transform-origin:center center}.fa-layers-counter{background-color:var(--fa-counter-background-color,#ff253a);border-radius:var(--fa-counter-border-radius,1em);box-sizing:border-box;color:var(--fa-inverse,#fff);line-height:var(--fa-counter-line-height,1);max-width:var(--fa-counter-max-width,5em);min-width:var(--fa-counter-min-width,1.5em);overflow:hidden;padding:var(--fa-counter-padding,.25em .5em);right:var(--fa-right,0);text-overflow:ellipsis;top:var(--fa-top,0);transform:scale(var(--fa-counter-scale,.25));transform-origin:top right}.fa-layers-bottom-right{bottom:var(--fa-bottom,0);right:var(--fa-right,0);top:auto;transform:scale(var(--fa-layers-scale,.25));transform-origin:bottom right}.fa-layers-bottom-left{bottom:var(--fa-bottom,0);left:var(--fa-left,0);right:auto;top:auto;transform:scale(var(--fa-layers-scale,.25));transform-origin:bottom left}.fa-layers-top-right{top:var(--fa-top,0);right:var(--fa-right,0);transform:scale(var(--fa-layers-scale,.25));transform-origin:top right}.fa-layers-top-left{left:var(--fa-left,0);right:auto;top:var(--fa-top,0);transform:scale(var(--fa-layers-scale,.25));transform-origin:top left}.svg-inline--fa .fa-primary{fill:var(--fa-primary-color,currentColor);opacity:var(--fa-primary-opacity,1)}.svg-inline--fa .fa-secondary{fill:var(--fa-secondary-color,currentColor)}.svg-inline--fa .fa-secondary,.svg-inline--fa.fa-swap-opacity .fa-primary{opacity:var(--fa-secondary-opacity,.4)}.svg-inline--fa.fa-swap-opacity .fa-secondary{opacity:var(--fa-primary-opacity,1)}.svg-inline--fa mask .fa-primary,.svg-inline--fa mask .fa-secondary{fill:#000}.svg-inline--fa.fa-inverse{fill:var(--fa-inverse,#fff)}.fa-stack-1x,.fa-stack-2x{inset:0;margin:auto;position:absolute;z-index:var(--fa-stack-z-index,auto)}
|
||||
27
static/css/v4-font-face.css
Normal file
27
static/css/v4-font-face.css
Normal file
@@ -0,0 +1,27 @@
|
||||
/*!
|
||||
* Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||
* Copyright 2025 Fonticons, Inc.
|
||||
*/
|
||||
@font-face {
|
||||
font-family: "FontAwesome";
|
||||
font-display: block;
|
||||
src: url("../webfonts/fa-solid-900.woff2") format("woff2");
|
||||
}
|
||||
@font-face {
|
||||
font-family: "FontAwesome";
|
||||
font-display: block;
|
||||
src: url("../webfonts/fa-brands-400.woff2") format("woff2");
|
||||
}
|
||||
@font-face {
|
||||
font-family: "FontAwesome";
|
||||
font-display: block;
|
||||
src: url("../webfonts/fa-regular-400.woff2") format("woff2");
|
||||
unicode-range: U+F003, U+F006, U+F014, U+F016-F017, U+F01A-F01B, U+F01D, U+F022, U+F03E, U+F044, U+F046, U+F05C-F05D, U+F06E, U+F070, U+F087-F088, U+F08A, U+F094, U+F096-F097, U+F09D, U+F0A0, U+F0A2, U+F0A4-F0A7, U+F0C5, U+F0C7, U+F0E5-F0E6, U+F0EB, U+F0F6-F0F8, U+F10C, U+F114-F115, U+F118-F11A, U+F11C-F11D, U+F133, U+F147, U+F14E, U+F150-F152, U+F185-F186, U+F18E, U+F190-F192, U+F196, U+F1C1-F1C9, U+F1D9, U+F1DB, U+F1E3, U+F1EA, U+F1F7, U+F1F9, U+F20A, U+F247-F248, U+F24A, U+F24D, U+F255-F25B, U+F25D, U+F271-F274, U+F278, U+F27B, U+F28C, U+F28E, U+F29C, U+F2B5, U+F2B7, U+F2BA, U+F2BC, U+F2BE, U+F2C0-F2C1, U+F2C3, U+F2D0, U+F2D2, U+F2D4, U+F2DC;
|
||||
}
|
||||
@font-face {
|
||||
font-family: "FontAwesome";
|
||||
font-display: block;
|
||||
src: url("../webfonts/fa-v4compatibility.woff2") format("woff2");
|
||||
unicode-range: U+F041, U+F047, U+F065-F066, U+F07D-F07E, U+F080, U+F08B, U+F08E, U+F090, U+F09A, U+F0AC, U+F0AE, U+F0B2, U+F0D0, U+F0D6, U+F0E4, U+F0EC, U+F10A-F10B, U+F123, U+F13E, U+F148-F149, U+F14C, U+F156, U+F15E, U+F160-F161, U+F163, U+F175-F178, U+F195, U+F1F8, U+F219, U+F27A;
|
||||
}
|
||||
6
static/css/v4-font-face.min.css
vendored
Normal file
6
static/css/v4-font-face.min.css
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
/*!
|
||||
* Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||
* Copyright 2025 Fonticons, Inc.
|
||||
*/
|
||||
@font-face{font-family:"FontAwesome";font-display:block;src:url(../webfonts/fa-solid-900.woff2) format("woff2")}@font-face{font-family:"FontAwesome";font-display:block;src:url(../webfonts/fa-brands-400.woff2) format("woff2")}@font-face{font-family:"FontAwesome";font-display:block;src:url(../webfonts/fa-regular-400.woff2) format("woff2");unicode-range:u+f003,u+f006,u+f014,u+f016-f017,u+f01a-f01b,u+f01d,u+f022,u+f03e,u+f044,u+f046,u+f05c-f05d,u+f06e,u+f070,u+f087-f088,u+f08a,u+f094,u+f096-f097,u+f09d,u+f0a0,u+f0a2,u+f0a4-f0a7,u+f0c5,u+f0c7,u+f0e5-f0e6,u+f0eb,u+f0f6-f0f8,u+f10c,u+f114-f115,u+f118-f11a,u+f11c-f11d,u+f133,u+f147,u+f14e,u+f150-f152,u+f185-f186,u+f18e,u+f190-f192,u+f196,u+f1c1-f1c9,u+f1d9,u+f1db,u+f1e3,u+f1ea,u+f1f7,u+f1f9,u+f20a,u+f247-f248,u+f24a,u+f24d,u+f255-f25b,u+f25d,u+f271-f274,u+f278,u+f27b,u+f28c,u+f28e,u+f29c,u+f2b5,u+f2b7,u+f2ba,u+f2bc,u+f2be,u+f2c0-f2c1,u+f2c3,u+f2d0,u+f2d2,u+f2d4,u+f2dc}@font-face{font-family:"FontAwesome";font-display:block;src:url(../webfonts/fa-v4compatibility.woff2) format("woff2");unicode-range:u+f041,u+f047,u+f065-f066,u+f07d-f07e,u+f080,u+f08b,u+f08e,u+f090,u+f09a,u+f0ac,u+f0ae,u+f0b2,u+f0d0,u+f0d6,u+f0e4,u+f0ec,u+f10a-f10b,u+f123,u+f13e,u+f148-f149,u+f14c,u+f156,u+f15e,u+f160-f161,u+f163,u+f175-f178,u+f195,u+f1f8,u+f219,u+f27a}
|
||||
2818
static/css/v4-shims.css
Normal file
2818
static/css/v4-shims.css
Normal file
File diff suppressed because it is too large
Load Diff
6
static/css/v4-shims.min.css
vendored
Normal file
6
static/css/v4-shims.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
23
static/css/v5-font-face.css
Normal file
23
static/css/v5-font-face.css
Normal file
@@ -0,0 +1,23 @@
|
||||
/*!
|
||||
* Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||
* Copyright 2025 Fonticons, Inc.
|
||||
*/
|
||||
@font-face {
|
||||
font-family: "Font Awesome 5 Brands";
|
||||
font-display: block;
|
||||
font-weight: 400;
|
||||
src: url("../webfonts/fa-brands-400.woff2") format("woff2");
|
||||
}
|
||||
@font-face {
|
||||
font-family: "Font Awesome 5 Free";
|
||||
font-display: block;
|
||||
font-weight: 900;
|
||||
src: url("../webfonts/fa-solid-900.woff2") format("woff2");
|
||||
}
|
||||
@font-face {
|
||||
font-family: "Font Awesome 5 Free";
|
||||
font-display: block;
|
||||
font-weight: 400;
|
||||
src: url("../webfonts/fa-regular-400.woff2") format("woff2");
|
||||
}
|
||||
6
static/css/v5-font-face.min.css
vendored
Normal file
6
static/css/v5-font-face.min.css
vendored
Normal file
@@ -0,0 +1,6 @@
|
||||
/*!
|
||||
* Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com
|
||||
* License - https://fontawesome.com/license/free (Icons: CC BY 4.0, Fonts: SIL OFL 1.1, Code: MIT License)
|
||||
* Copyright 2025 Fonticons, Inc.
|
||||
*/
|
||||
@font-face{font-family:"Font Awesome 5 Brands";font-display:block;font-weight:400;src:url(../webfonts/fa-brands-400.woff2) format("woff2")}@font-face{font-family:"Font Awesome 5 Free";font-display:block;font-weight:900;src:url(../webfonts/fa-solid-900.woff2) format("woff2")}@font-face{font-family:"Font Awesome 5 Free";font-display:block;font-weight:400;src:url(../webfonts/fa-regular-400.woff2) format("woff2")}
|
||||
BIN
static/teamforge/logo.png
Normal file
BIN
static/teamforge/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 413 KiB |
BIN
static/webfonts/fa-brands-400.woff2
Normal file
BIN
static/webfonts/fa-brands-400.woff2
Normal file
Binary file not shown.
BIN
static/webfonts/fa-regular-400.woff2
Normal file
BIN
static/webfonts/fa-regular-400.woff2
Normal file
Binary file not shown.
BIN
static/webfonts/fa-solid-900.woff2
Normal file
BIN
static/webfonts/fa-solid-900.woff2
Normal file
Binary file not shown.
BIN
static/webfonts/fa-v4compatibility.woff2
Normal file
BIN
static/webfonts/fa-v4compatibility.woff2
Normal file
Binary file not shown.
35
templates/backend/base.html
Normal file
35
templates/backend/base.html
Normal file
@@ -0,0 +1,35 @@
|
||||
{% extends "base.html" %}
|
||||
|
||||
{% block sidebar %}
|
||||
<div class="flex flex-col gap-4 w-full">
|
||||
<div class="flex flex-col gap-1">
|
||||
<div class="text-neutral border-b border-neutral font-bold text-sm opacity-40 mb-2">Members</div>
|
||||
<a class="flex flex-row gap-2 items-center hover:bg-neutral-content rounded-md p-2 cursor-default hover:cursor-pointer">
|
||||
<i class="fa-solid fa-users"></i>
|
||||
<span>Members</span>
|
||||
</a>
|
||||
<a class="flex flex-row gap-2 items-center hover:bg-neutral-content rounded-md p-2">
|
||||
<i class="fa-solid fa-users"></i>
|
||||
<span>Members</span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<div>Members</div>
|
||||
<a>
|
||||
<i class="fa-solid fa-users"></i>
|
||||
<span>Members</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<ul class="menu bg-base-200 rounded-box w-56">
|
||||
<li class="menu-title">Members</li>
|
||||
<li class="menu-active">
|
||||
<a>
|
||||
<i class="fa-solid fa-users w-5 h-5 mr-2 self-center"></i>Members
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
{% endblock sidebar %}
|
||||
1
templates/backend/index.html
Normal file
1
templates/backend/index.html
Normal file
@@ -0,0 +1 @@
|
||||
TEST FILE
|
||||
112
templates/base.html
Normal file
112
templates/base.html
Normal file
@@ -0,0 +1,112 @@
|
||||
{% load tailwind_tags %}
|
||||
{% load static %}
|
||||
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta viewport="width=device-width, initial-scale=1.0">
|
||||
<meta name="viewport" content="viewport-fit=cover, width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||
|
||||
<title>
|
||||
{% if config.TF_CLUB_NAME != "TeamForge" %}{{ config.TF_CLUB_NAME }} - {% endif %}TeamForge
|
||||
</title>
|
||||
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/choices.js@11.1.0/public/assets/styles/choices.min.css"/>
|
||||
<script src="https://cdn.jsdelivr.net/npm/choices.js@11.1.0/public/assets/scripts/choices.min.js"></script>
|
||||
|
||||
<link href="{% static "css/fontawesome.css" %}" rel="stylesheet"/>
|
||||
<link href="{% static "css/solid.css" %}" rel="stylesheet"/>
|
||||
<link href="{% static "css/regular.css" %}" rel="stylesheet"/>
|
||||
<link href="{% static "css/brands.css" %}" rel="stylesheet"/>
|
||||
|
||||
{% tailwind_css %}
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<header id="site-header" class="sticky top-0 z-50 bg-white shadow transition-all duration-300">
|
||||
<div class="mx-auto max-w-6xl px-6 py-4 flex items-center justify-between transition-all duration-300">
|
||||
<h1 class="text-2xl font-bold tracking-tight transition-all duration-300">MY LOGO</h1>
|
||||
|
||||
<nav class="flex items-center space-x-6 text-lg">
|
||||
<a href="#" class="hover:text-blue-600 transition-colors">Home</a>
|
||||
<a href="#" class="hover:text-blue-600 transition-colors">About</a>
|
||||
<a href="#" class="hover:text-blue-600 transition-colors">Contact</a>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<main class="mx-auto max-w-6xl px-6 pt-8 space-y-8">
|
||||
<p class="text-gray-700">
|
||||
Scroll down to see the navbar shrink
|
||||
</p>
|
||||
|
||||
<div class="h-[2000px] bg-gray-200 rounded-lg"></div>
|
||||
</main>
|
||||
|
||||
|
||||
{% comment %}<div class="navbar bg-base-200 shadow-sm sticky top-0 z-10">
|
||||
<div class="flex-1">
|
||||
<label for="drawer-button" aria-label="open sidebar" class="btn btn-square btn-outline lg:hidden">
|
||||
<!-- Sidebar toggle icon -->
|
||||
<i class="fa-solid fa-bars"></i>
|
||||
</label>
|
||||
|
||||
<div class="flex flex-row items-center px-4 py-2 gap-4">
|
||||
{% if config.TF_CLUB_LOGO != "teamforge/logo.png" %}
|
||||
<img class="max-h-18 transform-gpu mask mask-circle" src="{% static config.TF_CLUB_LOGO %}" alt="{{ config.TF_CLUB_NAME }} Logo"/>
|
||||
{% else %}
|
||||
<img class="max-h-18 transform-gpu" src="{% static config.TF_CLUB_LOGO %}" alt="{{ config.TF_CLUB_NAME }} Logo"/>
|
||||
{% endif %}
|
||||
|
||||
<a class="font-jersey text-4xl">{{ config.TF_CLUB_NAME }}</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mr-2 flex flex-row gap-2">
|
||||
<div class="avatar avatar-placeholder">
|
||||
<div class="bg-neutral text-neutral-content w-12 rounded-full">
|
||||
<span>BS</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="drawer lg:drawer-open">
|
||||
<input id="drawer-button" type="checkbox" class="drawer-toggle"/>
|
||||
<div class="drawer-content flex flex-col items-center justify-center">
|
||||
{% block content %}PAGE CONTENT{% endblock content %}
|
||||
</div>
|
||||
|
||||
<div class="drawer-side z-0">
|
||||
<label for="drawer-button" aria-label="close sidebar" class="drawer-overlay"></label>
|
||||
<div class="flex min-h-full flex-col items-start bg-base-200 py-4 px-8">
|
||||
{% block sidebar %}SIDEBAR CONTENT{% endblock sidebar %}
|
||||
</div>
|
||||
</div>
|
||||
</div>{% endcomment %}
|
||||
</body>
|
||||
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", () => {
|
||||
const header = document.getElementById("site-header");
|
||||
let lastScrollY = window.scrollY;
|
||||
const threshold = 40; // Amount to scroll before shrinking
|
||||
|
||||
const onScroll = () => {
|
||||
const current = window.scrollY;
|
||||
|
||||
if (current > threshold) {
|
||||
header.classList.add("navbar-small");
|
||||
} else {
|
||||
header.classList.remove("navbar-small");
|
||||
}
|
||||
|
||||
lastScrollY = current;
|
||||
};
|
||||
|
||||
window.addEventListener("scroll", onScroll, {passive: true});
|
||||
});
|
||||
</script>
|
||||
</html>
|
||||
5
templates/members/member_filter.html
Normal file
5
templates/members/member_filter.html
Normal file
@@ -0,0 +1,5 @@
|
||||
{% extends "backend/base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<div class="h-full">1</div>
|
||||
{% endblock content %}
|
||||
0
theme/__init__.py
Normal file
0
theme/__init__.py
Normal file
5
theme/apps.py
Normal file
5
theme/apps.py
Normal file
@@ -0,0 +1,5 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class ThemeConfig(AppConfig):
|
||||
name = 'theme'
|
||||
1
theme/static_src/.gitignore
vendored
Normal file
1
theme/static_src/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
node_modules
|
||||
1725
theme/static_src/package-lock.json
generated
Normal file
1725
theme/static_src/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
26
theme/static_src/package.json
Normal file
26
theme/static_src/package.json
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"name": "theme",
|
||||
"version": "4.4.0",
|
||||
"description": "",
|
||||
"scripts": {
|
||||
"start": "npm run dev",
|
||||
"build": "npm run build:clean && npm run build:tailwind",
|
||||
"build:clean": "rimraf ../static/css/dist",
|
||||
"build:tailwind": "cross-env NODE_ENV=production postcss ./src/styles.css -o ../static/css/dist/styles.css --minify",
|
||||
"dev": "cross-env NODE_ENV=development postcss ./src/styles.css -o ../static/css/dist/styles.css --watch"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@tailwindcss/postcss": "^4.1.16",
|
||||
"daisyui": "^5.3.10",
|
||||
"cross-env": "^10.1.0",
|
||||
"postcss": "^8.5.6",
|
||||
"postcss-cli": "^11.0.1",
|
||||
"postcss-nested": "^7.0.2",
|
||||
"postcss-simple-vars": "^7.0.1",
|
||||
"rimraf": "^6.0.1",
|
||||
"tailwindcss": "^4.1.16"
|
||||
}
|
||||
}
|
||||
7
theme/static_src/postcss.config.js
Normal file
7
theme/static_src/postcss.config.js
Normal file
@@ -0,0 +1,7 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
"@tailwindcss/postcss": {},
|
||||
"postcss-simple-vars": {},
|
||||
"postcss-nested": {}
|
||||
},
|
||||
}
|
||||
39
theme/static_src/src/styles.css
Normal file
39
theme/static_src/src/styles.css
Normal file
@@ -0,0 +1,39 @@
|
||||
@import url('https://fonts.googleapis.com/css2?family=Cabin:ital,wght@0,400..700;1,400..700&family=Catamaran:wght@100..900&family=Fira+Sans:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&family=Noto+Sans:ital,wght@0,100..900;1,100..900&family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&family=Ubuntu:ital,wght@0,300;0,400;0,500;0,700;1,300;1,400;1,500;1,700&display=swap');
|
||||
@import url('https://fonts.googleapis.com/css2?family=Graduate&display=swap');
|
||||
@import url('https://fonts.googleapis.com/css2?family=Barlow+Semi+Condensed:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap');
|
||||
@import url('https://fonts.googleapis.com/css2?family=Barlow+Semi+Condensed:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&family=Open+Sans:ital,wght@0,300..800;1,300..800&display=swap');
|
||||
|
||||
@import "tailwindcss";
|
||||
|
||||
@plugin "daisyui";
|
||||
|
||||
/**
|
||||
* A catch-all path to Django template files, JavaScript, and Python files
|
||||
* that contain Tailwind CSS classes and will be scanned by Tailwind to generate the final CSS file.
|
||||
*
|
||||
* If your final CSS file is not being updated after code changes, you may want to broaden or narrow
|
||||
* the scope of this path.
|
||||
*/
|
||||
@source "../../../**/*.{html,py,js}";
|
||||
|
||||
@theme {
|
||||
--font-jersey: Graduate, sans-serif;
|
||||
--font-sans: Open Sans, Noto Sans, Barlow Semi Condensed, Ubuntu, Fira Sans, Catamaran, Cabin, Roboto, sans-serif;
|
||||
}
|
||||
|
||||
@layer utilities {
|
||||
.navbar-small {
|
||||
}
|
||||
|
||||
.navbar-small > div {
|
||||
@apply py-1;
|
||||
}
|
||||
|
||||
.navbar-small h1 {
|
||||
@apply text-lg;
|
||||
}
|
||||
|
||||
.navbar-small nav {
|
||||
@apply text-base;
|
||||
}
|
||||
}
|
||||
26
theme/templates/base.html
Normal file
26
theme/templates/base.html
Normal file
@@ -0,0 +1,26 @@
|
||||
{% load static tailwind_tags %}
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<title>Django Tailwind</title>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta http-equiv="X-UA-Compatible" content="ie=edge">
|
||||
{% tailwind_css %}
|
||||
</head>
|
||||
|
||||
<body class="bg-gray-50 text-black font-serif leading-normal tracking-normal">
|
||||
|
||||
<div class="toast toast-top toast-end">
|
||||
<div class="alert alert-info">
|
||||
<span>Hello from daisyUi 👋</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="container mx-auto">
|
||||
<section class="flex items-center justify-center h-screen">
|
||||
<h1 class="text-5xl">Django + Tailwind = ❤️</h1>
|
||||
</section>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
108
uv.lock
generated
108
uv.lock
generated
@@ -222,19 +222,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/d7/ae/f19e24789a5ad852670d6885f5480f5e5895576945fcc01817dfd9bc002a/django-6.0-py3-none-any.whl", hash = "sha256:1cc2c7344303bbfb7ba5070487c17f7fc0b7174bbb0a38cebf03c675f5f19b6d", size = 8339181, upload-time = "2025-12-03T16:26:16.231Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "django-browser-reload"
|
||||
version = "1.21.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "asgiref" },
|
||||
{ name = "django" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/79/ef/ab407c1a2f14e13c75a6419a2d124954aa0364f62580ad95a3d31dc6c73d/django_browser_reload-1.21.0.tar.gz", hash = "sha256:3335ad3d107eb657f623d1a8e680dfbcab8a83ae1f94df1895e069dddf5604ba", size = 15800, upload-time = "2025-09-22T17:00:35.199Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/46/61/1b4a8c589652859995bcab87682286443eb9fdf2d7fd584975b9ffc1db33/django_browser_reload-1.21.0-py3-none-any.whl", hash = "sha256:0b2a86ab460774fa9bb142a121c70e75a72f18109f51a4f6de409cd633d3a70d", size = 12852, upload-time = "2025-09-22T17:00:33.479Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "django-constance"
|
||||
version = "4.3.4"
|
||||
@@ -256,6 +243,18 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/64/96/d967ca440d6a8e3861120f51985d8e5aec79b9a8bdda16041206adfe7adc/django_extensions-4.1-py3-none-any.whl", hash = "sha256:0699a7af28f2523bf8db309a80278519362cd4b6e1fd0a8cd4bf063e1e023336", size = 232980, upload-time = "2025-04-11T01:15:37.701Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "django-filter"
|
||||
version = "25.2"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "django" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/2c/e4/465d2699cd388c0005fb8d6ae6709f239917c6d8790ac35719676fffdcf3/django_filter-25.2.tar.gz", hash = "sha256:760e984a931f4468d096f5541787efb8998c61217b73006163bf2f9523fe8f23", size = 143818, upload-time = "2025-10-05T09:51:31.521Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/c1/40/6a02495c5658beb1f31eb09952d8aa12ef3c2a66342331ce3a35f7132439/django_filter-25.2-py3-none-any.whl", hash = "sha256:9c0f8609057309bba611062fe1b720b4a873652541192d232dd28970383633e3", size = 94145, upload-time = "2025-10-05T09:51:29.728Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "django-phonenumber-field"
|
||||
version = "8.4.0"
|
||||
@@ -273,18 +272,6 @@ phonenumbers = [
|
||||
{ name = "phonenumbers" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "django-polymorphic"
|
||||
version = "4.5.1"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
dependencies = [
|
||||
{ name = "django" },
|
||||
]
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/a0/fa/63345191ac6e1e117ae9c989e57f70be33ab84160bb6632db30b894cb8f8/django_polymorphic-4.5.1.tar.gz", hash = "sha256:9c43fbd1975ff3f5ed0cecaef8da22e099a63780b8b5611c0dd403319222d638", size = 50326, upload-time = "2025-12-24T18:34:26.567Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/b6/10/123ce0f64fdb2f3f4707ae022c4e47f51dbafe09ec30c68ce9a5a61e5d4f/django_polymorphic-4.5.1-py3-none-any.whl", hash = "sha256:e4c6845de64761e2c9c1e8253c1e58a70218874904caa85e2178c311298b8dbe", size = 64482, upload-time = "2025-12-24T18:34:24.98Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "django-tailwind"
|
||||
version = "4.4.2"
|
||||
@@ -305,9 +292,6 @@ cookiecutter = [
|
||||
honcho = [
|
||||
{ name = "honcho" },
|
||||
]
|
||||
reload = [
|
||||
{ name = "django-browser-reload" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "honcho"
|
||||
@@ -424,64 +408,6 @@ wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/37/71/364ea74338bde467bec6b6b0ab33b5ced57e473dfb427b96cc78da8e6af4/phonenumbers-9.0.21-py2.py3-none-any.whl", hash = "sha256:3a0f717fddf901a5a424f47c43fb72722cb45bd25ee87331987b00eafe6855bf", size = 2584216, upload-time = "2025-12-18T07:37:24.539Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "pillow"
|
||||
version = "12.0.0"
|
||||
source = { registry = "https://pypi.org/simple" }
|
||||
sdist = { url = "https://files.pythonhosted.org/packages/5a/b0/cace85a1b0c9775a9f8f5d5423c8261c858760e2466c79b2dd184638b056/pillow-12.0.0.tar.gz", hash = "sha256:87d4f8125c9988bfbed67af47dd7a953e2fc7b0cc1e7800ec6d2080d490bb353", size = 47008828, upload-time = "2025-10-15T18:24:14.008Z" }
|
||||
wheels = [
|
||||
{ url = "https://files.pythonhosted.org/packages/62/f2/de993bb2d21b33a98d031ecf6a978e4b61da207bef02f7b43093774c480d/pillow-12.0.0-cp313-cp313-ios_13_0_arm64_iphoneos.whl", hash = "sha256:0869154a2d0546545cde61d1789a6524319fc1897d9ee31218eae7a60ccc5643", size = 4045493, upload-time = "2025-10-15T18:22:25.758Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0e/b6/bc8d0c4c9f6f111a783d045310945deb769b806d7574764234ffd50bc5ea/pillow-12.0.0-cp313-cp313-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:a7921c5a6d31b3d756ec980f2f47c0cfdbce0fc48c22a39347a895f41f4a6ea4", size = 4120461, upload-time = "2025-10-15T18:22:27.286Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/5d/57/d60d343709366a353dc56adb4ee1e7d8a2cc34e3fbc22905f4167cfec119/pillow-12.0.0-cp313-cp313-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:1ee80a59f6ce048ae13cda1abf7fbd2a34ab9ee7d401c46be3ca685d1999a399", size = 3576912, upload-time = "2025-10-15T18:22:28.751Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a4/a4/a0a31467e3f83b94d37568294b01d22b43ae3c5d85f2811769b9c66389dd/pillow-12.0.0-cp313-cp313-macosx_10_13_x86_64.whl", hash = "sha256:c50f36a62a22d350c96e49ad02d0da41dbd17ddc2e29750dbdba4323f85eb4a5", size = 5249132, upload-time = "2025-10-15T18:22:30.641Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/83/06/48eab21dd561de2914242711434c0c0eb992ed08ff3f6107a5f44527f5e9/pillow-12.0.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:5193fde9a5f23c331ea26d0cf171fbf67e3f247585f50c08b3e205c7aeb4589b", size = 4650099, upload-time = "2025-10-15T18:22:32.73Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fc/bd/69ed99fd46a8dba7c1887156d3572fe4484e3f031405fcc5a92e31c04035/pillow-12.0.0-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:bde737cff1a975b70652b62d626f7785e0480918dece11e8fef3c0cf057351c3", size = 6230808, upload-time = "2025-10-15T18:22:34.337Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ea/94/8fad659bcdbf86ed70099cb60ae40be6acca434bbc8c4c0d4ef356d7e0de/pillow-12.0.0-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:a6597ff2b61d121172f5844b53f21467f7082f5fb385a9a29c01414463f93b07", size = 8037804, upload-time = "2025-10-15T18:22:36.402Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/20/39/c685d05c06deecfd4e2d1950e9a908aa2ca8bc4e6c3b12d93b9cafbd7837/pillow-12.0.0-cp313-cp313-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0b817e7035ea7f6b942c13aa03bb554fc44fea70838ea21f8eb31c638326584e", size = 6345553, upload-time = "2025-10-15T18:22:38.066Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/38/57/755dbd06530a27a5ed74f8cb0a7a44a21722ebf318edbe67ddbd7fb28f88/pillow-12.0.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:f4f1231b7dec408e8670264ce63e9c71409d9583dd21d32c163e25213ee2a344", size = 7037729, upload-time = "2025-10-15T18:22:39.769Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ca/b6/7e94f4c41d238615674d06ed677c14883103dce1c52e4af16f000338cfd7/pillow-12.0.0-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:6e51b71417049ad6ab14c49608b4a24d8fb3fe605e5dfabfe523b58064dc3d27", size = 6459789, upload-time = "2025-10-15T18:22:41.437Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9c/14/4448bb0b5e0f22dd865290536d20ec8a23b64e2d04280b89139f09a36bb6/pillow-12.0.0-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:d120c38a42c234dc9a8c5de7ceaaf899cf33561956acb4941653f8bdc657aa79", size = 7130917, upload-time = "2025-10-15T18:22:43.152Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/dd/ca/16c6926cc1c015845745d5c16c9358e24282f1e588237a4c36d2b30f182f/pillow-12.0.0-cp313-cp313-win32.whl", hash = "sha256:4cc6b3b2efff105c6a1656cfe59da4fdde2cda9af1c5e0b58529b24525d0a098", size = 6302391, upload-time = "2025-10-15T18:22:44.753Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6d/2a/dd43dcfd6dae9b6a49ee28a8eedb98c7d5ff2de94a5d834565164667b97b/pillow-12.0.0-cp313-cp313-win_amd64.whl", hash = "sha256:4cf7fed4b4580601c4345ceb5d4cbf5a980d030fd5ad07c4d2ec589f95f09905", size = 7007477, upload-time = "2025-10-15T18:22:46.838Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/77/f0/72ea067f4b5ae5ead653053212af05ce3705807906ba3f3e8f58ddf617e6/pillow-12.0.0-cp313-cp313-win_arm64.whl", hash = "sha256:9f0b04c6b8584c2c193babcccc908b38ed29524b29dd464bc8801bf10d746a3a", size = 2435918, upload-time = "2025-10-15T18:22:48.399Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f5/5e/9046b423735c21f0487ea6cb5b10f89ea8f8dfbe32576fe052b5ba9d4e5b/pillow-12.0.0-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:7fa22993bac7b77b78cae22bad1e2a987ddf0d9015c63358032f84a53f23cdc3", size = 5251406, upload-time = "2025-10-15T18:22:49.905Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/12/66/982ceebcdb13c97270ef7a56c3969635b4ee7cd45227fa707c94719229c5/pillow-12.0.0-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:f135c702ac42262573fe9714dfe99c944b4ba307af5eb507abef1667e2cbbced", size = 4653218, upload-time = "2025-10-15T18:22:51.587Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/16/b3/81e625524688c31859450119bf12674619429cab3119eec0e30a7a1029cb/pillow-12.0.0-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c85de1136429c524e55cfa4e033b4a7940ac5c8ee4d9401cc2d1bf48154bbc7b", size = 6266564, upload-time = "2025-10-15T18:22:53.215Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/98/59/dfb38f2a41240d2408096e1a76c671d0a105a4a8471b1871c6902719450c/pillow-12.0.0-cp313-cp313t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:38df9b4bfd3db902c9c2bd369bcacaf9d935b2fff73709429d95cc41554f7b3d", size = 8069260, upload-time = "2025-10-15T18:22:54.933Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/dc/3d/378dbea5cd1874b94c312425ca77b0f47776c78e0df2df751b820c8c1d6c/pillow-12.0.0-cp313-cp313t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:7d87ef5795da03d742bf49439f9ca4d027cde49c82c5371ba52464aee266699a", size = 6379248, upload-time = "2025-10-15T18:22:56.605Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/84/b0/d525ef47d71590f1621510327acec75ae58c721dc071b17d8d652ca494d8/pillow-12.0.0-cp313-cp313t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:aff9e4d82d082ff9513bdd6acd4f5bd359f5b2c870907d2b0a9c5e10d40c88fe", size = 7066043, upload-time = "2025-10-15T18:22:58.53Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/61/2c/aced60e9cf9d0cde341d54bf7932c9ffc33ddb4a1595798b3a5150c7ec4e/pillow-12.0.0-cp313-cp313t-musllinux_1_2_aarch64.whl", hash = "sha256:8d8ca2b210ada074d57fcee40c30446c9562e542fc46aedc19baf758a93532ee", size = 6490915, upload-time = "2025-10-15T18:23:00.582Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/ef/26/69dcb9b91f4e59f8f34b2332a4a0a951b44f547c4ed39d3e4dcfcff48f89/pillow-12.0.0-cp313-cp313t-musllinux_1_2_x86_64.whl", hash = "sha256:99a7f72fb6249302aa62245680754862a44179b545ded638cf1fef59befb57ef", size = 7157998, upload-time = "2025-10-15T18:23:02.627Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/61/2b/726235842220ca95fa441ddf55dd2382b52ab5b8d9c0596fe6b3f23dafe8/pillow-12.0.0-cp313-cp313t-win32.whl", hash = "sha256:4078242472387600b2ce8d93ade8899c12bf33fa89e55ec89fe126e9d6d5d9e9", size = 6306201, upload-time = "2025-10-15T18:23:04.709Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c0/3d/2afaf4e840b2df71344ababf2f8edd75a705ce500e5dc1e7227808312ae1/pillow-12.0.0-cp313-cp313t-win_amd64.whl", hash = "sha256:2c54c1a783d6d60595d3514f0efe9b37c8808746a66920315bfd34a938d7994b", size = 7013165, upload-time = "2025-10-15T18:23:06.46Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/6f/75/3fa09aa5cf6ed04bee3fa575798ddf1ce0bace8edb47249c798077a81f7f/pillow-12.0.0-cp313-cp313t-win_arm64.whl", hash = "sha256:26d9f7d2b604cd23aba3e9faf795787456ac25634d82cd060556998e39c6fa47", size = 2437834, upload-time = "2025-10-15T18:23:08.194Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/54/2a/9a8c6ba2c2c07b71bec92cf63e03370ca5e5f5c5b119b742bcc0cde3f9c5/pillow-12.0.0-cp314-cp314-ios_13_0_arm64_iphoneos.whl", hash = "sha256:beeae3f27f62308f1ddbcfb0690bf44b10732f2ef43758f169d5e9303165d3f9", size = 4045531, upload-time = "2025-10-15T18:23:10.121Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/84/54/836fdbf1bfb3d66a59f0189ff0b9f5f666cee09c6188309300df04ad71fa/pillow-12.0.0-cp314-cp314-ios_13_0_arm64_iphonesimulator.whl", hash = "sha256:d4827615da15cd59784ce39d3388275ec093ae3ee8d7f0c089b76fa87af756c2", size = 4120554, upload-time = "2025-10-15T18:23:12.14Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0d/cd/16aec9f0da4793e98e6b54778a5fbce4f375c6646fe662e80600b8797379/pillow-12.0.0-cp314-cp314-ios_13_0_x86_64_iphonesimulator.whl", hash = "sha256:3e42edad50b6909089750e65c91aa09aaf1e0a71310d383f11321b27c224ed8a", size = 3576812, upload-time = "2025-10-15T18:23:13.962Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f6/b7/13957fda356dc46339298b351cae0d327704986337c3c69bb54628c88155/pillow-12.0.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:e5d8efac84c9afcb40914ab49ba063d94f5dbdf5066db4482c66a992f47a3a3b", size = 5252689, upload-time = "2025-10-15T18:23:15.562Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/fc/f5/eae31a306341d8f331f43edb2e9122c7661b975433de5e447939ae61c5da/pillow-12.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:266cd5f2b63ff316d5a1bba46268e603c9caf5606d44f38c2873c380950576ad", size = 4650186, upload-time = "2025-10-15T18:23:17.379Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/86/62/2a88339aa40c4c77e79108facbd307d6091e2c0eb5b8d3cf4977cfca2fe6/pillow-12.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:58eea5ebe51504057dd95c5b77d21700b77615ab0243d8152793dc00eb4faf01", size = 6230308, upload-time = "2025-10-15T18:23:18.971Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c7/33/5425a8992bcb32d1cb9fa3dd39a89e613d09a22f2c8083b7bf43c455f760/pillow-12.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:f13711b1a5ba512d647a0e4ba79280d3a9a045aaf7e0cc6fbe96b91d4cdf6b0c", size = 8039222, upload-time = "2025-10-15T18:23:20.909Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/d8/61/3f5d3b35c5728f37953d3eec5b5f3e77111949523bd2dd7f31a851e50690/pillow-12.0.0-cp314-cp314-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6846bd2d116ff42cba6b646edf5bf61d37e5cbd256425fa089fee4ff5c07a99e", size = 6346657, upload-time = "2025-10-15T18:23:23.077Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/3a/be/ee90a3d79271227e0f0a33c453531efd6ed14b2e708596ba5dd9be948da3/pillow-12.0.0-cp314-cp314-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c98fa880d695de164b4135a52fd2e9cd7b7c90a9d8ac5e9e443a24a95ef9248e", size = 7038482, upload-time = "2025-10-15T18:23:25.005Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/44/34/a16b6a4d1ad727de390e9bd9f19f5f669e079e5826ec0f329010ddea492f/pillow-12.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:fa3ed2a29a9e9d2d488b4da81dcb54720ac3104a20bf0bd273f1e4648aff5af9", size = 6461416, upload-time = "2025-10-15T18:23:27.009Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/b6/39/1aa5850d2ade7d7ba9f54e4e4c17077244ff7a2d9e25998c38a29749eb3f/pillow-12.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:d034140032870024e6b9892c692fe2968493790dd57208b2c37e3fb35f6df3ab", size = 7131584, upload-time = "2025-10-15T18:23:29.752Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/bf/db/4fae862f8fad0167073a7733973bfa955f47e2cac3dc3e3e6257d10fab4a/pillow-12.0.0-cp314-cp314-win32.whl", hash = "sha256:1b1b133e6e16105f524a8dec491e0586d072948ce15c9b914e41cdadd209052b", size = 6400621, upload-time = "2025-10-15T18:23:32.06Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/2b/24/b350c31543fb0107ab2599464d7e28e6f856027aadda995022e695313d94/pillow-12.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:8dc232e39d409036af549c86f24aed8273a40ffa459981146829a324e0848b4b", size = 7142916, upload-time = "2025-10-15T18:23:34.71Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/0f/9b/0ba5a6fd9351793996ef7487c4fdbde8d3f5f75dbedc093bb598648fddf0/pillow-12.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:d52610d51e265a51518692045e372a4c363056130d922a7351429ac9f27e70b0", size = 2523836, upload-time = "2025-10-15T18:23:36.967Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/f5/7a/ceee0840aebc579af529b523d530840338ecf63992395842e54edc805987/pillow-12.0.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:1979f4566bb96c1e50a62d9831e2ea2d1211761e5662afc545fa766f996632f6", size = 5255092, upload-time = "2025-10-15T18:23:38.573Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/44/76/20776057b4bfd1aef4eeca992ebde0f53a4dce874f3ae693d0ec90a4f79b/pillow-12.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:b2e4b27a6e15b04832fe9bf292b94b5ca156016bbc1ea9c2c20098a0320d6cf6", size = 4653158, upload-time = "2025-10-15T18:23:40.238Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/82/3f/d9ff92ace07be8836b4e7e87e6a4c7a8318d47c2f1463ffcf121fc57d9cb/pillow-12.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:fb3096c30df99fd01c7bf8e544f392103d0795b9f98ba71a8054bcbf56b255f1", size = 6267882, upload-time = "2025-10-15T18:23:42.434Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/9f/7a/4f7ff87f00d3ad33ba21af78bfcd2f032107710baf8280e3722ceec28cda/pillow-12.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:7438839e9e053ef79f7112c881cef684013855016f928b168b81ed5835f3e75e", size = 8071001, upload-time = "2025-10-15T18:23:44.29Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/75/87/fcea108944a52dad8cca0715ae6247e271eb80459364a98518f1e4f480c1/pillow-12.0.0-cp314-cp314t-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5d5c411a8eaa2299322b647cd932586b1427367fd3184ffbb8f7a219ea2041ca", size = 6380146, upload-time = "2025-10-15T18:23:46.065Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/91/52/0d31b5e571ef5fd111d2978b84603fce26aba1b6092f28e941cb46570745/pillow-12.0.0-cp314-cp314t-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:d7e091d464ac59d2c7ad8e7e08105eaf9dafbc3883fd7265ffccc2baad6ac925", size = 7067344, upload-time = "2025-10-15T18:23:47.898Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/7b/f4/2dd3d721f875f928d48e83bb30a434dee75a2531bca839bb996bb0aa5a91/pillow-12.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:792a2c0be4dcc18af9d4a2dfd8a11a17d5e25274a1062b0ec1c2d79c76f3e7f8", size = 6491864, upload-time = "2025-10-15T18:23:49.607Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/30/4b/667dfcf3d61fc309ba5a15b141845cece5915e39b99c1ceab0f34bf1d124/pillow-12.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:afbefa430092f71a9593a99ab6a4e7538bc9eabbf7bf94f91510d3503943edc4", size = 7158911, upload-time = "2025-10-15T18:23:51.351Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/a2/2f/16cabcc6426c32218ace36bf0d55955e813f2958afddbf1d391849fee9d1/pillow-12.0.0-cp314-cp314t-win32.whl", hash = "sha256:3830c769decf88f1289680a59d4f4c46c72573446352e2befec9a8512104fa52", size = 6408045, upload-time = "2025-10-15T18:23:53.177Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/35/73/e29aa0c9c666cf787628d3f0dcf379f4791fba79f4936d02f8b37165bdf8/pillow-12.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:905b0365b210c73afb0ebe9101a32572152dfd1c144c7e28968a331b9217b94a", size = 7148282, upload-time = "2025-10-15T18:23:55.316Z" },
|
||||
{ url = "https://files.pythonhosted.org/packages/c1/70/6b41bdcddf541b437bbb9f47f94d2db5d9ddef6c37ccab8c9107743748a4/pillow-12.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:99353a06902c2e43b43e8ff74ee65a7d90307d82370604746738a1e0661ccca7", size = 2525630, upload-time = "2025-10-15T18:23:57.149Z" },
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "psycopg2-binary"
|
||||
version = "2.9.11"
|
||||
@@ -689,10 +615,9 @@ dependencies = [
|
||||
{ name = "django" },
|
||||
{ name = "django-constance" },
|
||||
{ name = "django-extensions" },
|
||||
{ name = "django-filter" },
|
||||
{ name = "django-phonenumber-field", extra = ["phonenumbers"] },
|
||||
{ name = "django-polymorphic" },
|
||||
{ name = "django-tailwind", extra = ["cookiecutter", "honcho", "reload"] },
|
||||
{ name = "pillow" },
|
||||
{ name = "django-tailwind", extra = ["cookiecutter", "honcho"] },
|
||||
{ name = "psycopg2-binary" },
|
||||
{ name = "python-decouple" },
|
||||
{ name = "rules" },
|
||||
@@ -710,10 +635,9 @@ requires-dist = [
|
||||
{ name = "django", specifier = ">=6.0" },
|
||||
{ name = "django-constance", specifier = ">=4.3.4" },
|
||||
{ name = "django-extensions", specifier = ">=4.1" },
|
||||
{ name = "django-filter", specifier = ">=25.2" },
|
||||
{ name = "django-phonenumber-field", extras = ["phonenumbers"], specifier = ">=8.4.0" },
|
||||
{ name = "django-polymorphic", specifier = ">=4.5.1" },
|
||||
{ name = "django-tailwind", extras = ["cookiecutter", "honcho", "reload"], specifier = ">=4.4.2" },
|
||||
{ name = "pillow", specifier = ">=12.0.0" },
|
||||
{ name = "django-tailwind", extras = ["cookiecutter", "honcho"], specifier = ">=4.4.2" },
|
||||
{ name = "psycopg2-binary", specifier = ">=2.9.11" },
|
||||
{ name = "python-decouple", specifier = ">=3.8" },
|
||||
{ name = "rules", specifier = ">=3.5" },
|
||||
|
||||
Reference in New Issue
Block a user