From 14b0bc6d015947f3c5837558a8136222c56f2803 Mon Sep 17 00:00:00 2001 From: tegwick Date: Fri, 8 May 2026 14:26:48 +0200 Subject: [PATCH] Prototype implementation --- .env.example | 6 + .gitignore | 7 + .python-version | 1 + CLAUDE.md | 43 +- Makefile | 30 + conftest.py | 10 + docker-compose.dev.yml | 14 + docker-compose.test.yml | 14 + manage.py | 22 + package-lock.json | 1643 +++++++++++++++++ package.json | 12 + pyproject.toml | 44 + static/src/main.css | 43 + static/vendor/alpinejs/alpine.min.js | 5 + static/vendor/htmx/htmx.min.js | 1 + uv.lock | 616 ++++++ vergabe_teilnahme/__init__.py | 0 vergabe_teilnahme/apps/__init__.py | 0 vergabe_teilnahme/apps/accounts/__init__.py | 0 vergabe_teilnahme/apps/accounts/admin.py | 12 + vergabe_teilnahme/apps/accounts/apps.py | 5 + .../apps/accounts/migrations/0001_initial.py | 46 + ..._alter_mitarbeiter_mobilnummer_and_more.py | 28 + .../apps/accounts/migrations/__init__.py | 0 vergabe_teilnahme/apps/accounts/models.py | 25 + vergabe_teilnahme/apps/accounts/tests.py | 3 + vergabe_teilnahme/apps/accounts/views.py | 3 + vergabe_teilnahme/apps/aufgaben/__init__.py | 0 vergabe_teilnahme/apps/aufgaben/admin.py | 15 + vergabe_teilnahme/apps/aufgaben/apps.py | 5 + .../apps/aufgaben/migrations/0001_initial.py | 51 + .../apps/aufgaben/migrations/0002_initial.py | 71 + .../apps/aufgaben/migrations/__init__.py | 0 vergabe_teilnahme/apps/aufgaben/models.py | 113 ++ vergabe_teilnahme/apps/aufgaben/tests.py | 3 + vergabe_teilnahme/apps/aufgaben/urls.py | 2 + vergabe_teilnahme/apps/aufgaben/views.py | 3 + .../apps/ausschreibungen/__init__.py | 0 .../apps/ausschreibungen/admin.py | 11 + .../apps/ausschreibungen/apps.py | 5 + .../migrations/0001_initial.py | 53 + .../ausschreibungen/migrations/__init__.py | 0 .../apps/ausschreibungen/models.py | 107 ++ .../apps/ausschreibungen/tests.py | 3 + .../apps/ausschreibungen/urls.py | 9 + .../apps/ausschreibungen/views.py | 7 + vergabe_teilnahme/apps/bibliothek/__init__.py | 0 vergabe_teilnahme/apps/bibliothek/admin.py | 27 + vergabe_teilnahme/apps/bibliothek/apps.py | 5 + .../bibliothek/migrations/0001_initial.py | 115 ++ .../apps/bibliothek/migrations/__init__.py | 0 vergabe_teilnahme/apps/bibliothek/models.py | 154 ++ vergabe_teilnahme/apps/bibliothek/tests.py | 3 + vergabe_teilnahme/apps/bibliothek/urls.py | 2 + vergabe_teilnahme/apps/bibliothek/views.py | 3 + vergabe_teilnahme/apps/core/__init__.py | 0 vergabe_teilnahme/apps/core/admin.py | 22 + vergabe_teilnahme/apps/core/apps.py | 5 + .../apps/core/context_processors.py | 13 + .../apps/core/management/__init__.py | 0 .../apps/core/management/commands/__init__.py | 0 .../apps/core/management/commands/seed_dev.py | 239 +++ .../apps/core/migrations/0001_initial.py | 70 + .../apps/core/migrations/__init__.py | 0 vergabe_teilnahme/apps/core/models.py | 106 ++ vergabe_teilnahme/apps/core/services.py | 106 ++ .../apps/core/templatetags/__init__.py | 0 .../apps/core/templatetags/vergabe_tags.py | 71 + vergabe_teilnahme/apps/core/tests.py | 3 + vergabe_teilnahme/apps/core/tests/__init__.py | 0 .../apps/core/tests/test_services.py | 41 + vergabe_teilnahme/apps/core/view_helpers.py | 14 + vergabe_teilnahme/apps/core/views.py | 29 + vergabe_teilnahme/apps/dokumente/__init__.py | 0 vergabe_teilnahme/apps/dokumente/admin.py | 10 + vergabe_teilnahme/apps/dokumente/apps.py | 5 + .../apps/dokumente/migrations/0001_initial.py | 36 + .../apps/dokumente/migrations/0002_initial.py | 34 + .../apps/dokumente/migrations/__init__.py | 0 vergabe_teilnahme/apps/dokumente/models.py | 97 + vergabe_teilnahme/apps/dokumente/tests.py | 3 + vergabe_teilnahme/apps/dokumente/urls.py | 2 + vergabe_teilnahme/apps/dokumente/views.py | 3 + vergabe_teilnahme/apps/feedback/__init__.py | 0 vergabe_teilnahme/apps/feedback/admin.py | 10 + vergabe_teilnahme/apps/feedback/apps.py | 5 + .../apps/feedback/migrations/0001_initial.py | 42 + .../apps/feedback/migrations/__init__.py | 0 vergabe_teilnahme/apps/feedback/models.py | 50 + vergabe_teilnahme/apps/feedback/tests.py | 3 + vergabe_teilnahme/apps/feedback/urls.py | 10 + vergabe_teilnahme/apps/feedback/views.py | 46 + vergabe_teilnahme/apps/lose/__init__.py | 0 vergabe_teilnahme/apps/lose/admin.py | 15 + vergabe_teilnahme/apps/lose/apps.py | 5 + .../apps/lose/migrations/0001_initial.py | 63 + .../apps/lose/migrations/__init__.py | 0 vergabe_teilnahme/apps/lose/models.py | 81 + vergabe_teilnahme/apps/lose/tests.py | 3 + vergabe_teilnahme/apps/lose/urls.py | 2 + vergabe_teilnahme/apps/lose/views.py | 3 + .../apps/marktbegleiter/__init__.py | 0 .../apps/marktbegleiter/admin.py | 16 + vergabe_teilnahme/apps/marktbegleiter/apps.py | 5 + .../marktbegleiter/migrations/0001_initial.py | 70 + .../marktbegleiter/migrations/__init__.py | 0 .../apps/marktbegleiter/models.py | 71 + .../apps/marktbegleiter/tests.py | 3 + vergabe_teilnahme/apps/marktbegleiter/urls.py | 2 + .../apps/marktbegleiter/views.py | 3 + .../apps/nachbetrachtung/__init__.py | 0 .../apps/nachbetrachtung/admin.py | 9 + .../apps/nachbetrachtung/apps.py | 5 + .../migrations/0001_initial.py | 39 + .../nachbetrachtung/migrations/__init__.py | 0 .../apps/nachbetrachtung/models.py | 38 + .../apps/nachbetrachtung/tests.py | 3 + .../apps/nachbetrachtung/urls.py | 2 + .../apps/nachbetrachtung/views.py | 3 + vergabe_teilnahme/apps/partner/__init__.py | 0 vergabe_teilnahme/apps/partner/admin.py | 20 + vergabe_teilnahme/apps/partner/apps.py | 5 + .../apps/partner/migrations/0001_initial.py | 86 + .../apps/partner/migrations/__init__.py | 0 vergabe_teilnahme/apps/partner/models.py | 88 + vergabe_teilnahme/apps/partner/tests.py | 3 + vergabe_teilnahme/apps/partner/urls.py | 2 + vergabe_teilnahme/apps/partner/views.py | 3 + vergabe_teilnahme/apps/preise/__init__.py | 0 vergabe_teilnahme/apps/preise/admin.py | 10 + vergabe_teilnahme/apps/preise/apps.py | 5 + .../apps/preise/migrations/0001_initial.py | 48 + .../apps/preise/migrations/__init__.py | 0 vergabe_teilnahme/apps/preise/models.py | 49 + vergabe_teilnahme/apps/preise/tests.py | 3 + vergabe_teilnahme/apps/preise/urls.py | 2 + vergabe_teilnahme/apps/preise/views.py | 3 + vergabe_teilnahme/asgi.py | 16 + vergabe_teilnahme/settings/__init__.py | 0 vergabe_teilnahme/settings/base.py | 92 + vergabe_teilnahme/settings/dev.py | 4 + vergabe_teilnahme/settings/prod.py | 15 + .../templates/ausschreibungen/dashboard.html | 6 + vergabe_teilnahme/templates/base.html | 29 + vergabe_teilnahme/templates/errors/404.html | 10 + vergabe_teilnahme/templates/errors/500.html | 10 + .../templates/partials/breadcrumb.html | 12 + .../templates/partials/feedback_button.html | 8 + .../templates/partials/feedback_modal.html | 46 + .../templates/partials/field_row.html | 8 + .../templates/partials/phase_nav.html | 21 + .../templates/partials/sidebar.html | 59 + .../templates/partials/status_badge.html | 1 + .../templates/partials/topbar.html | 46 + vergabe_teilnahme/urls.py | 40 + vergabe_teilnahme/wsgi.py | 16 + vite.config.js | 13 + workplans/WP-0001-projektgeruest.md | 26 +- workplans/WP-0002-fachmodelle.md | 30 +- workplans/WP-0003-basis-ui.md | 22 +- 160 files changed, 5731 insertions(+), 42 deletions(-) create mode 100644 .env.example create mode 100644 .python-version create mode 100644 Makefile create mode 100644 conftest.py create mode 100644 docker-compose.dev.yml create mode 100644 docker-compose.test.yml create mode 100755 manage.py create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 pyproject.toml create mode 100644 static/src/main.css create mode 100644 static/vendor/alpinejs/alpine.min.js create mode 100644 static/vendor/htmx/htmx.min.js create mode 100644 uv.lock create mode 100644 vergabe_teilnahme/__init__.py create mode 100644 vergabe_teilnahme/apps/__init__.py create mode 100644 vergabe_teilnahme/apps/accounts/__init__.py create mode 100644 vergabe_teilnahme/apps/accounts/admin.py create mode 100644 vergabe_teilnahme/apps/accounts/apps.py create mode 100644 vergabe_teilnahme/apps/accounts/migrations/0001_initial.py create mode 100644 vergabe_teilnahme/apps/accounts/migrations/0002_alter_mitarbeiter_mobilnummer_and_more.py create mode 100644 vergabe_teilnahme/apps/accounts/migrations/__init__.py create mode 100644 vergabe_teilnahme/apps/accounts/models.py create mode 100644 vergabe_teilnahme/apps/accounts/tests.py create mode 100644 vergabe_teilnahme/apps/accounts/views.py create mode 100644 vergabe_teilnahme/apps/aufgaben/__init__.py create mode 100644 vergabe_teilnahme/apps/aufgaben/admin.py create mode 100644 vergabe_teilnahme/apps/aufgaben/apps.py create mode 100644 vergabe_teilnahme/apps/aufgaben/migrations/0001_initial.py create mode 100644 vergabe_teilnahme/apps/aufgaben/migrations/0002_initial.py create mode 100644 vergabe_teilnahme/apps/aufgaben/migrations/__init__.py create mode 100644 vergabe_teilnahme/apps/aufgaben/models.py create mode 100644 vergabe_teilnahme/apps/aufgaben/tests.py create mode 100644 vergabe_teilnahme/apps/aufgaben/urls.py create mode 100644 vergabe_teilnahme/apps/aufgaben/views.py create mode 100644 vergabe_teilnahme/apps/ausschreibungen/__init__.py create mode 100644 vergabe_teilnahme/apps/ausschreibungen/admin.py create mode 100644 vergabe_teilnahme/apps/ausschreibungen/apps.py create mode 100644 vergabe_teilnahme/apps/ausschreibungen/migrations/0001_initial.py create mode 100644 vergabe_teilnahme/apps/ausschreibungen/migrations/__init__.py create mode 100644 vergabe_teilnahme/apps/ausschreibungen/models.py create mode 100644 vergabe_teilnahme/apps/ausschreibungen/tests.py create mode 100644 vergabe_teilnahme/apps/ausschreibungen/urls.py create mode 100644 vergabe_teilnahme/apps/ausschreibungen/views.py create mode 100644 vergabe_teilnahme/apps/bibliothek/__init__.py create mode 100644 vergabe_teilnahme/apps/bibliothek/admin.py create mode 100644 vergabe_teilnahme/apps/bibliothek/apps.py create mode 100644 vergabe_teilnahme/apps/bibliothek/migrations/0001_initial.py create mode 100644 vergabe_teilnahme/apps/bibliothek/migrations/__init__.py create mode 100644 vergabe_teilnahme/apps/bibliothek/models.py create mode 100644 vergabe_teilnahme/apps/bibliothek/tests.py create mode 100644 vergabe_teilnahme/apps/bibliothek/urls.py create mode 100644 vergabe_teilnahme/apps/bibliothek/views.py create mode 100644 vergabe_teilnahme/apps/core/__init__.py create mode 100644 vergabe_teilnahme/apps/core/admin.py create mode 100644 vergabe_teilnahme/apps/core/apps.py create mode 100644 vergabe_teilnahme/apps/core/context_processors.py create mode 100644 vergabe_teilnahme/apps/core/management/__init__.py create mode 100644 vergabe_teilnahme/apps/core/management/commands/__init__.py create mode 100644 vergabe_teilnahme/apps/core/management/commands/seed_dev.py create mode 100644 vergabe_teilnahme/apps/core/migrations/0001_initial.py create mode 100644 vergabe_teilnahme/apps/core/migrations/__init__.py create mode 100644 vergabe_teilnahme/apps/core/models.py create mode 100644 vergabe_teilnahme/apps/core/services.py create mode 100644 vergabe_teilnahme/apps/core/templatetags/__init__.py create mode 100644 vergabe_teilnahme/apps/core/templatetags/vergabe_tags.py create mode 100644 vergabe_teilnahme/apps/core/tests.py create mode 100644 vergabe_teilnahme/apps/core/tests/__init__.py create mode 100644 vergabe_teilnahme/apps/core/tests/test_services.py create mode 100644 vergabe_teilnahme/apps/core/view_helpers.py create mode 100644 vergabe_teilnahme/apps/core/views.py create mode 100644 vergabe_teilnahme/apps/dokumente/__init__.py create mode 100644 vergabe_teilnahme/apps/dokumente/admin.py create mode 100644 vergabe_teilnahme/apps/dokumente/apps.py create mode 100644 vergabe_teilnahme/apps/dokumente/migrations/0001_initial.py create mode 100644 vergabe_teilnahme/apps/dokumente/migrations/0002_initial.py create mode 100644 vergabe_teilnahme/apps/dokumente/migrations/__init__.py create mode 100644 vergabe_teilnahme/apps/dokumente/models.py create mode 100644 vergabe_teilnahme/apps/dokumente/tests.py create mode 100644 vergabe_teilnahme/apps/dokumente/urls.py create mode 100644 vergabe_teilnahme/apps/dokumente/views.py create mode 100644 vergabe_teilnahme/apps/feedback/__init__.py create mode 100644 vergabe_teilnahme/apps/feedback/admin.py create mode 100644 vergabe_teilnahme/apps/feedback/apps.py create mode 100644 vergabe_teilnahme/apps/feedback/migrations/0001_initial.py create mode 100644 vergabe_teilnahme/apps/feedback/migrations/__init__.py create mode 100644 vergabe_teilnahme/apps/feedback/models.py create mode 100644 vergabe_teilnahme/apps/feedback/tests.py create mode 100644 vergabe_teilnahme/apps/feedback/urls.py create mode 100644 vergabe_teilnahme/apps/feedback/views.py create mode 100644 vergabe_teilnahme/apps/lose/__init__.py create mode 100644 vergabe_teilnahme/apps/lose/admin.py create mode 100644 vergabe_teilnahme/apps/lose/apps.py create mode 100644 vergabe_teilnahme/apps/lose/migrations/0001_initial.py create mode 100644 vergabe_teilnahme/apps/lose/migrations/__init__.py create mode 100644 vergabe_teilnahme/apps/lose/models.py create mode 100644 vergabe_teilnahme/apps/lose/tests.py create mode 100644 vergabe_teilnahme/apps/lose/urls.py create mode 100644 vergabe_teilnahme/apps/lose/views.py create mode 100644 vergabe_teilnahme/apps/marktbegleiter/__init__.py create mode 100644 vergabe_teilnahme/apps/marktbegleiter/admin.py create mode 100644 vergabe_teilnahme/apps/marktbegleiter/apps.py create mode 100644 vergabe_teilnahme/apps/marktbegleiter/migrations/0001_initial.py create mode 100644 vergabe_teilnahme/apps/marktbegleiter/migrations/__init__.py create mode 100644 vergabe_teilnahme/apps/marktbegleiter/models.py create mode 100644 vergabe_teilnahme/apps/marktbegleiter/tests.py create mode 100644 vergabe_teilnahme/apps/marktbegleiter/urls.py create mode 100644 vergabe_teilnahme/apps/marktbegleiter/views.py create mode 100644 vergabe_teilnahme/apps/nachbetrachtung/__init__.py create mode 100644 vergabe_teilnahme/apps/nachbetrachtung/admin.py create mode 100644 vergabe_teilnahme/apps/nachbetrachtung/apps.py create mode 100644 vergabe_teilnahme/apps/nachbetrachtung/migrations/0001_initial.py create mode 100644 vergabe_teilnahme/apps/nachbetrachtung/migrations/__init__.py create mode 100644 vergabe_teilnahme/apps/nachbetrachtung/models.py create mode 100644 vergabe_teilnahme/apps/nachbetrachtung/tests.py create mode 100644 vergabe_teilnahme/apps/nachbetrachtung/urls.py create mode 100644 vergabe_teilnahme/apps/nachbetrachtung/views.py create mode 100644 vergabe_teilnahme/apps/partner/__init__.py create mode 100644 vergabe_teilnahme/apps/partner/admin.py create mode 100644 vergabe_teilnahme/apps/partner/apps.py create mode 100644 vergabe_teilnahme/apps/partner/migrations/0001_initial.py create mode 100644 vergabe_teilnahme/apps/partner/migrations/__init__.py create mode 100644 vergabe_teilnahme/apps/partner/models.py create mode 100644 vergabe_teilnahme/apps/partner/tests.py create mode 100644 vergabe_teilnahme/apps/partner/urls.py create mode 100644 vergabe_teilnahme/apps/partner/views.py create mode 100644 vergabe_teilnahme/apps/preise/__init__.py create mode 100644 vergabe_teilnahme/apps/preise/admin.py create mode 100644 vergabe_teilnahme/apps/preise/apps.py create mode 100644 vergabe_teilnahme/apps/preise/migrations/0001_initial.py create mode 100644 vergabe_teilnahme/apps/preise/migrations/__init__.py create mode 100644 vergabe_teilnahme/apps/preise/models.py create mode 100644 vergabe_teilnahme/apps/preise/tests.py create mode 100644 vergabe_teilnahme/apps/preise/urls.py create mode 100644 vergabe_teilnahme/apps/preise/views.py create mode 100644 vergabe_teilnahme/asgi.py create mode 100644 vergabe_teilnahme/settings/__init__.py create mode 100644 vergabe_teilnahme/settings/base.py create mode 100644 vergabe_teilnahme/settings/dev.py create mode 100644 vergabe_teilnahme/settings/prod.py create mode 100644 vergabe_teilnahme/templates/ausschreibungen/dashboard.html create mode 100644 vergabe_teilnahme/templates/base.html create mode 100644 vergabe_teilnahme/templates/errors/404.html create mode 100644 vergabe_teilnahme/templates/errors/500.html create mode 100644 vergabe_teilnahme/templates/partials/breadcrumb.html create mode 100644 vergabe_teilnahme/templates/partials/feedback_button.html create mode 100644 vergabe_teilnahme/templates/partials/feedback_modal.html create mode 100644 vergabe_teilnahme/templates/partials/field_row.html create mode 100644 vergabe_teilnahme/templates/partials/phase_nav.html create mode 100644 vergabe_teilnahme/templates/partials/sidebar.html create mode 100644 vergabe_teilnahme/templates/partials/status_badge.html create mode 100644 vergabe_teilnahme/templates/partials/topbar.html create mode 100644 vergabe_teilnahme/urls.py create mode 100644 vergabe_teilnahme/wsgi.py create mode 100644 vite.config.js diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..85eb501 --- /dev/null +++ b/.env.example @@ -0,0 +1,6 @@ +DATABASE_URL=postgres://vergabe:vergabe@localhost:5432/vergabe_db +SECRET_KEY=change-me-in-production +DEBUG=True +ALLOWED_HOSTS=localhost,127.0.0.1 +MEDIA_ROOT=media/ +MAX_UPLOAD_SIZE=52428800 diff --git a/.gitignore b/.gitignore index 36b13f1..8f25469 100644 --- a/.gitignore +++ b/.gitignore @@ -171,6 +171,13 @@ cython_debug/ # Ruff stuff: .ruff_cache/ +# Build output +static/dist/ +node_modules/ + +# Media uploads +media/ + # PyPI configuration file .pypirc diff --git a/.python-version b/.python-version new file mode 100644 index 0000000..e4fba21 --- /dev/null +++ b/.python-version @@ -0,0 +1 @@ +3.12 diff --git a/CLAUDE.md b/CLAUDE.md index 214637d..d1744cd 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -6,11 +6,48 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co **Vergabe Teilnahme** is a web-based tender/bid management system (internal collaboration tool) that supports a company through the full lifecycle of public and private procurement bids — from initial research through post-award retrospective. The language of the application and all domain documentation is **German**. -This repo is currently **pre-implementation**. The authoritative requirements are in `wiki/ProductRequirementsDocument.md`. +The authoritative requirements are in `wiki/ProductRequirementsDocument.md`. Technical architecture in `wiki/ArchitectureBlueprint.md`. Use cases in `wiki/UseCaseCatalog.md`. -## Planned Tech Stack +## Tech Stack -The .gitignore targets **Python** (Django, Flask, uv, Ruff, pytest). No framework or tooling has been selected yet — check for a `pyproject.toml`, `Pipfile`, or `requirements.txt` before assuming. +**Django 6.x** · **uv** (package manager) · **Tailwind CSS v4** (via Vite) · **HTMX 2.x** · **Alpine.js 3.x** · **PostgreSQL 16+** (psycopg3) + +## Entwicklungs-Commands + +```bash +make db # PostgreSQL via Docker starten (oder infra-postgres-1 verwenden) +make dev # Django-Dev-Server (Port 8000) +make css # Tailwind CSS im Watch-Modus +make migrate # Migrations generieren und ausführen +make test # pytest ausführen +make lint # ruff + mypy +uv run manage.py test vergabe_teilnahme.apps. # Einzelne App testen +uv run pytest vergabe_teilnahme/apps//tests/ # Einzelne Testdatei +``` + +## Projektstruktur + +``` +vergabe_teilnahme/ +├── apps/ # Alle Django-Apps +│ ├── core/ # FlexibleModel, CustomAttribute, EntityFieldConfig, Freigabe +│ ├── accounts/ # Mitarbeiter (AbstractUser) +│ └── ... # je eine App pro Fachdomäne +├── settings/ # base.py, dev.py, prod.py +└── urls.py + +static/ +├── src/main.css # Tailwind-Quelldatei (mit @layer components und @theme brand tokens) +├── vendor/ # HTMX, Alpine.js (lokal, kein CDN) +└── dist/ # Build-Output (gitignored) + +workplans/ # Ralph-Loop-Workplans (WP-0001 bis WP-0012) +wiki/ # PRD, Blueprint, Use-Case-Katalog +``` + +## Shared Infrastructure Note + +Port 5432 is used by `infra-postgres-1` (the Custodian shared PostgreSQL container). The `vergabe_db` database and `vergabe` user are created there. `docker-compose.dev.yml` documents the intended standalone setup but is not started when infra container is active. ## Domain Model — Key Concepts diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..e7930eb --- /dev/null +++ b/Makefile @@ -0,0 +1,30 @@ +.PHONY: dev db migrate shell test lint css createsuperuser collectstatic + +db: + docker compose -f docker-compose.dev.yml up -d 2>/dev/null || echo "DB already running" + +dev: + uv run manage.py runserver 0.0.0.0:8000 + +css: + npm run dev + +migrate: + uv run manage.py makemigrations + uv run manage.py migrate + +shell: + uv run manage.py shell_plus 2>/dev/null || uv run manage.py shell + +test: + uv run pytest + +lint: + uv run ruff check . + uv run mypy vergabe_teilnahme/ + +createsuperuser: + uv run manage.py createsuperuser + +collectstatic: + uv run manage.py collectstatic --noinput diff --git a/conftest.py b/conftest.py new file mode 100644 index 0000000..709e1cc --- /dev/null +++ b/conftest.py @@ -0,0 +1,10 @@ +import pytest + + +@pytest.fixture +def mitarbeiter(db): + from vergabe_teilnahme.apps.accounts.models import Mitarbeiter + + return Mitarbeiter.objects.create_user( + username='testuser', password='testpass', first_name='Test', last_name='User' + ) diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml new file mode 100644 index 0000000..be209ab --- /dev/null +++ b/docker-compose.dev.yml @@ -0,0 +1,14 @@ +services: + db: + image: postgres:16-alpine + environment: + POSTGRES_DB: vergabe_db + POSTGRES_USER: vergabe + POSTGRES_PASSWORD: vergabe + ports: + - "5432:5432" + volumes: + - postgres_data:/var/lib/postgresql/data + +volumes: + postgres_data: diff --git a/docker-compose.test.yml b/docker-compose.test.yml new file mode 100644 index 0000000..1fc2ad6 --- /dev/null +++ b/docker-compose.test.yml @@ -0,0 +1,14 @@ +services: + db: + image: postgres:16-alpine + environment: + POSTGRES_DB: vergabe_test + POSTGRES_USER: vergabe + POSTGRES_PASSWORD: vergabe + ports: + - "5433:5432" + volumes: + - postgres_test_data:/var/lib/postgresql/data + +volumes: + postgres_test_data: diff --git a/manage.py b/manage.py new file mode 100755 index 0000000..157122c --- /dev/null +++ b/manage.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python +"""Django's command-line utility for administrative tasks.""" +import os +import sys + + +def main(): + """Run administrative tasks.""" + os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'vergabe_teilnahme.settings.dev') + try: + from django.core.management import execute_from_command_line + except ImportError as exc: + raise ImportError( + "Couldn't import Django. Are you sure it's installed and " + "available on your PYTHONPATH environment variable? Did you " + "forget to activate a virtual environment?" + ) from exc + execute_from_command_line(sys.argv) + + +if __name__ == '__main__': + main() diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..a3dea9a --- /dev/null +++ b/package-lock.json @@ -0,0 +1,1643 @@ +{ + "name": "vergabe-teilnahme", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "devDependencies": { + "@tailwindcss/vite": "^4.0", + "tailwindcss": "^4.0", + "vite": "^5.0" + } + }, + "node_modules/@esbuild/aix-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.21.5.tgz", + "integrity": "sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "aix" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.21.5.tgz", + "integrity": "sha512-vCPvzSjpPHEi1siZdlvAlsPxXl7WbOVUBBAowWug4rJHb68Ox8KualB+1ocNvT5fjv6wpkX6o/iEpbDrf68zcg==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.21.5.tgz", + "integrity": "sha512-c0uX9VAUBQ7dTDCjq+wdyGLowMdtR/GoC2U5IYk/7D1H1JYC0qseD7+11iMP2mRLN9RcCMRcjC4YMclCzGwS/A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/android-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.21.5.tgz", + "integrity": "sha512-D7aPRUUNHRBwHxzxRvp856rjUHRFW1SdQATKXH2hqA0kAZb1hKmi02OpYRacl0TxIGz/ZmXWlbZgjwWYaCakTA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.21.5.tgz", + "integrity": "sha512-DwqXqZyuk5AiWWf3UfLiRDJ5EDd49zg6O9wclZ7kUMv2WRFr4HKjXp/5t8JZ11QbQfUS6/cRCKGwYhtNAY88kQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/darwin-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.21.5.tgz", + "integrity": "sha512-se/JjF8NlmKVG4kNIuyWMV/22ZaerB+qaSi5MdrXtd6R08kvs2qCN4C09miupktDitvh8jRFflwGFBQcxZRjbw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.21.5.tgz", + "integrity": "sha512-5JcRxxRDUJLX8JXp/wcBCy3pENnCgBR9bN6JsY4OmhfUtIHe3ZW0mawA7+RDAcMLrMIZaf03NlQiX9DGyB8h4g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/freebsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.21.5.tgz", + "integrity": "sha512-J95kNBj1zkbMXtHVH29bBriQygMXqoVQOQYA+ISs0/2l3T9/kj42ow2mpqerRBxDJnmkUDCaQT/dfNXWX/ZZCQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.21.5.tgz", + "integrity": "sha512-bPb5AHZtbeNGjCKVZ9UGqGwo8EUu4cLq68E95A53KlxAPRmUyYv2D6F0uUI65XisGOL1hBP5mTronbgo+0bFcA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.21.5.tgz", + "integrity": "sha512-ibKvmyYzKsBeX8d8I7MH/TMfWDXBF3db4qM6sy+7re0YXya+K1cem3on9XgdT2EQGMu4hQyZhan7TeQ8XkGp4Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.21.5.tgz", + "integrity": "sha512-YvjXDqLRqPDl2dvRODYmmhz4rPeVKYvppfGYKSNGdyZkA01046pLWyRKKI3ax8fbJoK5QbxblURkwK/MWY18Tg==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-loong64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.21.5.tgz", + "integrity": "sha512-uHf1BmMG8qEvzdrzAqg2SIG/02+4/DHB6a9Kbya0XDvwDEKCoC8ZRWI5JJvNdUjtciBGFQ5PuBlpEOXQj+JQSg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-mips64el": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.21.5.tgz", + "integrity": "sha512-IajOmO+KJK23bj52dFSNCMsz1QP1DqM6cwLUv3W1QwyxkyIWecfafnI555fvSGqEKwjMXVLokcV5ygHW5b3Jbg==", + "cpu": [ + "mips64el" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-ppc64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.21.5.tgz", + "integrity": "sha512-1hHV/Z4OEfMwpLO8rp7CvlhBDnjsC3CttJXIhBi+5Aj5r+MBvy4egg7wCbe//hSsT+RvDAG7s81tAvpL2XAE4w==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-riscv64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.21.5.tgz", + "integrity": "sha512-2HdXDMd9GMgTGrPWnJzP2ALSokE/0O5HhTUvWIbD3YdjME8JwvSCnNGBnTThKGEB91OZhzrJ4qIIxk/SBmyDDA==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-s390x": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.21.5.tgz", + "integrity": "sha512-zus5sxzqBJD3eXxwvjN1yQkRepANgxE9lgOW2qLnmr8ikMTphkjgXu1HR01K4FJg8h1kEEDAqDcZQtbrRnB41A==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/linux-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.21.5.tgz", + "integrity": "sha512-1rYdTpyv03iycF1+BhzrzQJCdOuAOtaqHTWJZCWvijKD2N5Xu0TtVC8/+1faWqcP9iBCWOmjmhoH94dH82BxPQ==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/netbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.21.5.tgz", + "integrity": "sha512-Woi2MXzXjMULccIwMnLciyZH4nCIMpWQAs049KEeMvOcNADVxo0UBIQPfSmxB3CWKedngg7sWZdLvLczpe0tLg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "netbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/openbsd-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.21.5.tgz", + "integrity": "sha512-HLNNw99xsvx12lFBUwoT8EVCsSvRNDVxNpjZ7bPn947b8gJPzeHWyNVhFsaerc0n3TsbOINvRP2byTZ5LKezow==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/sunos-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.21.5.tgz", + "integrity": "sha512-6+gjmFpfy0BHU5Tpptkuh8+uw3mnrvgs+dSPQXQOv3ekbordwnzTVEb4qnIvQcYXq6gzkyTnoZ9dZG+D4garKg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "sunos" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-arm64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.21.5.tgz", + "integrity": "sha512-Z0gOTd75VvXqyq7nsl93zwahcTROgqvuAcYDUr+vOv8uHhNSKROyU961kgtCD1e95IqPKSQKH7tBTslnS3tA8A==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-ia32": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.21.5.tgz", + "integrity": "sha512-SWXFF1CL2RVNMaVs+BBClwtfZSvDgtL//G/smwAc5oVK/UPu2Gu9tIaRgFmYFFKrmg3SyAjSrElf0TiJ1v8fYA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@esbuild/win32-x64": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.21.5.tgz", + "integrity": "sha512-tQd/1efJuzPC6rCFwEvLtci/xNFcTZknmXs98FYDfGE4wP9ClFV98nyKrzJKVPMhdDnjzLhdUyMX4PsQAPjwIw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, + "node_modules/@rollup/rollup-android-arm-eabi": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.60.3.tgz", + "integrity": "sha512-x35CNW/ANXG3hE/EZpRU8MXX1JDN86hBb2wMGAtltkz7pc6cxgjpy1OMMfDosOQ+2hWqIkag/fGok1Yady9nGw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-android-arm64": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.60.3.tgz", + "integrity": "sha512-xw3xtkDApIOGayehp2+Rz4zimfkaX65r4t47iy+ymQB2G4iJCBBfj0ogVg5jpvjpn8UWn/+q9tprxleYeNp3Hw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ] + }, + "node_modules/@rollup/rollup-darwin-arm64": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.60.3.tgz", + "integrity": "sha512-vo6Y5Qfpx7/5EaamIwi0WqW2+zfiusVihKatLvtN1VFVy3D13uERk/6gZLU1UiHRL6fDXqj/ELIeVRGnvcTE1g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-darwin-x64": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.60.3.tgz", + "integrity": "sha512-D+0QGcZhBzTN82weOnsSlY7V7+RMmPuF1CkbxyMAGE8+ZHeUjyb76ZiWmBlCu//AQQONvxcqRbwZTajZKqjuOw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@rollup/rollup-freebsd-arm64": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.60.3.tgz", + "integrity": "sha512-6HnvHCT7fDyj6R0Ph7A6x8dQS/S38MClRWeDLqc0MdfWkxjiu1HSDYrdPhqSILzjTIC/pnXbbJbo+ft+gy/9hQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-freebsd-x64": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.60.3.tgz", + "integrity": "sha512-KHLgC3WKlUYW3ShFKnnosZDOJ0xjg9zp7au3sIm2bs/tGBeC2ipmvRh/N7JKi0t9Ue20C0dpEshi8WUubg+cnA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ] + }, + "node_modules/@rollup/rollup-linux-arm-gnueabihf": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.60.3.tgz", + "integrity": "sha512-DV6fJoxEYWJOvaZIsok7KrYl0tPvga5OZ2yvKHNNYyk/2roMLqQAbGhr78EQ5YhHpnhLKJD3S1WFusAkmUuV5g==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm-musleabihf": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.60.3.tgz", + "integrity": "sha512-mQKoJAzvuOs6F+TZybQO4GOTSMUu7v0WdxEk24krQ/uUxXoPTtHjuaUuPmFhtBcM4K0ons8nrE3JyhTuCFtT/w==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-gnu": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.60.3.tgz", + "integrity": "sha512-Whjj2qoiJ6+OOJMGptTYazaJvjOJm+iKHpXQM1P3LzGjt7Ff++Tp7nH4N8J/BUA7R9IHfDyx4DJIflifwnbmIA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-arm64-musl": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.60.3.tgz", + "integrity": "sha512-4YTNHKqGng5+yiZt3mg77nmyuCfmNfX4fPmyUapBcIk+BdwSwmCWGXOUxhXbBEkFHtoN5boLj/5NON+u5QC9tg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.60.3.tgz", + "integrity": "sha512-SU3kNlhkpI4UqlUc2VXPGK9o886ZsSeGfMAX2ba2b8DKmMXq4AL7KUrkSWVbb7koVqx41Yczx6dx5PNargIrEA==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.60.3.tgz", + "integrity": "sha512-6lDLl5h4TXpB1mTf2rQWnAk/LcXrx9vBfu/DT5TIPhvMhRWaZ5MxkIc8u4lJAmBo6klTe1ywXIUHFjylW505sg==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.60.3.tgz", + "integrity": "sha512-BMo8bOw8evlup/8G+cj5xWtPyp93xPdyoSN16Zy90Q2QZ0ZYRhCt6ZJSwbrRzG9HApFabjwj2p25TUPDWrhzqQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.60.3.tgz", + "integrity": "sha512-E0L8X1dZN1/Rph+5VPF6Xj2G7JJvMACVXtamTJIDrVI44Y3K+G8gQaMEAavbqCGTa16InptiVrX6eM6pmJ+7qA==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-gnu": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.60.3.tgz", + "integrity": "sha512-oZJ/WHaVfHUiRAtmTAeo3DcevNsVvH8mbvodjZy7D5QKvCefO371SiKRpxoDcCxB3PTRTLayWBkvmDQKTcX/sw==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.60.3.tgz", + "integrity": "sha512-Dhbyh7j9FybM3YaTgaHmVALwA8AkUwTPccyCQ79TG9AJUsMQqgN1DDEZNr4+QUfwiWvLDumW5vdwzoeUF+TNxQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-s390x-gnu": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.60.3.tgz", + "integrity": "sha512-cJd1X5XhHHlltkaypz1UcWLA8AcoIi1aWhsvaWDskD1oz2eKCypnqvTQ8ykMNI0RSmm7NkTdSqSSD7zM0xa6Ig==", + "cpu": [ + "s390x" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-gnu": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.60.3.tgz", + "integrity": "sha512-DAZDBHQfG2oQuhY7mc6I3/qB4LU2fQCjRvxbDwd/Jdvb9fypP4IJ4qmtu6lNjes6B531AI8cg1aKC2di97bUxA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-x64-musl": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.60.3.tgz", + "integrity": "sha512-cRxsE8c13mZOh3vP+wLDxpQBRrOHDIGOWyDL93Sy0Ga8y515fBcC2pjUfFwUe5T7tqvTvWbCpg1URM/AXdWIXA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.60.3.tgz", + "integrity": "sha512-QaWcIgRxqEdQdhJqW4DJctsH6HCmo5vHxY0krHSX4jMtOqfzC+dqDGuHM87bu4H8JBeibWx7jFz+h6/4C8wA5Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.60.3.tgz", + "integrity": "sha512-AaXwSvUi3QIPtroAUw1t5yHGIyqKEXwH54WUocFolZhpGDruJcs8c+xPNDRn4XiQsS7MEwnYsHW2l0MBLDMkWg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, + "node_modules/@rollup/rollup-win32-arm64-msvc": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.60.3.tgz", + "integrity": "sha512-65LAKM/bAWDqKNEelHlcHvm2V+Vfb8C6INFxQXRHCvaVN1rJfwr4NvdP4FyzUaLqWfaCGaadf6UbTm8xJeYfEg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-ia32-msvc": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.60.3.tgz", + "integrity": "sha512-EEM2gyhBF5MFnI6vMKdX1LAosE627RGBzIoGMdLloPZkXrUN0Ckqgr2Qi8+J3zip/8NVVro3/FjB+tjhZUgUHA==", + "cpu": [ + "ia32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.60.3.tgz", + "integrity": "sha512-E5Eb5H/DpxaoXH++Qkv28RcUJboMopmdDUALBczvHMf7hNIxaDZqwY5lK12UK1BHacSmvupoEWGu+n993Z0y1A==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@rollup/rollup-win32-x64-msvc": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.60.3.tgz", + "integrity": "sha512-hPt/bgL5cE+Qp+/TPHBqptcAgPzgj46mPcg/16zNUmbQk0j+mOEQV/+Lqu8QRtDV3Ek95Q6FeFITpuhl6OTsAA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@tailwindcss/node": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/node/-/node-4.2.4.tgz", + "integrity": "sha512-Ai7+yQPxz3ddrDQzFfBKdHEVBg0w3Zl83jnjuwxnZOsnH9pGn93QHQtpU0p/8rYWxvbFZHneni6p1BSLK4DkGA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/remapping": "^2.3.5", + "enhanced-resolve": "^5.19.0", + "jiti": "^2.6.1", + "lightningcss": "1.32.0", + "magic-string": "^0.30.21", + "source-map-js": "^1.2.1", + "tailwindcss": "4.2.4" + } + }, + "node_modules/@tailwindcss/oxide": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide/-/oxide-4.2.4.tgz", + "integrity": "sha512-9El/iI069DKDSXwTvB9J4BwdO5JhRrOweGaK25taBAvBXyXqJAX+Jqdvs8r8gKpsI/1m0LeJLyQYTf/WLrBT1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20" + }, + "optionalDependencies": { + "@tailwindcss/oxide-android-arm64": "4.2.4", + "@tailwindcss/oxide-darwin-arm64": "4.2.4", + "@tailwindcss/oxide-darwin-x64": "4.2.4", + "@tailwindcss/oxide-freebsd-x64": "4.2.4", + "@tailwindcss/oxide-linux-arm-gnueabihf": "4.2.4", + "@tailwindcss/oxide-linux-arm64-gnu": "4.2.4", + "@tailwindcss/oxide-linux-arm64-musl": "4.2.4", + "@tailwindcss/oxide-linux-x64-gnu": "4.2.4", + "@tailwindcss/oxide-linux-x64-musl": "4.2.4", + "@tailwindcss/oxide-wasm32-wasi": "4.2.4", + "@tailwindcss/oxide-win32-arm64-msvc": "4.2.4", + "@tailwindcss/oxide-win32-x64-msvc": "4.2.4" + } + }, + "node_modules/@tailwindcss/oxide-android-arm64": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-android-arm64/-/oxide-android-arm64-4.2.4.tgz", + "integrity": "sha512-e7MOr1SAn9U8KlZzPi1ZXGZHeC5anY36qjNwmZv9pOJ8E4Q6jmD1vyEHkQFmNOIN7twGPEMXRHmitN4zCMN03g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-arm64": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-arm64/-/oxide-darwin-arm64-4.2.4.tgz", + "integrity": "sha512-tSC/Kbqpz/5/o/C2sG7QvOxAKqyd10bq+ypZNf+9Fi2TvbVbv1zNpcEptcsU7DPROaSbVgUXmrzKhurFvo5eDg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-darwin-x64": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-darwin-x64/-/oxide-darwin-x64-4.2.4.tgz", + "integrity": "sha512-yPyUXn3yO/ufR6+Kzv0t4fCg2qNr90jxXc5QqBpjlPNd0NqyDXcmQb/6weunH/MEDXW5dhyEi+agTDiqa3WsGg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-freebsd-x64": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-freebsd-x64/-/oxide-freebsd-x64-4.2.4.tgz", + "integrity": "sha512-BoMIB4vMQtZsXdGLVc2z+P9DbETkiopogfWZKbWwM8b/1Vinbs4YcUwo+kM/KeLkX3Ygrf4/PsRndKaYhS8Eiw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm-gnueabihf": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm-gnueabihf/-/oxide-linux-arm-gnueabihf-4.2.4.tgz", + "integrity": "sha512-7pIHBLTHYRAlS7V22JNuTh33yLH4VElwKtB3bwchK/UaKUPpQ0lPQiOWcbm4V3WP2I6fNIJ23vABIvoy2izdwA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-gnu": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-gnu/-/oxide-linux-arm64-gnu-4.2.4.tgz", + "integrity": "sha512-+E4wxJ0ZGOzSH325reXTWB48l42i93kQqMvDyz5gqfRzRZ7faNhnmvlV4EPGJU3QJM/3Ab5jhJ5pCRUsKn6OQw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-arm64-musl": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-arm64-musl/-/oxide-linux-arm64-musl-4.2.4.tgz", + "integrity": "sha512-bBADEGAbo4ASnppIziaQJelekCxdMaxisrk+fB7Thit72IBnALp9K6ffA2G4ruj90G9XRS2VQ6q2bCKbfFV82g==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-gnu": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-gnu/-/oxide-linux-x64-gnu-4.2.4.tgz", + "integrity": "sha512-7Mx25E4WTfnht0TVRTyC00j3i0M+EeFe7wguMDTlX4mRxafznw0CA8WJkFjWYH5BlgELd1kSjuU2JiPnNZbJDA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-linux-x64-musl": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-linux-x64-musl/-/oxide-linux-x64-musl-4.2.4.tgz", + "integrity": "sha512-2wwJRF7nyhOR0hhHoChc04xngV3iS+akccHTGtz965FwF0up4b2lOdo6kI1EbDaEXKgvcrFBYcYQQ/rrnWFVfA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-wasm32-wasi": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-wasm32-wasi/-/oxide-wasm32-wasi-4.2.4.tgz", + "integrity": "sha512-FQsqApeor8Fo6gUEklzmaa9994orJZZDBAlQpK2Mq+DslRKFJeD6AjHpBQ0kZFQohVr8o85PPh8eOy86VlSCmw==", + "bundleDependencies": [ + "@napi-rs/wasm-runtime", + "@emnapi/core", + "@emnapi/runtime", + "@tybys/wasm-util", + "@emnapi/wasi-threads", + "tslib" + ], + "cpu": [ + "wasm32" + ], + "dev": true, + "license": "MIT", + "optional": true, + "dependencies": { + "@emnapi/core": "^1.8.1", + "@emnapi/runtime": "^1.8.1", + "@emnapi/wasi-threads": "^1.1.0", + "@napi-rs/wasm-runtime": "^1.1.1", + "@tybys/wasm-util": "^0.10.1", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@tailwindcss/oxide-win32-arm64-msvc": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-arm64-msvc/-/oxide-win32-arm64-msvc-4.2.4.tgz", + "integrity": "sha512-L9BXqxC4ToVgwMFqj3pmZRqyHEztulpUJzCxUtLjobMCzTPsGt1Fa9enKbOpY2iIyVtaHNeNvAK8ERP/64sqGQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/oxide-win32-x64-msvc": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/oxide-win32-x64-msvc/-/oxide-win32-x64-msvc-4.2.4.tgz", + "integrity": "sha512-ESlKG0EpVJQwRjXDDa9rLvhEAh0mhP1sF7sap9dNZT0yyl9SAG6T7gdP09EH0vIv0UNTlo6jPWyujD6559fZvw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 20" + } + }, + "node_modules/@tailwindcss/vite": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/@tailwindcss/vite/-/vite-4.2.4.tgz", + "integrity": "sha512-pCvohwOCspk3ZFn6eJzrrX3g4n2JY73H6MmYC87XfGPyTty4YsCjYTMArRZm/zOI8dIt3+EcrLHAFPe5A4bgtw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@tailwindcss/node": "4.2.4", + "@tailwindcss/oxide": "4.2.4", + "tailwindcss": "4.2.4" + }, + "peerDependencies": { + "vite": "^5.2.0 || ^6 || ^7 || ^8" + } + }, + "node_modules/@types/estree": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=8" + } + }, + "node_modules/enhanced-resolve": { + "version": "5.21.1", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.21.1.tgz", + "integrity": "sha512-8p7DUVq6XJnZEz9W4oSwiwycxBIjHjRzYb3Je3zVN+geKTRQKzAkR/K4PBExlS0090d9nshak6phMUxr3PDjmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/esbuild": { + "version": "0.21.5", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", + "integrity": "sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "bin": { + "esbuild": "bin/esbuild" + }, + "engines": { + "node": ">=12" + }, + "optionalDependencies": { + "@esbuild/aix-ppc64": "0.21.5", + "@esbuild/android-arm": "0.21.5", + "@esbuild/android-arm64": "0.21.5", + "@esbuild/android-x64": "0.21.5", + "@esbuild/darwin-arm64": "0.21.5", + "@esbuild/darwin-x64": "0.21.5", + "@esbuild/freebsd-arm64": "0.21.5", + "@esbuild/freebsd-x64": "0.21.5", + "@esbuild/linux-arm": "0.21.5", + "@esbuild/linux-arm64": "0.21.5", + "@esbuild/linux-ia32": "0.21.5", + "@esbuild/linux-loong64": "0.21.5", + "@esbuild/linux-mips64el": "0.21.5", + "@esbuild/linux-ppc64": "0.21.5", + "@esbuild/linux-riscv64": "0.21.5", + "@esbuild/linux-s390x": "0.21.5", + "@esbuild/linux-x64": "0.21.5", + "@esbuild/netbsd-x64": "0.21.5", + "@esbuild/openbsd-x64": "0.21.5", + "@esbuild/sunos-x64": "0.21.5", + "@esbuild/win32-arm64": "0.21.5", + "@esbuild/win32-ia32": "0.21.5", + "@esbuild/win32-x64": "0.21.5" + } + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/jiti": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.7.0.tgz", + "integrity": "sha512-AC/7JofJvZGrrneWNaEnJeOLUx+JlGt7tNa0wZiRPT4MY1wmfKjt2+6O2p2uz2+skll8OZZmJMNqeke7kKbNgQ==", + "dev": true, + "license": "MIT", + "bin": { + "jiti": "lib/jiti-cli.mjs" + } + }, + "node_modules/lightningcss": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss/-/lightningcss-1.32.0.tgz", + "integrity": "sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==", + "dev": true, + "license": "MPL-2.0", + "peer": true, + "dependencies": { + "detect-libc": "^2.0.3" + }, + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + }, + "optionalDependencies": { + "lightningcss-android-arm64": "1.32.0", + "lightningcss-darwin-arm64": "1.32.0", + "lightningcss-darwin-x64": "1.32.0", + "lightningcss-freebsd-x64": "1.32.0", + "lightningcss-linux-arm-gnueabihf": "1.32.0", + "lightningcss-linux-arm64-gnu": "1.32.0", + "lightningcss-linux-arm64-musl": "1.32.0", + "lightningcss-linux-x64-gnu": "1.32.0", + "lightningcss-linux-x64-musl": "1.32.0", + "lightningcss-win32-arm64-msvc": "1.32.0", + "lightningcss-win32-x64-msvc": "1.32.0" + } + }, + "node_modules/lightningcss-android-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-android-arm64/-/lightningcss-android-arm64-1.32.0.tgz", + "integrity": "sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "android" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-arm64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-arm64/-/lightningcss-darwin-arm64-1.32.0.tgz", + "integrity": "sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-darwin-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-darwin-x64/-/lightningcss-darwin-x64-1.32.0.tgz", + "integrity": "sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-freebsd-x64": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-freebsd-x64/-/lightningcss-freebsd-x64-1.32.0.tgz", + "integrity": "sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm-gnueabihf": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm-gnueabihf/-/lightningcss-linux-arm-gnueabihf-1.32.0.tgz", + "integrity": "sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-gnu/-/lightningcss-linux-arm64-gnu-1.32.0.tgz", + "integrity": "sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-arm64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-arm64-musl/-/lightningcss-linux-arm64-musl-1.32.0.tgz", + "integrity": "sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-gnu": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-gnu/-/lightningcss-linux-x64-gnu-1.32.0.tgz", + "integrity": "sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-linux-x64-musl": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-linux-x64-musl/-/lightningcss-linux-x64-musl-1.32.0.tgz", + "integrity": "sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-arm64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-arm64-msvc/-/lightningcss-win32-arm64-msvc-1.32.0.tgz", + "integrity": "sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/lightningcss-win32-x64-msvc": { + "version": "1.32.0", + "resolved": "https://registry.npmjs.org/lightningcss-win32-x64-msvc/-/lightningcss-win32-x64-msvc-1.32.0.tgz", + "integrity": "sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MPL-2.0", + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 12.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/parcel" + } + }, + "node_modules/magic-string": { + "version": "0.30.21", + "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.21.tgz", + "integrity": "sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/sourcemap-codec": "^1.5.5" + } + }, + "node_modules/nanoid": { + "version": "3.3.12", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.12.tgz", + "integrity": "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, + "node_modules/postcss": { + "version": "8.5.14", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.14.tgz", + "integrity": "sha512-SoSL4+OSEtR99LHFZQiJLkT59C5B1amGO1NzTwj7TT1qCUgUO6hxOvzkOYxD+vMrXBM3XJIKzokoERdqQq/Zmg==", + "dev": true, + "funding": [ + { + "type": "opencollective", + "url": "https://opencollective.com/postcss/" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/funding/github/npm/postcss" + }, + { + "type": "github", + "url": "https://github.com/sponsors/ai" + } + ], + "license": "MIT", + "dependencies": { + "nanoid": "^3.3.11", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" + }, + "engines": { + "node": "^10 || ^12 || >=14" + } + }, + "node_modules/rollup": { + "version": "4.60.3", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.60.3.tgz", + "integrity": "sha512-pAQK9HalE84QSm4Po3EmWIZPd3FnjkShVkiMlz1iligWYkWQ7wHYd1PF/T7QZ5TVSD6uSTon5gBVMSM4JfBV+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/estree": "1.0.8" + }, + "bin": { + "rollup": "dist/bin/rollup" + }, + "engines": { + "node": ">=18.0.0", + "npm": ">=8.0.0" + }, + "optionalDependencies": { + "@rollup/rollup-android-arm-eabi": "4.60.3", + "@rollup/rollup-android-arm64": "4.60.3", + "@rollup/rollup-darwin-arm64": "4.60.3", + "@rollup/rollup-darwin-x64": "4.60.3", + "@rollup/rollup-freebsd-arm64": "4.60.3", + "@rollup/rollup-freebsd-x64": "4.60.3", + "@rollup/rollup-linux-arm-gnueabihf": "4.60.3", + "@rollup/rollup-linux-arm-musleabihf": "4.60.3", + "@rollup/rollup-linux-arm64-gnu": "4.60.3", + "@rollup/rollup-linux-arm64-musl": "4.60.3", + "@rollup/rollup-linux-loong64-gnu": "4.60.3", + "@rollup/rollup-linux-loong64-musl": "4.60.3", + "@rollup/rollup-linux-ppc64-gnu": "4.60.3", + "@rollup/rollup-linux-ppc64-musl": "4.60.3", + "@rollup/rollup-linux-riscv64-gnu": "4.60.3", + "@rollup/rollup-linux-riscv64-musl": "4.60.3", + "@rollup/rollup-linux-s390x-gnu": "4.60.3", + "@rollup/rollup-linux-x64-gnu": "4.60.3", + "@rollup/rollup-linux-x64-musl": "4.60.3", + "@rollup/rollup-openbsd-x64": "4.60.3", + "@rollup/rollup-openharmony-arm64": "4.60.3", + "@rollup/rollup-win32-arm64-msvc": "4.60.3", + "@rollup/rollup-win32-ia32-msvc": "4.60.3", + "@rollup/rollup-win32-x64-gnu": "4.60.3", + "@rollup/rollup-win32-x64-msvc": "4.60.3", + "fsevents": "~2.3.2" + } + }, + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/tailwindcss": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.2.4.tgz", + "integrity": "sha512-HhKppgO81FQof5m6TEnuBWCZGgfRAWbaeOaGT00KOy/Pf/j6oUihdvBpA7ltCeAvZpFhW3j0PTclkxsd4IXYDA==", + "dev": true, + "license": "MIT" + }, + "node_modules/tapable": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.3.tgz", + "integrity": "sha512-uxc/zpqFg6x7C8vOE7lh6Lbda8eEL9zmVm/PLeTPBRhh1xCgdWaQ+J1CUieGpIfm2HdtsUpRv+HshiasBMcc6A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/vite": { + "version": "5.4.21", + "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", + "integrity": "sha512-o5a9xKjbtuhY6Bi5S3+HvbRERmouabWbyUcpXXUA1u+GNUKoROi9byOJ8M0nHbHYHkYICiMlqxkg1KkYmm25Sw==", + "dev": true, + "license": "MIT", + "peer": true, + "dependencies": { + "esbuild": "^0.21.3", + "postcss": "^8.4.43", + "rollup": "^4.20.0" + }, + "bin": { + "vite": "bin/vite.js" + }, + "engines": { + "node": "^18.0.0 || >=20.0.0" + }, + "funding": { + "url": "https://github.com/vitejs/vite?sponsor=1" + }, + "optionalDependencies": { + "fsevents": "~2.3.3" + }, + "peerDependencies": { + "@types/node": "^18.0.0 || >=20.0.0", + "less": "*", + "lightningcss": "^1.21.0", + "sass": "*", + "sass-embedded": "*", + "stylus": "*", + "sugarss": "*", + "terser": "^5.4.0" + }, + "peerDependenciesMeta": { + "@types/node": { + "optional": true + }, + "less": { + "optional": true + }, + "lightningcss": { + "optional": true + }, + "sass": { + "optional": true + }, + "sass-embedded": { + "optional": true + }, + "stylus": { + "optional": true + }, + "sugarss": { + "optional": true + }, + "terser": { + "optional": true + } + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..8fdaa72 --- /dev/null +++ b/package.json @@ -0,0 +1,12 @@ +{ + "type": "module", + "scripts": { + "dev": "vite build --watch", + "build": "vite build" + }, + "devDependencies": { + "vite": "^5.0", + "@tailwindcss/vite": "^4.0", + "tailwindcss": "^4.0" + } +} diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..58ad587 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,44 @@ +[project] +name = "vergabe-teilnahme" +version = "0.1.0" +description = "Ausschreibungs- und Teilnahme-Management-System" +requires-python = ">=3.12" +dependencies = [ + "django>=5.2", + "psycopg[binary]>=3.2", + "django-storages>=1.14", + "whitenoise>=6.7", + "python-decouple>=3.8", + "dj-database-url>=2.1", +] + +[dependency-groups] +dev = [ + "pytest-django>=4.8", + "pytest-cov>=5.0", + "factory-boy>=3.3", + "ruff>=0.4", + "mypy>=1.10", + "django-stubs>=5.0", +] + +[tool.pytest.ini_options] +DJANGO_SETTINGS_MODULE = "vergabe_teilnahme.settings.dev" +python_files = ["test_*.py"] +python_classes = ["Test*"] +python_functions = ["test_*"] +addopts = "--tb=short -q" + +[tool.ruff] +line-length = 100 +target-version = "py312" + +[tool.ruff.lint] +select = ["E", "F", "I", "N", "UP"] + +[tool.mypy] +python_version = "3.12" +plugins = ["mypy_django_plugin.main"] + +[tool.django-stubs] +django_settings_module = "vergabe_teilnahme.settings.dev" diff --git a/static/src/main.css b/static/src/main.css new file mode 100644 index 0000000..b042e87 --- /dev/null +++ b/static/src/main.css @@ -0,0 +1,43 @@ +@import "tailwindcss"; + +@theme { + --color-brand-50: #f0f4ff; + --color-brand-100: #dce7ff; + --color-brand-500: #3b5bdb; + --color-brand-600: #2f4ac7; + --color-brand-700: #2541b2; + --color-brand-900: #152d99; +} + +@layer base { + /* German-app base resets */ + html { + font-family: ui-sans-serif, system-ui, sans-serif; + } +} + +@layer components { + .card { @apply bg-white rounded-xl border border-slate-200 shadow-sm p-6; } + .btn-primary { @apply bg-brand-500 text-white px-4 py-2 rounded-lg hover:bg-brand-600 transition-colors; } + .btn-secondary { @apply bg-white text-slate-700 border border-slate-300 px-4 py-2 rounded-lg hover:bg-slate-50; } + .btn-danger { @apply bg-red-600 text-white px-4 py-2 rounded-lg hover:bg-red-700; } + .btn-ghost { @apply text-slate-600 px-3 py-2 rounded-lg hover:bg-slate-100; } + .field-row { @apply grid grid-cols-3 gap-4 py-3 border-b border-slate-100 last:border-0; } + .field-label { @apply text-sm font-medium text-slate-500 col-span-1; } + .field-value { @apply text-sm text-slate-900 col-span-2; } + .phase-badge { @apply inline-flex items-center justify-center w-7 h-7 rounded-full text-sm font-bold; } + .phase-todo { @apply inline-flex items-center justify-center w-7 h-7 rounded-full text-sm font-bold bg-slate-200 text-slate-500; } + .phase-active { @apply inline-flex items-center justify-center w-7 h-7 rounded-full text-sm font-bold bg-brand-500 text-white; } + .phase-done { @apply inline-flex items-center justify-center w-7 h-7 rounded-full text-sm font-bold bg-green-500 text-white; } + .phase-warn { @apply inline-flex items-center justify-center w-7 h-7 rounded-full text-sm font-bold bg-amber-400 text-amber-900; } + .section-title { @apply text-base font-semibold text-slate-900 mb-4; } + .page-title { @apply text-2xl font-bold text-slate-900; } + .form-input { @apply w-full rounded-lg border border-slate-300 px-3 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-brand-500 focus:border-transparent; } + .form-label { @apply block text-sm font-medium text-slate-700 mb-1; } + .table-base { @apply w-full text-sm text-left; } + .table-header { @apply bg-slate-50 text-slate-500 font-medium text-xs uppercase tracking-wide; } + .table-row { @apply border-t border-slate-100 hover:bg-slate-50 transition-colors; } + .sidebar-link { @apply flex items-center px-3 py-2 rounded-lg text-sm text-slate-700 hover:bg-slate-100 transition-colors; } + .sidebar-link-active { @apply bg-brand-50 text-brand-700 font-medium; } + .sidebar-section-btn { @apply w-full flex items-center justify-between px-3 py-2 text-xs font-semibold text-slate-500 uppercase tracking-wide hover:text-slate-700; } +} diff --git a/static/vendor/alpinejs/alpine.min.js b/static/vendor/alpinejs/alpine.min.js new file mode 100644 index 0000000..2fdd6ec --- /dev/null +++ b/static/vendor/alpinejs/alpine.min.js @@ -0,0 +1,5 @@ +(()=>{var nt=!1,it=!1,W=[],ot=-1;function Ut(e){Rn(e)}function Rn(e){W.includes(e)||W.push(e),Mn()}function Wt(e){let t=W.indexOf(e);t!==-1&&t>ot&&W.splice(t,1)}function Mn(){!it&&!nt&&(nt=!0,queueMicrotask(Nn))}function Nn(){nt=!1,it=!0;for(let e=0;ee.effect(t,{scheduler:r=>{st?Ut(r):r()}}),at=e.raw}function ct(e){N=e}function Yt(e){let t=()=>{};return[n=>{let i=N(n);return e._x_effects||(e._x_effects=new Set,e._x_runEffects=()=>{e._x_effects.forEach(o=>o())}),e._x_effects.add(i),t=()=>{i!==void 0&&(e._x_effects.delete(i),$(i))},i},()=>{t()}]}function ve(e,t){let r=!0,n,i=N(()=>{let o=e();JSON.stringify(o),r?n=o:queueMicrotask(()=>{t(o,n),n=o}),r=!1});return()=>$(i)}var Xt=[],Zt=[],Qt=[];function er(e){Qt.push(e)}function te(e,t){typeof t=="function"?(e._x_cleanups||(e._x_cleanups=[]),e._x_cleanups.push(t)):(t=e,Zt.push(t))}function Ae(e){Xt.push(e)}function Oe(e,t,r){e._x_attributeCleanups||(e._x_attributeCleanups={}),e._x_attributeCleanups[t]||(e._x_attributeCleanups[t]=[]),e._x_attributeCleanups[t].push(r)}function lt(e,t){e._x_attributeCleanups&&Object.entries(e._x_attributeCleanups).forEach(([r,n])=>{(t===void 0||t.includes(r))&&(n.forEach(i=>i()),delete e._x_attributeCleanups[r])})}function tr(e){for(e._x_effects?.forEach(Wt);e._x_cleanups?.length;)e._x_cleanups.pop()()}var ut=new MutationObserver(mt),ft=!1;function ue(){ut.observe(document,{subtree:!0,childList:!0,attributes:!0,attributeOldValue:!0}),ft=!0}function dt(){kn(),ut.disconnect(),ft=!1}var le=[];function kn(){let e=ut.takeRecords();le.push(()=>e.length>0&&mt(e));let t=le.length;queueMicrotask(()=>{if(le.length===t)for(;le.length>0;)le.shift()()})}function m(e){if(!ft)return e();dt();let t=e();return ue(),t}var pt=!1,Se=[];function rr(){pt=!0}function nr(){pt=!1,mt(Se),Se=[]}function mt(e){if(pt){Se=Se.concat(e);return}let t=[],r=new Set,n=new Map,i=new Map;for(let o=0;o{s.nodeType===1&&s._x_marker&&r.add(s)}),e[o].addedNodes.forEach(s=>{if(s.nodeType===1){if(r.has(s)){r.delete(s);return}s._x_marker||t.push(s)}})),e[o].type==="attributes")){let s=e[o].target,a=e[o].attributeName,c=e[o].oldValue,l=()=>{n.has(s)||n.set(s,[]),n.get(s).push({name:a,value:s.getAttribute(a)})},u=()=>{i.has(s)||i.set(s,[]),i.get(s).push(a)};s.hasAttribute(a)&&c===null?l():s.hasAttribute(a)?(u(),l()):u()}i.forEach((o,s)=>{lt(s,o)}),n.forEach((o,s)=>{Xt.forEach(a=>a(s,o))});for(let o of r)t.some(s=>s.contains(o))||Zt.forEach(s=>s(o));for(let o of t)o.isConnected&&Qt.forEach(s=>s(o));t=null,r=null,n=null,i=null}function Ce(e){return z(B(e))}function k(e,t,r){return e._x_dataStack=[t,...B(r||e)],()=>{e._x_dataStack=e._x_dataStack.filter(n=>n!==t)}}function B(e){return e._x_dataStack?e._x_dataStack:typeof ShadowRoot=="function"&&e instanceof ShadowRoot?B(e.host):e.parentNode?B(e.parentNode):[]}function z(e){return new Proxy({objects:e},Dn)}var Dn={ownKeys({objects:e}){return Array.from(new Set(e.flatMap(t=>Object.keys(t))))},has({objects:e},t){return t==Symbol.unscopables?!1:e.some(r=>Object.prototype.hasOwnProperty.call(r,t)||Reflect.has(r,t))},get({objects:e},t,r){return t=="toJSON"?Pn:Reflect.get(e.find(n=>Reflect.has(n,t))||{},t,r)},set({objects:e},t,r,n){let i=e.find(s=>Object.prototype.hasOwnProperty.call(s,t))||e[e.length-1],o=Object.getOwnPropertyDescriptor(i,t);return o?.set&&o?.get?o.set.call(n,r)||!0:Reflect.set(i,t,r)}};function Pn(){return Reflect.ownKeys(this).reduce((t,r)=>(t[r]=Reflect.get(this,r),t),{})}function Te(e){let t=n=>typeof n=="object"&&!Array.isArray(n)&&n!==null,r=(n,i="")=>{Object.entries(Object.getOwnPropertyDescriptors(n)).forEach(([o,{value:s,enumerable:a}])=>{if(a===!1||s===void 0||typeof s=="object"&&s!==null&&s.__v_skip)return;let c=i===""?o:`${i}.${o}`;typeof s=="object"&&s!==null&&s._x_interceptor?n[o]=s.initialize(e,c,o):t(s)&&s!==n&&!(s instanceof Element)&&r(s,c)})};return r(e)}function Re(e,t=()=>{}){let r={initialValue:void 0,_x_interceptor:!0,initialize(n,i,o){return e(this.initialValue,()=>In(n,i),s=>ht(n,i,s),i,o)}};return t(r),n=>{if(typeof n=="object"&&n!==null&&n._x_interceptor){let i=r.initialize.bind(r);r.initialize=(o,s,a)=>{let c=n.initialize(o,s,a);return r.initialValue=c,i(o,s,a)}}else r.initialValue=n;return r}}function In(e,t){return t.split(".").reduce((r,n)=>r[n],e)}function ht(e,t,r){if(typeof t=="string"&&(t=t.split(".")),t.length===1)e[t[0]]=r;else{if(t.length===0)throw error;return e[t[0]]||(e[t[0]]={}),ht(e[t[0]],t.slice(1),r)}}var ir={};function y(e,t){ir[e]=t}function fe(e,t){let r=Ln(t);return Object.entries(ir).forEach(([n,i])=>{Object.defineProperty(e,`$${n}`,{get(){return i(t,r)},enumerable:!1})}),e}function Ln(e){let[t,r]=_t(e),n={interceptor:Re,...t};return te(e,r),n}function or(e,t,r,...n){try{return r(...n)}catch(i){re(i,e,t)}}function re(e,t,r=void 0){e=Object.assign(e??{message:"No error message given."},{el:t,expression:r}),console.warn(`Alpine Expression Error: ${e.message} + +${r?'Expression: "'+r+`" + +`:""}`,t),setTimeout(()=>{throw e},0)}var Me=!0;function ke(e){let t=Me;Me=!1;let r=e();return Me=t,r}function R(e,t,r={}){let n;return x(e,t)(i=>n=i,r),n}function x(...e){return sr(...e)}var sr=xt;function ar(e){sr=e}function xt(e,t){let r={};fe(r,e);let n=[r,...B(e)],i=typeof t=="function"?$n(n,t):Fn(n,t,e);return or.bind(null,e,t,i)}function $n(e,t){return(r=()=>{},{scope:n={},params:i=[]}={})=>{let o=t.apply(z([n,...e]),i);Ne(r,o)}}var gt={};function jn(e,t){if(gt[e])return gt[e];let r=Object.getPrototypeOf(async function(){}).constructor,n=/^[\n\s]*if.*\(.*\)/.test(e.trim())||/^(let|const)\s/.test(e.trim())?`(async()=>{ ${e} })()`:e,o=(()=>{try{let s=new r(["__self","scope"],`with (scope) { __self.result = ${n} }; __self.finished = true; return __self.result;`);return Object.defineProperty(s,"name",{value:`[Alpine] ${e}`}),s}catch(s){return re(s,t,e),Promise.resolve()}})();return gt[e]=o,o}function Fn(e,t,r){let n=jn(t,r);return(i=()=>{},{scope:o={},params:s=[]}={})=>{n.result=void 0,n.finished=!1;let a=z([o,...e]);if(typeof n=="function"){let c=n(n,a).catch(l=>re(l,r,t));n.finished?(Ne(i,n.result,a,s,r),n.result=void 0):c.then(l=>{Ne(i,l,a,s,r)}).catch(l=>re(l,r,t)).finally(()=>n.result=void 0)}}}function Ne(e,t,r,n,i){if(Me&&typeof t=="function"){let o=t.apply(r,n);o instanceof Promise?o.then(s=>Ne(e,s,r,n)).catch(s=>re(s,i,t)):e(o)}else typeof t=="object"&&t instanceof Promise?t.then(o=>e(o)):e(t)}var wt="x-";function C(e=""){return wt+e}function cr(e){wt=e}var De={};function d(e,t){return De[e]=t,{before(r){if(!De[r]){console.warn(String.raw`Cannot find directive \`${r}\`. \`${e}\` will use the default order of execution`);return}let n=G.indexOf(r);G.splice(n>=0?n:G.indexOf("DEFAULT"),0,e)}}}function lr(e){return Object.keys(De).includes(e)}function pe(e,t,r){if(t=Array.from(t),e._x_virtualDirectives){let o=Object.entries(e._x_virtualDirectives).map(([a,c])=>({name:a,value:c})),s=Et(o);o=o.map(a=>s.find(c=>c.name===a.name)?{name:`x-bind:${a.name}`,value:`"${a.value}"`}:a),t=t.concat(o)}let n={};return t.map(dr((o,s)=>n[o]=s)).filter(mr).map(zn(n,r)).sort(Kn).map(o=>Bn(e,o))}function Et(e){return Array.from(e).map(dr()).filter(t=>!mr(t))}var yt=!1,de=new Map,ur=Symbol();function fr(e){yt=!0;let t=Symbol();ur=t,de.set(t,[]);let r=()=>{for(;de.get(t).length;)de.get(t).shift()();de.delete(t)},n=()=>{yt=!1,r()};e(r),n()}function _t(e){let t=[],r=a=>t.push(a),[n,i]=Yt(e);return t.push(i),[{Alpine:K,effect:n,cleanup:r,evaluateLater:x.bind(x,e),evaluate:R.bind(R,e)},()=>t.forEach(a=>a())]}function Bn(e,t){let r=()=>{},n=De[t.type]||r,[i,o]=_t(e);Oe(e,t.original,o);let s=()=>{e._x_ignore||e._x_ignoreSelf||(n.inline&&n.inline(e,t,i),n=n.bind(n,e,t,i),yt?de.get(ur).push(n):n())};return s.runCleanups=o,s}var Pe=(e,t)=>({name:r,value:n})=>(r.startsWith(e)&&(r=r.replace(e,t)),{name:r,value:n}),Ie=e=>e;function dr(e=()=>{}){return({name:t,value:r})=>{let{name:n,value:i}=pr.reduce((o,s)=>s(o),{name:t,value:r});return n!==t&&e(n,t),{name:n,value:i}}}var pr=[];function ne(e){pr.push(e)}function mr({name:e}){return hr().test(e)}var hr=()=>new RegExp(`^${wt}([^:^.]+)\\b`);function zn(e,t){return({name:r,value:n})=>{let i=r.match(hr()),o=r.match(/:([a-zA-Z0-9\-_:]+)/),s=r.match(/\.[^.\]]+(?=[^\]]*$)/g)||[],a=t||e[r]||r;return{type:i?i[1]:null,value:o?o[1]:null,modifiers:s.map(c=>c.replace(".","")),expression:n,original:a}}}var bt="DEFAULT",G=["ignore","ref","data","id","anchor","bind","init","for","model","modelable","transition","show","if",bt,"teleport"];function Kn(e,t){let r=G.indexOf(e.type)===-1?bt:e.type,n=G.indexOf(t.type)===-1?bt:t.type;return G.indexOf(r)-G.indexOf(n)}function J(e,t,r={}){e.dispatchEvent(new CustomEvent(t,{detail:r,bubbles:!0,composed:!0,cancelable:!0}))}function D(e,t){if(typeof ShadowRoot=="function"&&e instanceof ShadowRoot){Array.from(e.children).forEach(i=>D(i,t));return}let r=!1;if(t(e,()=>r=!0),r)return;let n=e.firstElementChild;for(;n;)D(n,t,!1),n=n.nextElementSibling}function E(e,...t){console.warn(`Alpine Warning: ${e}`,...t)}var _r=!1;function gr(){_r&&E("Alpine has already been initialized on this page. Calling Alpine.start() more than once can cause problems."),_r=!0,document.body||E("Unable to initialize. Trying to load Alpine before `` is available. Did you forget to add `defer` in Alpine's ` + + + {% include "partials/topbar.html" %} + +
+ {% include "partials/sidebar.html" %} + +
+ {% include "partials/breadcrumb.html" %} + {% block content %}{% endblock %} +
+
+ + {% include "partials/feedback_button.html" %} + + + + {% block extra_js %}{% endblock %} + + diff --git a/vergabe_teilnahme/templates/errors/404.html b/vergabe_teilnahme/templates/errors/404.html new file mode 100644 index 0000000..23a62d1 --- /dev/null +++ b/vergabe_teilnahme/templates/errors/404.html @@ -0,0 +1,10 @@ +{% extends "base.html" %} +{% block title %}Seite nicht gefunden{% endblock %} +{% block content %} +
+

404

+

Seite nicht gefunden

+

Die angeforderte Seite existiert nicht oder wurde verschoben.

+ Zur Übersicht +
+{% endblock %} diff --git a/vergabe_teilnahme/templates/errors/500.html b/vergabe_teilnahme/templates/errors/500.html new file mode 100644 index 0000000..eb063de --- /dev/null +++ b/vergabe_teilnahme/templates/errors/500.html @@ -0,0 +1,10 @@ +{% extends "base.html" %} +{% block title %}Serverfehler{% endblock %} +{% block content %} +
+

500

+

Interner Serverfehler

+

Ein unerwarteter Fehler ist aufgetreten. Bitte versuche es später erneut.

+ Zur Übersicht +
+{% endblock %} diff --git a/vergabe_teilnahme/templates/partials/breadcrumb.html b/vergabe_teilnahme/templates/partials/breadcrumb.html new file mode 100644 index 0000000..975671f --- /dev/null +++ b/vergabe_teilnahme/templates/partials/breadcrumb.html @@ -0,0 +1,12 @@ +{% if breadcrumbs %} + +{% endif %} diff --git a/vergabe_teilnahme/templates/partials/feedback_button.html b/vergabe_teilnahme/templates/partials/feedback_button.html new file mode 100644 index 0000000..2969bf5 --- /dev/null +++ b/vergabe_teilnahme/templates/partials/feedback_button.html @@ -0,0 +1,8 @@ + diff --git a/vergabe_teilnahme/templates/partials/feedback_modal.html b/vergabe_teilnahme/templates/partials/feedback_modal.html new file mode 100644 index 0000000..8bf104b --- /dev/null +++ b/vergabe_teilnahme/templates/partials/feedback_modal.html @@ -0,0 +1,46 @@ +
+
+

Feedback

+
+ {% csrf_token %} + + {% if current_ausschreibung %} + + {% endif %} +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ + +
+
+
+
diff --git a/vergabe_teilnahme/templates/partials/field_row.html b/vergabe_teilnahme/templates/partials/field_row.html new file mode 100644 index 0000000..c8a6ab0 --- /dev/null +++ b/vergabe_teilnahme/templates/partials/field_row.html @@ -0,0 +1,8 @@ +{% if not hidden %} +
+
{{ label }}
+
+ {% if value %}{{ value }}{% else %}{% endif %} +
+
+{% endif %} diff --git a/vergabe_teilnahme/templates/partials/phase_nav.html b/vergabe_teilnahme/templates/partials/phase_nav.html new file mode 100644 index 0000000..17f0e46 --- /dev/null +++ b/vergabe_teilnahme/templates/partials/phase_nav.html @@ -0,0 +1,21 @@ +{% load vergabe_tags %} +
+

+ {{ current_ausschreibung.titel|truncatechars:30 }} +

+ {% for phase in phases %} + + + {{ phase.nummer }} + + {{ phase.name }} + {% if phase.warnung %} + + {% endif %} + + {% endfor %} +
diff --git a/vergabe_teilnahme/templates/partials/sidebar.html b/vergabe_teilnahme/templates/partials/sidebar.html new file mode 100644 index 0000000..f76c28c --- /dev/null +++ b/vergabe_teilnahme/templates/partials/sidebar.html @@ -0,0 +1,59 @@ + diff --git a/vergabe_teilnahme/templates/partials/status_badge.html b/vergabe_teilnahme/templates/partials/status_badge.html new file mode 100644 index 0000000..5ce0c94 --- /dev/null +++ b/vergabe_teilnahme/templates/partials/status_badge.html @@ -0,0 +1 @@ +{{ label }} diff --git a/vergabe_teilnahme/templates/partials/topbar.html b/vergabe_teilnahme/templates/partials/topbar.html new file mode 100644 index 0000000..cb34d4f --- /dev/null +++ b/vergabe_teilnahme/templates/partials/topbar.html @@ -0,0 +1,46 @@ +{% load static %} +
+ + + Vergabe Teilnahme + +
+ + + + + +
+ +
+ + +
+
+ {{ request.user.get_rolle_display|default:"Mitarbeiter" }} +
+ Abmelden +
+
+
diff --git a/vergabe_teilnahme/urls.py b/vergabe_teilnahme/urls.py new file mode 100644 index 0000000..02deb1f --- /dev/null +++ b/vergabe_teilnahme/urls.py @@ -0,0 +1,40 @@ +from django.contrib import admin +from django.conf import settings +from django.conf.urls.static import static +from django.http import JsonResponse +from django.shortcuts import redirect +from django.urls import include, path + +from vergabe_teilnahme.apps.core import views as core_views + + +def health(request): + return JsonResponse({'status': 'ok'}) + + +def home(request): + return redirect('ausschreibungen:dashboard') + + +handler404 = 'vergabe_teilnahme.apps.core.views.custom_404' +handler500 = 'vergabe_teilnahme.apps.core.views.custom_500' + +urlpatterns = [ + path('admin/', admin.site.urls), + path('health/', health), + path('', home, name='home'), + path('ausschreibungen/', include('vergabe_teilnahme.apps.ausschreibungen.urls', namespace='ausschreibungen')), + path('lose/', include('vergabe_teilnahme.apps.lose.urls')), + path('aufgaben/', include('vergabe_teilnahme.apps.aufgaben.urls')), + path('dokumente/', include('vergabe_teilnahme.apps.dokumente.urls')), + path('preise/', include('vergabe_teilnahme.apps.preise.urls')), + path('partner/', include('vergabe_teilnahme.apps.partner.urls')), + path('bibliothek/', include('vergabe_teilnahme.apps.bibliothek.urls')), + path('marktbegleiter/', include('vergabe_teilnahme.apps.marktbegleiter.urls')), + path('nachbetrachtung/', include('vergabe_teilnahme.apps.nachbetrachtung.urls')), + path('feedback/', include('vergabe_teilnahme.apps.feedback.urls', namespace='feedback')), + path('suche/', core_views.suche, name='suche'), +] + +if settings.DEBUG: + urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) diff --git a/vergabe_teilnahme/wsgi.py b/vergabe_teilnahme/wsgi.py new file mode 100644 index 0000000..5b689d1 --- /dev/null +++ b/vergabe_teilnahme/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for vergabe_teilnahme project. + +It exposes the WSGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/6.0/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'vergabe_teilnahme.settings.dev') + +application = get_wsgi_application() diff --git a/vite.config.js b/vite.config.js new file mode 100644 index 0000000..e2ae93b --- /dev/null +++ b/vite.config.js @@ -0,0 +1,13 @@ +import { defineConfig } from 'vite' +import tailwindcss from '@tailwindcss/vite' + +export default defineConfig({ + plugins: [tailwindcss()], + build: { + outDir: 'static/dist', + emptyOutDir: true, + rollupOptions: { + input: 'static/src/main.css', + }, + }, +}) diff --git a/workplans/WP-0001-projektgeruest.md b/workplans/WP-0001-projektgeruest.md index a996535..c96f37d 100644 --- a/workplans/WP-0001-projektgeruest.md +++ b/workplans/WP-0001-projektgeruest.md @@ -1,7 +1,7 @@ --- id: WP-0001 title: Projektgerüst — Django-Setup, Tailwind, Dev-Stack -status: todo +status: done phase: 1-of-12 created: "2026-05-08" --- @@ -20,7 +20,7 @@ für PostgreSQL, pytest-django, Makefile. ```task id: WP-0001-T01 title: pyproject.toml und uv-Projektstruktur anlegen -status: todo +status: done Erstelle `pyproject.toml` mit uv als Package-Manager. @@ -41,7 +41,7 @@ Führe `uv sync` aus und bestätige, dass die virtuelle Umgebung erstellt wird. ```task id: WP-0001-T02 title: Django-Projekt initialisieren und Settings-Struktur anlegen -status: todo +status: done Führe `uv run django-admin startproject vergabe_teilnahme .` aus (Punkt am Ende — kein verschachteltes Projektverzeichnis). @@ -63,7 +63,7 @@ Passe `manage.py` und `vergabe_teilnahme/wsgi.py` auf ```task id: WP-0001-T03 title: .env.example und PostgreSQL-Konfiguration -status: todo +status: done Erstelle `.env.example`: ``` @@ -85,7 +85,7 @@ Prüfe dass `.env` und `*.sqlite3` in `.gitignore` enthalten sind. ```task id: WP-0001-T04 title: Alle Django-Apps anlegen -status: todo +status: done Erstelle folgende Apps mit `uv run manage.py startapp `: core, accounts, ausschreibungen, lose, aufgaben, @@ -105,7 +105,7 @@ Stelle sicher, dass `django.contrib.contenttypes` ebenfalls in INSTALLED_APPS is ```task id: WP-0001-T05 title: Tailwind CSS v4 via Vite integrieren -status: todo +status: done Erstelle `package.json` im Projektwurzelverzeichnis: ```json @@ -182,7 +182,7 @@ Führe `npm install` aus. ```task id: WP-0001-T06 title: HTMX und Alpine.js einbinden -status: todo +status: done Lade HTMX und Alpine.js als lokale Vendor-Dateien (keine CDN-Abhängigkeit): - `static/vendor/htmx/htmx.min.js` — HTMX 2.x (von unpkg herunterladen) @@ -201,7 +201,7 @@ Prüfe: `ls static/vendor/htmx/` und `ls static/vendor/alpinejs/` sollten die Da ```task id: WP-0001-T07 title: Docker Compose für Entwicklungs-PostgreSQL -status: todo +status: done Erstelle `docker-compose.dev.yml`: ```yaml @@ -230,7 +230,7 @@ Prüfe Verbindung: `uv run manage.py check --database default` ```task id: WP-0001-T08 title: pytest-django konfigurieren -status: todo +status: done Füge in `pyproject.toml` hinzu: ```toml @@ -264,7 +264,7 @@ Prüfe: `uv run pytest --co -q` (keine Tests vorhanden, aber Konfiguration valid ```task id: WP-0001-T09 title: Makefile für häufige Dev-Commands -status: todo +status: done Erstelle `Makefile` im Projektwurzel: ```makefile @@ -307,7 +307,7 @@ Prüfe: `make db` startet PostgreSQL, `make migrate` läuft fehlerfrei durch ```task id: WP-0001-T10 title: Django URL-Grundkonfiguration und Health-Check -status: todo +status: done Editiere `vergabe_teilnahme/urls.py`: ```python @@ -343,7 +343,7 @@ gibt `{"status": "ok"}` zurück. ```task id: WP-0001-T11 title: CLAUDE.md mit Build-Commands aktualisieren -status: todo +status: done Aktualisiere `/home/worsch/vergabe-teilnahme/CLAUDE.md` um einen Abschnitt "## Entwicklungs-Commands": @@ -389,7 +389,7 @@ Füge außerdem `static/dist/` zu `.gitignore` hinzu. ```task id: WP-0001-T12 title: Erstes `uv run manage.py migrate` und Smoke-Test -status: todo +status: done Führe die gesamte initiale Setup-Sequenz durch und verifiziere: diff --git a/workplans/WP-0002-fachmodelle.md b/workplans/WP-0002-fachmodelle.md index 9a34623..ff9e927 100644 --- a/workplans/WP-0002-fachmodelle.md +++ b/workplans/WP-0002-fachmodelle.md @@ -1,7 +1,7 @@ --- id: WP-0002 title: Fachmodelle — alle Django-Models, Migrationen, Admin -status: todo +status: done phase: 2-of-12 created: "2026-05-08" depends_on: WP-0001 @@ -21,7 +21,7 @@ und ein Management-Command für Seed-Daten. ```task id: WP-0002-T01 title: Accounts-App: Mitarbeiter-Modell (AbstractUser) -status: todo +status: done Datei: `vergabe_teilnahme/apps/accounts/models.py` @@ -55,7 +55,7 @@ Setze `AUTH_USER_MODEL = 'accounts.Mitarbeiter'` in `settings/base.py` (bereits ```task id: WP-0002-T02 title: Core-App: FlexibleModel-Mixin und Basis-Infrastruktur -status: todo +status: done Datei: `vergabe_teilnahme/apps/core/models.py` @@ -104,7 +104,7 @@ Alle vier in `core/admin.py` registrieren. ```task id: WP-0002-T03 title: Core-App: services.py mit Utility-Funktionen -status: todo +status: done Datei: `vergabe_teilnahme/apps/core/services.py` @@ -159,7 +159,7 @@ Schreibe Tests in `vergabe_teilnahme/apps/core/tests/test_services.py`: ```task id: WP-0002-T04 title: Ausschreibungen-App: Ausschreibung-Modell -status: todo +status: done Datei: `vergabe_teilnahme/apps/ausschreibungen/models.py` @@ -183,7 +183,7 @@ Admin-Registrierung mit list_display=['titel', 'ausschreiber', 'status', 'abgabe ```task id: WP-0002-T05 title: Lose-App: Los- und Anforderung-Modell -status: todo +status: done Datei: `vergabe_teilnahme/apps/lose/models.py` @@ -215,7 +215,7 @@ Admin für beide Modelle. ```task id: WP-0002-T06 title: Aufgaben-App: Aufgabe- und Bieterfrage-Modell -status: todo +status: done Datei: `vergabe_teilnahme/apps/aufgaben/models.py` @@ -242,7 +242,7 @@ Admin für beide Modelle. ```task id: WP-0002-T07 title: Dokumente-App: Dokument-Modell mit Datei-Upload -status: todo +status: done Datei: `vergabe_teilnahme/apps/dokumente/models.py` @@ -270,7 +270,7 @@ Admin-Registrierung. ```task id: WP-0002-T08 title: Preise-App: Preispunkt-Modell -status: todo +status: done Datei: `vergabe_teilnahme/apps/preise/models.py` @@ -303,7 +303,7 @@ Admin mit list_display=['konkrete_leistung', 'leistungstyp', 'einzelpreis', 'ver ```task id: WP-0002-T09 title: Partner-App: Subunternehmer und Dienstleistertyp -status: todo +status: done Datei: `vergabe_teilnahme/apps/partner/models.py` @@ -336,7 +336,7 @@ Admin für alle drei Modelle. ```task id: WP-0002-T10 title: Bibliothek-App: Nachweis, Referenz, Leistungsblatt, Entscheidungsregel -status: todo +status: done Datei: `vergabe_teilnahme/apps/bibliothek/models.py` @@ -375,7 +375,7 @@ Admin für alle vier Modelle. ```task id: WP-0002-T11 title: Marktbegleiter-App: Marktbegleiter und Ausschreibungspassage -status: todo +status: done Datei: `vergabe_teilnahme/apps/marktbegleiter/models.py` @@ -415,7 +415,7 @@ Admin für beide Modelle. ```task id: WP-0002-T12 title: Nachbetrachtung- und Feedback-Modelle -status: todo +status: done Datei: `vergabe_teilnahme/apps/nachbetrachtung/models.py` @@ -453,7 +453,7 @@ Admin für beide Modelle. ```task id: WP-0002-T13 title: Alle Migrationen generieren und ausführen -status: todo +status: done Führe aus (in dieser Reihenfolge, da Apps voneinander abhängen): ```bash @@ -482,7 +482,7 @@ Prüfe: `uv run manage.py showmigrations` → alle Migrationen als [X] markiert. ```task id: WP-0002-T14 title: Management-Command für Entwicklungs-Seed-Daten -status: todo +status: done Erstelle `vergabe_teilnahme/apps/core/management/commands/seed_dev.py` diff --git a/workplans/WP-0003-basis-ui.md b/workplans/WP-0003-basis-ui.md index 95365a7..1c7294d 100644 --- a/workplans/WP-0003-basis-ui.md +++ b/workplans/WP-0003-basis-ui.md @@ -1,7 +1,7 @@ --- id: WP-0003 title: Basis-UI — Shell-Layout, Templates, Template-Tags, Navigation -status: todo +status: done phase: 3-of-12 created: "2026-05-08" depends_on: WP-0002 @@ -24,7 +24,7 @@ Tailwind-Klassen aus WP-0001 (`static/src/main.css`) werden hier genutzt. ```task id: WP-0003-T01 title: Template-Verzeichnisstruktur und base.html Shell-Layout -status: todo +status: done Erstelle Verzeichnisstruktur: ``` @@ -94,7 +94,7 @@ TEMPLATES = [{ ```task id: WP-0003-T02 title: Topbar-Partial -status: todo +status: done `vergabe_teilnahme/templates/partials/topbar.html`: @@ -120,7 +120,7 @@ Topbar-Höhe: `h-14` (56px), `bg-white border-b border-slate-200`. ```task id: WP-0003-T03 title: Sidebar globale Navigation -status: todo +status: done `vergabe_teilnahme/templates/partials/sidebar.html`: @@ -175,7 +175,7 @@ Füge CSS-Hilfsklassen in `static/src/main.css` hinzu: ```task id: WP-0003-T04 title: Context-Processor und Phasen-Navigator-Partial -status: todo +status: done **Context-Processor** `vergabe_teilnahme/apps/core/context_processors.py`: ```python @@ -223,7 +223,7 @@ Implementiere diese Funktion in `core/services.py`. ```task id: WP-0003-T05 title: Breadcrumb-Partial -status: todo +status: done `vergabe_teilnahme/templates/partials/breadcrumb.html`: @@ -252,7 +252,7 @@ Erstelle eine Hilfsfunktion `core.views_helpers.make_breadcrumbs(*args)` die die ```task id: WP-0003-T06 title: Template-Tags: status_badge und phase_badge -status: todo +status: done Erstelle `vergabe_teilnahme/apps/core/templatetags/__init__.py` (leer). Erstelle `vergabe_teilnahme/apps/core/templatetags/vergabe_tags.py`. @@ -306,7 +306,7 @@ Erstelle `partials/status_badge.html`: ```task id: WP-0003-T07 title: Template-Tag: render_field (EntityFieldConfig-aware) -status: todo +status: done Ergänze `vergabe_tags.py`: @@ -359,7 +359,7 @@ da Admin-Änderungen sofort wirken sollen — entscheide dich für kein Caching ```task id: WP-0003-T08 title: Feedback-Button und Feedback-Modal-Partial -status: todo +status: done `partials/feedback_button.html`: ```html @@ -419,7 +419,7 @@ Verkable URLs in `feedback/urls.py` und include in Haupt-URLs. ```task id: WP-0003-T09 title: Error-Templates und Django-URL-Konfiguration -status: todo +status: done `vergabe_teilnahme/templates/errors/404.html`: ```html @@ -464,7 +464,7 @@ path('lose/', include('vergabe_teilnahme.apps.lose.urls')), ```task id: WP-0003-T10 title: Einfache Startseite mit Redirect und Smoke-Test -status: todo +status: done Erstelle `core/views.py` mit einer einfachen Redirect-View auf das Dashboard: ```python