Compare commits
145 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f95e87b450 | ||
|
|
1de949a08e | ||
|
|
af4bc5b282 | ||
|
|
77a71d8f6c | ||
|
|
946a991176 | ||
|
|
38386ed830 | ||
|
|
0b01a76953 | ||
|
|
462fa79592 | ||
|
|
5df588e8e9 | ||
|
|
5cca857358 | ||
|
|
882d2d2f60 | ||
|
|
1ef0b5e15a | ||
|
|
5f97e1f37e | ||
|
|
4aa0d125e7 | ||
|
|
ecedaaa716 | ||
|
|
817f0075d1 | ||
|
|
944a632495 | ||
|
|
950bd95c2a | ||
|
|
4381578205 | ||
|
|
d4793af2a4 | ||
|
|
6f192e816d | ||
|
|
4198669b94 | ||
|
|
2a6e02f395 | ||
|
|
fec28de07f | ||
|
|
c41cfe43f0 | ||
|
|
762c0aeb97 | ||
|
|
c010bbaff0 | ||
|
|
6e3f749b07 | ||
|
|
e4ef1230dd | ||
|
|
a7bdd02652 | ||
|
|
aa60e63d1b | ||
|
|
6b5ae04e36 | ||
|
|
634bef971d | ||
|
|
2b76953e68 | ||
|
|
192396933f | ||
|
|
82f8cfc0ff | ||
|
|
c2e96aad74 | ||
|
|
2d7ea57f57 | ||
|
|
9e6e893c28 | ||
|
|
f71c4b9ef9 | ||
|
|
ce8d4bde18 | ||
|
|
4e54511fe0 | ||
|
|
a1a286b67f | ||
|
|
fe4b4fb53a | ||
|
|
b4f84d7ecd | ||
|
|
9157df7a1c | ||
|
|
ef7086a08a | ||
|
|
15d57f0a2c | ||
|
|
82993ce74d | ||
|
|
76fa34bf64 | ||
|
|
d31c8c2fb2 | ||
|
|
4f5b94cbd7 | ||
|
|
db4325ad2c | ||
|
|
1d2f0a7ce8 | ||
|
|
3fc7196652 | ||
|
|
a7bd398f39 | ||
|
|
cd4e021332 | ||
|
|
9c897bd1e3 | ||
|
|
addb647618 | ||
|
|
8d07486bed | ||
|
|
208ade8219 | ||
|
|
2cc44d2c09 | ||
|
|
366e813f29 | ||
|
|
5683596e09 | ||
|
|
811d574576 | ||
|
|
4df30004de | ||
|
|
55a0248de4 | ||
|
|
b3c84e1cb6 | ||
|
|
7b7a1f77a0 | ||
|
|
24da37aa87 | ||
|
|
2fdd71f6d6 | ||
|
|
648ecc5901 | ||
|
|
f0e2bd8096 | ||
|
|
10591444a8 | ||
|
|
6c706573b0 | ||
|
|
0106d380fc | ||
|
|
a05c32167e | ||
|
|
cbfa49b7bc | ||
|
|
f56684a6e8 | ||
|
|
2bee3f55c6 | ||
|
|
eb04f5b0b5 | ||
|
|
0866a6e4a1 | ||
|
|
0dc4289232 | ||
|
|
6d8d8cb39e | ||
|
|
3096f05f67 | ||
|
|
641fdf6213 | ||
|
|
ea695e3234 | ||
|
|
ac502169a9 | ||
|
|
f6d9b565d7 | ||
|
|
288072ce1a | ||
|
|
e9c7f91a1d | ||
|
|
f29aaa2170 | ||
|
|
249caafddb | ||
|
|
38333a117b | ||
|
|
c5042066ff | ||
|
|
f028ac2d4e | ||
|
|
6372ade5c1 | ||
|
|
8378909517 | ||
|
|
67a44f8bdb | ||
|
|
10b1aec389 | ||
|
|
f9dc9ebd48 | ||
|
|
8bfa14352c | ||
|
|
ec7fccac35 | ||
|
|
397a2ced4e | ||
|
|
056cbfa144 | ||
|
|
e50f54eedc | ||
|
|
4eee179d37 | ||
|
|
1e3b5dff60 | ||
|
|
7b894ffd00 | ||
|
|
dbd725797c | ||
|
|
d9b0d6ac95 | ||
|
|
5bb5fb9401 | ||
|
|
c0d546fb9d | ||
|
|
9a2359a993 | ||
|
|
ed20bb1c76 | ||
|
|
fc6462c653 | ||
|
|
df7d80e2e4 | ||
|
|
89e71c4641 | ||
|
|
2d0e27075b | ||
|
|
bd758e3793 | ||
|
|
3e73203c94 | ||
|
|
e9f51560f6 | ||
|
|
1e7fd7afe0 | ||
|
|
3aa3349d72 | ||
|
|
042bad25e0 | ||
|
|
50f36dbc6c | ||
|
|
6894468e58 | ||
|
|
51257bbde8 | ||
|
|
5bd06fa0ef | ||
|
|
9a1625fa1b | ||
|
|
744c9c3f52 | ||
|
|
429ce4c4e5 | ||
|
|
9d58502f29 | ||
|
|
fcf12db850 | ||
|
|
4db097b544 | ||
|
|
4bf104fc76 | ||
|
|
7371fa8765 | ||
|
|
22a88599d1 | ||
|
|
c6d7cbe661 | ||
|
|
c67355ace7 | ||
|
|
ed37399c3c | ||
|
|
76943d8ef4 | ||
|
|
ebabb8d667 | ||
|
|
a603c5d202 | ||
|
|
75c4e3fa2b |
2
.env
2
.env
@@ -4,6 +4,8 @@
|
||||
# Prisma supports the native connection string format for PostgreSQL, MySQL, SQLite, SQL Server, MongoDB and CockroachDB.
|
||||
# See the documentation for all the connection string options: https://pris.ly/d/connection-strings
|
||||
|
||||
SECRET="jg57cya4mrNkGlnb2R85X1gI8LdY7iNZ9MF0Jbn0K5zQBshOxv"
|
||||
|
||||
POSTGRES_DB=main
|
||||
POSTGRES_HOST=localhost
|
||||
POSTGRES_PORT=5432
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
name: Auto Merge Staging into Main
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 2 * * *' # 2:00 UTC = 4:00 Europäische Zeit
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
merge:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0
|
||||
|
||||
- name: Set Git user
|
||||
run: |
|
||||
git config user.name "github-actions[bot]"
|
||||
git config user.email "github-actions[bot]@users.noreply.github.com"
|
||||
|
||||
- name: Fetch all branches
|
||||
run: |
|
||||
git fetch origin main
|
||||
git fetch origin staging
|
||||
|
||||
- name: Check if main has commits not in staging
|
||||
id: check
|
||||
run: |
|
||||
git fetch origin
|
||||
if [ $(git rev-list --count origin/staging..origin/main) -gt 0 ]; then
|
||||
echo "❌ Staging is behind main and requires manual merging."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Create PR from staging to main
|
||||
id: create_pr
|
||||
run: |
|
||||
PR_URL=$(gh pr create --base main --head staging --title "Auto-merge staging into main" --body "This PR was created automatically by GitHub Actions. It merges the latest \`staging\` into \`main\`.")
|
||||
echo "PR_URL=$PR_URL" >> $GITHUB_OUTPUT
|
||||
PR_NUMBER=$(echo $PR_URL | awk -F'/' '{print $NF}')
|
||||
echo "PR_NUMBER=$PR_NUMBER" >> $GITHUB_OUTPUT
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Enable auto-merge on PR
|
||||
if: steps.create_pr.outputs.PR_NUMBER != ''
|
||||
run: |
|
||||
gh pr merge ${{ steps.create_pr.outputs.PR_NUMBER }} --merge --auto
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
|
||||
notify_failure:
|
||||
needs: merge
|
||||
if: failure()
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Send Discord notification on failure
|
||||
run: |
|
||||
curl -H "Content-Type: application/json" \
|
||||
-X POST \
|
||||
-d "{\"content\": \"🚨 Auto-Merge fehlgeschlagen! Bitte manuell prüfen: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}\"}" \
|
||||
${{ secrets.DISCORD_WEBHOOK_URL }}
|
||||
2
.github/workflows/dev-pipeline.yml
vendored
2
.github/workflows/dev-pipeline.yml
vendored
@@ -28,4 +28,4 @@ jobs:
|
||||
git clean -f -d
|
||||
git pull origin dev
|
||||
git status
|
||||
make prod
|
||||
make prod-no-backup
|
||||
18
.github/workflows/enforce-pr-source.yml
vendored
Normal file
18
.github/workflows/enforce-pr-source.yml
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
name: Enforce PR Source
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, reopened, ready_for_review]
|
||||
branches:
|
||||
- main
|
||||
|
||||
jobs:
|
||||
check-pr-source:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Enforce only staging → main
|
||||
run: |
|
||||
if [[ "${{ github.head_ref }}" != "staging" ]]; then
|
||||
echo "ERROR: Only 'staging' branch may create PRs into 'main'!"
|
||||
exit 1
|
||||
fi
|
||||
9
.github/workflows/prevent-wrong-pr.yml
vendored
9
.github/workflows/prevent-wrong-pr.yml
vendored
@@ -13,18 +13,15 @@ jobs:
|
||||
steps:
|
||||
- name: Prevent dev merges
|
||||
run: |
|
||||
echo "${{ github.head_ref }}";
|
||||
echo "${{ github.base_ref }}";
|
||||
if [[ "${{ github.head_ref }}" == "dev" ]]; then
|
||||
echo "ERROR: Merging 'dev' into '${{ github.base_ref }}' is forbidden!"
|
||||
if [[ "${{ github.head_ref }}" == "dev" && "${{ github.base_ref }}" == "main" ]]; then
|
||||
echo "ERROR: Merging 'dev' into 'main' is forbidden!"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
|
||||
- name: Allow only staging into main
|
||||
if: github.base_ref == 'main'
|
||||
run: |
|
||||
echo "${{ github.head_ref }}";
|
||||
echo "${{ github.base_ref }}";
|
||||
if [[ "${{ github.head_ref }}" != "staging" ]]; then
|
||||
echo "ERROR: Only 'staging' branch is allowed to merge into 'main'. Current: '${{ github.head_ref }}'"
|
||||
exit 1
|
||||
|
||||
39
.github/workflows/prod-pipeline.yml
vendored
39
.github/workflows/prod-pipeline.yml
vendored
@@ -5,8 +5,33 @@ on:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
check-migrations:
|
||||
name: Check for new Prisma migrations
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
has_new_migrations: ${{ steps.diff.outputs.has_new_migrations }}
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
fetch-depth: 0 # get full history so git diff works properly
|
||||
|
||||
- name: Detect new migration files
|
||||
id: diff
|
||||
run: |
|
||||
# Compare the last two commits on main
|
||||
if git diff --quiet HEAD~1 -- prisma/migrations/; then
|
||||
echo "✅ No new Prisma migrations detected."
|
||||
echo "has_new_migrations=false" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "⚠️ New Prisma migrations detected! Blocking deployment."
|
||||
echo "has_new_migrations=true" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
|
||||
deploy:
|
||||
name: Deploy to production
|
||||
runs-on: ubuntu-latest
|
||||
needs: check-migrations
|
||||
if: needs.check-migrations.outputs.has_new_migrations == 'false'
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Install Bun
|
||||
@@ -29,3 +54,15 @@ jobs:
|
||||
git pull origin main
|
||||
git status
|
||||
make prod
|
||||
|
||||
block-deploy:
|
||||
name: Block deployment (new migrations detected)
|
||||
runs-on: ubuntu-latest
|
||||
needs: check-migrations
|
||||
if: needs.check-migrations.outputs.has_new_migrations == 'true'
|
||||
steps:
|
||||
- name: Stop deploy
|
||||
run: |
|
||||
echo "🚫 Deployment blocked because new Prisma migrations were detected."
|
||||
echo "Please apply migrations on staging and verify before deploying to production."
|
||||
exit 1
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -15,6 +15,8 @@ npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
pnpm-debug.log*
|
||||
error.log*
|
||||
combined.log*
|
||||
|
||||
# lockfile
|
||||
pnpm-lock.yaml
|
||||
|
||||
10
Makefile
10
Makefile
@@ -30,6 +30,9 @@ run-database: stop-database
|
||||
docker volume create $(DB_VOLUME)
|
||||
docker build -t $(DB_CONTAINER_NAME) .
|
||||
docker run -d --name $(DB_CONTAINER_NAME) \
|
||||
--log-driver=json-file \
|
||||
--log-opt max-size=50m \
|
||||
--log-opt max-file=3 \
|
||||
--restart=always \
|
||||
-e POSTGRES_USER=$(DB_USER) \
|
||||
-e POSTGRES_PASSWORD=$(DB_PASSWORD) \
|
||||
@@ -59,9 +62,12 @@ all:
|
||||
bun run dev 2>&1 | tee ~/logs/`date '+%d-%m-%Y_%H:%M:%S'`.log
|
||||
|
||||
update-dwd-klimafaktoren-cron:
|
||||
pm2 start bun --name "update-dwd-klimafaktoren-cron" --cron "0 12 28 * *" -- src/cronjobs/update-dwd-klimafaktoren.ts
|
||||
- pm2 delete update-dwd-klimafaktoren-cron
|
||||
pm2 start --no-autorestart bun --name "update-dwd-klimafaktoren-cron" --cron "0 12 28 * *" -- src/cronjobs/update-dwd-klimafaktoren.ts
|
||||
|
||||
prod: install-dependencies prisma-studio backup-database-cronjob update-dwd-klimafaktoren-cron
|
||||
prod: prod-no-backup backup-database-cronjob
|
||||
|
||||
prod-no-backup: install-dependencies prisma-studio update-dwd-klimafaktoren-cron
|
||||
bun run build
|
||||
mkdir -p ~/logs
|
||||
mkdir -p ~/persistent/online-energieausweis
|
||||
|
||||
@@ -5,6 +5,9 @@ import tailwind from "@astrojs/tailwind";
|
||||
import node from "@astrojs/node";
|
||||
import mdx from "@astrojs/mdx";
|
||||
import astroTypesafeAPI from "astro-typesafe-api"
|
||||
import { logger } from "./src/lib/logger";
|
||||
|
||||
logger.info("Astro config loaded");
|
||||
|
||||
// https://astro.build/config
|
||||
export default defineConfig({
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
#!/bin/bash
|
||||
|
||||
if [[ -z "$prev_restart_delay" && -n "$cron_restart" ]]; then
|
||||
echo "skipping initial launch...."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
FILE_NAME=data-dump_`date +%Y-%m-%d"_"%H_%M_%S`.sql.br
|
||||
FILE_NAME_COMPLETE=full-dump_`date +%Y-%m-%d"_"%H_%M_%S`.sql.br
|
||||
DATABASE_NAME=database
|
||||
|
||||
# Das wird benötigt für AWS Ionos Kompatibilität.
|
||||
export AWS_REQUEST_CHECKSUM_CALCULATION=when_required
|
||||
@@ -11,15 +17,15 @@ export AWS_RESPONSE_CHECKSUM_VALIDATION=when_required
|
||||
# IMPORTANT: Dieser Befehl benötigt das `ionos` Profil, sonst wird er nicht funktionieren.
|
||||
# Das Profil kann mit `aws configure --profile ionos` erstellt werden.
|
||||
# Den Key dafür findet man auf https://dcd.ionos.com/latest/?lang=en#/key-management
|
||||
docker exec -t online-energieausweis-database-1 pg_dump --data-only -U main main | brotli --best > $FILE_NAME
|
||||
docker exec -t $DATABASE_NAME pg_dump --data-only -U main main | brotli --quality=3 > $FILE_NAME
|
||||
|
||||
aws s3 cp $FILE_NAME s3://ibc-db-backup/ --profile ionos --endpoint-url https://s3-eu-central-1.ionoscloud.com --storage-class STANDARD
|
||||
aws s3 cp $FILE_NAME s3://ibc-db-backup/ --profile ionos --endpoint-url https://s3.eu-central-3.ionoscloud.com --storage-class STANDARD
|
||||
|
||||
echo "Uploaded $FILE_NAME"
|
||||
|
||||
docker exec -t online-energieausweis-database-1 pg_dumpall -c -U main | brotli --best > $FILE_NAME_COMPLETE
|
||||
docker exec -t $DATABASE_NAME pg_dumpall -c -U main | brotli --quality=3 > $FILE_NAME_COMPLETE
|
||||
|
||||
aws s3 cp $FILE_NAME_COMPLETE s3://ibc-db-backup/ --profile ionos --endpoint-url https://s3-eu-central-1.ionoscloud.com --storage-class STANDARD
|
||||
aws s3 cp $FILE_NAME_COMPLETE s3://ibc-db-backup/ --profile ionos --endpoint-url https://s3.eu-central-3.ionoscloud.com --storage-class STANDARD
|
||||
|
||||
echo "Uploaded $FILE_NAME_COMPLETE"
|
||||
|
||||
|
||||
72
bun.lock
72
bun.lock
@@ -15,6 +15,7 @@
|
||||
"@pdfme/common": "^5.2.16",
|
||||
"@pdfme/generator": "^5.2.16",
|
||||
"@pdfme/ui": "^5.2.16",
|
||||
"@svelte-plugins/datepicker": "^1.0.11",
|
||||
"@trpc/client": "^10.45.2",
|
||||
"@trpc/server": "^10.45.2",
|
||||
"astro": "^4.16.17",
|
||||
@@ -26,7 +27,8 @@
|
||||
"express": "^4.21.2",
|
||||
"flag-icons": "^6.15.0",
|
||||
"fontkit": "^2.0.4",
|
||||
"highlight.run": "^9.14.0",
|
||||
"handlebars": "^4.7.8",
|
||||
"heic2any": "^0.0.4",
|
||||
"is-base64": "^1.1.0",
|
||||
"js-cookie": "^3.0.5",
|
||||
"js-interpolate": "^1.3.2",
|
||||
@@ -34,11 +36,11 @@
|
||||
"jwt-decode": "^4.0.0",
|
||||
"mime": "^4.0.6",
|
||||
"moment": "^2.30.1",
|
||||
"moment-timezone": "^0.5.46",
|
||||
"moment-timezone": "^0.6.0",
|
||||
"nodemailer": "^6.10.0",
|
||||
"pdf-lib": "^1.17.1",
|
||||
"postcss-nested": "^7.0.2",
|
||||
"puppeteer": "^24.7.2",
|
||||
"puppeteer": "^24.15.0",
|
||||
"radix-svelte-icons": "^1.0.0",
|
||||
"sass": "^1.83.4",
|
||||
"sharp": "^0.33.5",
|
||||
@@ -53,7 +55,7 @@
|
||||
"tailwindcss": "^3.4.17",
|
||||
"trpc-openapi": "^1.2.0",
|
||||
"uuid": "^9.0.1",
|
||||
"winston": "^3.17.0",
|
||||
"winston": "^3.18.3",
|
||||
"zod": "^3.24.1",
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -267,7 +269,7 @@
|
||||
|
||||
"@cypress/xvfb": ["@cypress/xvfb@1.2.4", "", { "dependencies": { "debug": "^3.1.0", "lodash.once": "^4.1.1" } }, "sha512-skbBzPggOVYCbnGgV+0dmBdW/s77ZkAOXIC1knS8NagwDjBrNC1LuXtQJeiN6l+m7lzmHtaoUw/ctJKdqkG57Q=="],
|
||||
|
||||
"@dabh/diagnostics": ["@dabh/diagnostics@2.0.3", "", { "dependencies": { "colorspace": "1.1.x", "enabled": "2.0.x", "kuler": "^2.0.0" } }, "sha512-hrlQOIi7hAfzsMqlGSFyVucrx38O+j6wiGOf//H2ecvIEqYN4ADBSS2iLMh5UFyDunCNniUIPk/q3riFv45xRA=="],
|
||||
"@dabh/diagnostics": ["@dabh/diagnostics@2.0.8", "", { "dependencies": { "@so-ric/colorspace": "^1.1.6", "enabled": "2.0.x", "kuler": "^2.0.0" } }, "sha512-R4MSXTVnuMzGD7bzHdW2ZhhdPC/igELENcq5IjEverBvq5hn1SXCWcsi6eSsdWP0/Ur+SItRRjAktmdoX/8R/Q=="],
|
||||
|
||||
"@daybrush/utils": ["@daybrush/utils@1.13.0", "", {}, "sha512-ALK12C6SQNNHw1enXK+UO8bdyQ+jaWNQ1Af7Z3FNxeAwjYhQT7do+TRE4RASAJ3ObaS2+TJ7TXR3oz2Gzbw0PQ=="],
|
||||
|
||||
@@ -523,7 +525,7 @@
|
||||
|
||||
"@proload/core": ["@proload/core@0.3.3", "", { "dependencies": { "deepmerge": "^4.2.2", "escalade": "^3.1.1" } }, "sha512-7dAFWsIK84C90AMl24+N/ProHKm4iw0akcnoKjRvbfHifJZBLhaDsDus1QJmhG12lXj4e/uB/8mB/0aduCW+NQ=="],
|
||||
|
||||
"@puppeteer/browsers": ["@puppeteer/browsers@2.10.2", "", { "dependencies": { "debug": "^4.4.0", "extract-zip": "^2.0.1", "progress": "^2.0.3", "proxy-agent": "^6.5.0", "semver": "^7.7.1", "tar-fs": "^3.0.8", "yargs": "^17.7.2" }, "bin": { "browsers": "lib/cjs/main-cli.js" } }, "sha512-i4Ez+s9oRWQbNjtI/3+jxr7OH508mjAKvza0ekPJem0ZtmsYHP3B5dq62+IaBHKaGCOuqJxXzvFLUhJvQ6jtsQ=="],
|
||||
"@puppeteer/browsers": ["@puppeteer/browsers@2.10.6", "", { "dependencies": { "debug": "^4.4.1", "extract-zip": "^2.0.1", "progress": "^2.0.3", "proxy-agent": "^6.5.0", "semver": "^7.7.2", "tar-fs": "^3.1.0", "yargs": "^17.7.2" }, "bin": { "browsers": "lib/cjs/main-cli.js" } }, "sha512-pHUn6ZRt39bP3698HFQlu2ZHCkS/lPcpv7fVQcGBSzNNygw171UXAKrCUhy+TEMw4lEttOKDgNpb04hwUAJeiQ=="],
|
||||
|
||||
"@rc-component/async-validator": ["@rc-component/async-validator@5.0.4", "", { "dependencies": { "@babel/runtime": "^7.24.4" } }, "sha512-qgGdcVIF604M9EqjNF0hbUTz42bz/RDtxWdWuU5EQe3hi7M8ob54B6B35rOsvX5eSvIHIzT9iH1R3n+hk3CGfg=="],
|
||||
|
||||
@@ -711,6 +713,10 @@
|
||||
|
||||
"@smithy/util-waiter": ["@smithy/util-waiter@4.0.2", "", { "dependencies": { "@smithy/abort-controller": "^4.0.1", "@smithy/types": "^4.1.0", "tslib": "^2.6.2" } }, "sha512-piUTHyp2Axx3p/kc2CIJkYSv0BAaheBQmbACZgQSSfWUumWNW+R1lL+H9PDBxKJkvOeEX+hKYEFiwO8xagL8AQ=="],
|
||||
|
||||
"@so-ric/colorspace": ["@so-ric/colorspace@1.1.6", "", { "dependencies": { "color": "^5.0.2", "text-hex": "1.0.x" } }, "sha512-/KiKkpHNOBgkFJwu9sh48LkHSMYGyuTcSFK/qMBdnOAlrRJzRSXAOFB5qwzaVQuDl8wAvHVMkaASQDReTahxuw=="],
|
||||
|
||||
"@svelte-plugins/datepicker": ["@svelte-plugins/datepicker@1.0.11", "", {}, "sha512-Tqc07QLyRkCpc3Glg6oRLTUApLtCrOh52d6vJ7L32QI17HrwvcDDjaH3LF3X1SBm3CWdMrnqfJp3xjUZmB4wzw=="],
|
||||
|
||||
"@sveltejs/vite-plugin-svelte": ["@sveltejs/vite-plugin-svelte@2.5.3", "", { "dependencies": { "@sveltejs/vite-plugin-svelte-inspector": "^1.0.4", "debug": "^4.3.4", "deepmerge": "^4.3.1", "kleur": "^4.1.5", "magic-string": "^0.30.3", "svelte-hmr": "^0.15.3", "vitefu": "^0.2.4" }, "peerDependencies": { "svelte": "^3.54.0 || ^4.0.0 || ^5.0.0-next.0", "vite": "^4.0.0" } }, "sha512-erhNtXxE5/6xGZz/M9eXsmI7Pxa6MS7jyTy06zN3Ck++ldrppOnOlJwHHTsMC7DHDQdgUp4NAc4cDNQ9eGdB/w=="],
|
||||
|
||||
"@sveltejs/vite-plugin-svelte-inspector": ["@sveltejs/vite-plugin-svelte-inspector@1.0.4", "", { "dependencies": { "debug": "^4.3.4" }, "peerDependencies": { "@sveltejs/vite-plugin-svelte": "^2.2.0", "svelte": "^3.54.0 || ^4.0.0", "vite": "^4.0.0" } }, "sha512-zjiuZ3yydBtwpF3bj0kQNV0YXe+iKE545QGZVTaylW3eAzFr+pJ/cwK8lZEaRp4JtaJXhD5DyWAV4AxLh6DgaQ=="],
|
||||
@@ -1057,7 +1063,7 @@
|
||||
|
||||
"chownr": ["chownr@2.0.0", "", {}, "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ=="],
|
||||
|
||||
"chromium-bidi": ["chromium-bidi@4.1.1", "", { "dependencies": { "mitt": "^3.0.1", "zod": "^3.24.1" }, "peerDependencies": { "devtools-protocol": "*" } }, "sha512-biR7t4vF3YluE6RlMSk9IWk+b9U+WWyzHp+N2pL9vRTk+UXHYRTVp7jTK58ZNzMLBgoLMHY4QyJMbeuw3eKxqg=="],
|
||||
"chromium-bidi": ["chromium-bidi@7.2.0", "", { "dependencies": { "mitt": "^3.0.1", "zod": "^3.24.1" }, "peerDependencies": { "devtools-protocol": "*" } }, "sha512-gREyhyBstermK+0RbcJLbFhcQctg92AGgDe/h/taMJEOLRdtSswBAO9KmvltFSQWgM2LrwWu5SIuEUbdm3JsyQ=="],
|
||||
|
||||
"ci-info": ["ci-info@4.1.0", "", {}, "sha512-HutrvTNsF48wnxkzERIXOe5/mlcfFcbfCmwcg6CJnizbSue78AbDt+1cgl26zwn61WFxhcPykPfZrbqjGmBb4A=="],
|
||||
|
||||
@@ -1101,8 +1107,6 @@
|
||||
|
||||
"colorette": ["colorette@2.0.19", "", {}, "sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ=="],
|
||||
|
||||
"colorspace": ["colorspace@1.1.4", "", { "dependencies": { "color": "^3.1.3", "text-hex": "1.0.x" } }, "sha512-BgvKJiuVu1igBUF2kEjRCZXol6wiiGbY5ipL/oVPwm0BL9sIpMIzM8IK7vwuxIIzOXMV3Ey5w+vxhm0rR/TN8w=="],
|
||||
|
||||
"combined-stream": ["combined-stream@1.0.8", "", { "dependencies": { "delayed-stream": "~1.0.0" } }, "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg=="],
|
||||
|
||||
"comma-separated-tokens": ["comma-separated-tokens@2.0.3", "", {}, "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg=="],
|
||||
@@ -1241,7 +1245,7 @@
|
||||
|
||||
"devlop": ["devlop@1.1.0", "", { "dependencies": { "dequal": "^2.0.0" } }, "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA=="],
|
||||
|
||||
"devtools-protocol": ["devtools-protocol@0.0.1425554", "", {}, "sha512-uRfxR6Nlzdzt0ihVIkV+sLztKgs7rgquY/Mhcv1YNCWDh5IZgl5mnn2aeEnW5stYTE0wwiF4RYVz8eMEpV1SEw=="],
|
||||
"devtools-protocol": ["devtools-protocol@0.0.1464554", "", {}, "sha512-CAoP3lYfwAGQTaAXYvA6JZR0fjGUb7qec1qf4mToyoH2TZgUFeIqYcjh6f9jNuhHfuZiEdH+PONHYrLhRQX6aw=="],
|
||||
|
||||
"dezalgo": ["dezalgo@1.0.4", "", { "dependencies": { "asap": "^2.0.0", "wrappy": "1" } }, "sha512-rXSP0bf+5n0Qonsb+SVVfNfIsimO4HEtmnIpPHY8Q1UCzKlQrDMfdobr8nJOOsRgWCyMRqeSBQzmWUMq7zvVig=="],
|
||||
|
||||
@@ -1515,6 +1519,8 @@
|
||||
|
||||
"h3": ["h3@1.14.0", "", { "dependencies": { "cookie-es": "^1.2.2", "crossws": "^0.3.2", "defu": "^6.1.4", "destr": "^2.0.3", "iron-webcrypto": "^1.2.1", "ohash": "^1.1.4", "radix3": "^1.1.2", "ufo": "^1.5.4", "uncrypto": "^0.1.3", "unenv": "^1.10.0" } }, "sha512-ao22eiONdgelqcnknw0iD645qW0s9NnrJHr5OBz4WOMdBdycfSas1EQf1wXRsm+PcB2Yoj43pjBPwqIpJQTeWg=="],
|
||||
|
||||
"handlebars": ["handlebars@4.7.8", "", { "dependencies": { "minimist": "^1.2.5", "neo-async": "^2.6.2", "source-map": "^0.6.1", "wordwrap": "^1.0.0" }, "optionalDependencies": { "uglify-js": "^3.1.4" }, "bin": { "handlebars": "bin/handlebars" } }, "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ=="],
|
||||
|
||||
"has-flag": ["has-flag@4.0.0", "", {}, "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ=="],
|
||||
|
||||
"has-symbols": ["has-symbols@1.1.0", "", {}, "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ=="],
|
||||
@@ -1549,9 +1555,9 @@
|
||||
|
||||
"hastscript": ["hastscript@9.0.0", "", { "dependencies": { "@types/hast": "^3.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-parse-selector": "^4.0.0", "property-information": "^6.0.0", "space-separated-tokens": "^2.0.0" } }, "sha512-jzaLBGavEDKHrc5EfFImKN7nZKKBdSLIdGvCwDZ9TfzbF2ffXiov8CKE445L2Z1Ek2t/m4SKQ2j6Ipv7NyUolw=="],
|
||||
|
||||
"hexoid": ["hexoid@2.0.0", "", {}, "sha512-qlspKUK7IlSQv2o+5I7yhUd7TxlOG2Vr5LTa3ve2XSNVKAL/n/u/7KLvKmFNimomDIKvZFXWHv0T12mv7rT8Aw=="],
|
||||
"heic2any": ["heic2any@0.0.4", "", {}, "sha512-3lLnZiDELfabVH87htnRolZ2iehX9zwpRyGNz22GKXIu0fznlblf0/ftppXKNqS26dqFSeqfIBhAmAj/uSp0cA=="],
|
||||
|
||||
"highlight.run": ["highlight.run@9.14.0", "", {}, "sha512-ZR+ZLHlVU8lXqsuto0ZEMAOuvptaTBBf1jradnKDIn9OfAXupcYFbkASDlbsZtyBh2SYJSK50xwrucXujhksRg=="],
|
||||
"hexoid": ["hexoid@2.0.0", "", {}, "sha512-qlspKUK7IlSQv2o+5I7yhUd7TxlOG2Vr5LTa3ve2XSNVKAL/n/u/7KLvKmFNimomDIKvZFXWHv0T12mv7rT8Aw=="],
|
||||
|
||||
"hosted-git-info": ["hosted-git-info@2.8.9", "", {}, "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw=="],
|
||||
|
||||
@@ -1959,7 +1965,7 @@
|
||||
|
||||
"moment": ["moment@2.30.1", "", {}, "sha512-uEmtNhbDOrWPFS+hdjFCBfy9f2YoyzRpwcl+DqpC6taX21FzsTLQVbMV/W7PzNSX6x/bhC1zA3c2UQ5NzH6how=="],
|
||||
|
||||
"moment-timezone": ["moment-timezone@0.5.47", "", { "dependencies": { "moment": "^2.29.4" } }, "sha512-UbNt/JAWS0m/NJOebR0QMRHBk0hu03r5dx9GK8Cs0AS3I81yDcOc9k+DytPItgVvBP7J6Mf6U2n3BPAacAV9oA=="],
|
||||
"moment-timezone": ["moment-timezone@0.6.0", "", { "dependencies": { "moment": "^2.29.4" } }, "sha512-ldA5lRNm3iJCWZcBCab4pnNL3HSZYXVb/3TYr75/1WCTWYuTqYUb5f/S384pncYjJ88lbO8Z4uPDvmoluHJc8Q=="],
|
||||
|
||||
"mrmime": ["mrmime@2.0.0", "", {}, "sha512-eu38+hdgojoyq63s+yTpN4XMBdt5l8HhMhc4VKLO9KM5caLIBvUm4thi7fFaxyTmCKeNnXZ5pAlBwCUnhA09uw=="],
|
||||
|
||||
@@ -1977,6 +1983,8 @@
|
||||
|
||||
"negotiator": ["negotiator@0.6.3", "", {}, "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg=="],
|
||||
|
||||
"neo-async": ["neo-async@2.6.2", "", {}, "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw=="],
|
||||
|
||||
"neotraverse": ["neotraverse@0.6.18", "", {}, "sha512-Z4SmBUweYa09+o6pG+eASabEpP6QkQ70yHj351pQoEXIs8uHbaU2DWVmzBANKgflPa47A50PtB2+NgRpQvr7vA=="],
|
||||
|
||||
"netmask": ["netmask@2.0.2", "", {}, "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg=="],
|
||||
@@ -2187,9 +2195,9 @@
|
||||
|
||||
"punycode": ["punycode@2.3.1", "", {}, "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg=="],
|
||||
|
||||
"puppeteer": ["puppeteer@24.7.2", "", { "dependencies": { "@puppeteer/browsers": "2.10.2", "chromium-bidi": "4.1.1", "cosmiconfig": "^9.0.0", "devtools-protocol": "0.0.1425554", "puppeteer-core": "24.7.2", "typed-query-selector": "^2.12.0" }, "bin": { "puppeteer": "lib/cjs/puppeteer/node/cli.js" } }, "sha512-ifYqoY6wGs0yZeFuFPn8BE9FhuveXkarF+eO18I2e/axdoCh4Qh1AE+qXdJBhdaeoPt6eRNTY4Dih29Jbq8wow=="],
|
||||
"puppeteer": ["puppeteer@24.15.0", "", { "dependencies": { "@puppeteer/browsers": "2.10.6", "chromium-bidi": "7.2.0", "cosmiconfig": "^9.0.0", "devtools-protocol": "0.0.1464554", "puppeteer-core": "24.15.0", "typed-query-selector": "^2.12.0" }, "bin": { "puppeteer": "lib/cjs/puppeteer/node/cli.js" } }, "sha512-HPSOTw+DFsU/5s2TUUWEum9WjFbyjmvFDuGHtj2X4YUz2AzOzvKMkT3+A3FR+E+ZefiX/h3kyLyXzWJWx/eMLQ=="],
|
||||
|
||||
"puppeteer-core": ["puppeteer-core@24.7.2", "", { "dependencies": { "@puppeteer/browsers": "2.10.2", "chromium-bidi": "4.1.1", "debug": "^4.4.0", "devtools-protocol": "0.0.1425554", "typed-query-selector": "^2.12.0", "ws": "^8.18.1" } }, "sha512-P9pZyTmJqKODFCnkZgemCpoFA4LbAa8+NumHVQKyP5X9IgdNS1ZnAnIh1sMAwhF8/xEUGf7jt+qmNLlKieFw1Q=="],
|
||||
"puppeteer-core": ["puppeteer-core@24.15.0", "", { "dependencies": { "@puppeteer/browsers": "2.10.6", "chromium-bidi": "7.2.0", "debug": "^4.4.1", "devtools-protocol": "0.0.1464554", "typed-query-selector": "^2.12.0", "ws": "^8.18.3" } }, "sha512-2iy0iBeWbNyhgiCGd/wvGrDSo73emNFjSxYOcyAqYiagkYt5q4cPfVXaVDKBsukgc2fIIfLAalBZlaxldxdDYg=="],
|
||||
|
||||
"qs": ["qs@6.13.0", "", { "dependencies": { "side-channel": "^1.0.6" } }, "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg=="],
|
||||
|
||||
@@ -2563,7 +2571,7 @@
|
||||
|
||||
"tar": ["tar@6.2.1", "", { "dependencies": { "chownr": "^2.0.0", "fs-minipass": "^2.0.0", "minipass": "^5.0.0", "minizlib": "^2.1.1", "mkdirp": "^1.0.3", "yallist": "^4.0.0" } }, "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A=="],
|
||||
|
||||
"tar-fs": ["tar-fs@3.0.8", "", { "dependencies": { "pump": "^3.0.0", "tar-stream": "^3.1.5" }, "optionalDependencies": { "bare-fs": "^4.0.1", "bare-path": "^3.0.0" } }, "sha512-ZoROL70jptorGAlgAYiLoBLItEKw/fUxg9BSYK/dF/GAGYFJOJJJMvjPAKDJraCXFwadD456FCuvLWgfhMsPwg=="],
|
||||
"tar-fs": ["tar-fs@3.1.0", "", { "dependencies": { "pump": "^3.0.0", "tar-stream": "^3.1.5" }, "optionalDependencies": { "bare-fs": "^4.0.1", "bare-path": "^3.0.0" } }, "sha512-5Mty5y/sOF1YWj1J6GiBodjlDc05CUR8PKXrsnFAiSG0xA+GHeWLovaZPYUDXkH/1iKRf2+M5+OrRgzC7O9b7w=="],
|
||||
|
||||
"tar-stream": ["tar-stream@2.2.0", "", { "dependencies": { "bl": "^4.0.3", "end-of-stream": "^1.4.1", "fs-constants": "^1.0.0", "inherits": "^2.0.3", "readable-stream": "^3.1.1" } }, "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ=="],
|
||||
|
||||
@@ -2663,6 +2671,8 @@
|
||||
|
||||
"ufo": ["ufo@1.5.4", "", {}, "sha512-UsUk3byDzKd04EyoZ7U4DOlxQaD14JUKQl6/P7wiX4FNvUfm3XL246n9W5AmqwW5RSFJ27NAuM0iLscAOYUiGQ=="],
|
||||
|
||||
"uglify-js": ["uglify-js@3.19.3", "", { "bin": { "uglifyjs": "bin/uglifyjs" } }, "sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ=="],
|
||||
|
||||
"uncrypto": ["uncrypto@0.1.3", "", {}, "sha512-Ql87qFHB3s/De2ClA9e0gsnS6zXG27SkTiSJwjCc9MebbfapQfuPzumMIUMi38ezPZVNFcHI9sUIepeQfw8J8Q=="],
|
||||
|
||||
"undici-types": ["undici-types@6.20.0", "", {}, "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg=="],
|
||||
@@ -2755,19 +2765,21 @@
|
||||
|
||||
"widest-line": ["widest-line@5.0.0", "", { "dependencies": { "string-width": "^7.0.0" } }, "sha512-c9bZp7b5YtRj2wOe6dlj32MK+Bx/M/d+9VB2SHM1OtsUHR0aV0tdP6DWh/iMt0kWi1t5g1Iudu6hQRNd1A4PVA=="],
|
||||
|
||||
"winston": ["winston@3.17.0", "", { "dependencies": { "@colors/colors": "^1.6.0", "@dabh/diagnostics": "^2.0.2", "async": "^3.2.3", "is-stream": "^2.0.0", "logform": "^2.7.0", "one-time": "^1.0.0", "readable-stream": "^3.4.0", "safe-stable-stringify": "^2.3.1", "stack-trace": "0.0.x", "triple-beam": "^1.3.0", "winston-transport": "^4.9.0" } }, "sha512-DLiFIXYC5fMPxaRg832S6F5mJYvePtmO5G9v9IgUFPhXm9/GkXarH/TUrBAVzhTCzAj9anE/+GjrgXp/54nOgw=="],
|
||||
"winston": ["winston@3.18.3", "", { "dependencies": { "@colors/colors": "^1.6.0", "@dabh/diagnostics": "^2.0.8", "async": "^3.2.3", "is-stream": "^2.0.0", "logform": "^2.7.0", "one-time": "^1.0.0", "readable-stream": "^3.4.0", "safe-stable-stringify": "^2.3.1", "stack-trace": "0.0.x", "triple-beam": "^1.3.0", "winston-transport": "^4.9.0" } }, "sha512-NoBZauFNNWENgsnC9YpgyYwOVrl2m58PpQ8lNHjV3kosGs7KJ7Npk9pCUE+WJlawVSe8mykWDKWFSVfs3QO9ww=="],
|
||||
|
||||
"winston-transport": ["winston-transport@4.9.0", "", { "dependencies": { "logform": "^2.7.0", "readable-stream": "^3.6.2", "triple-beam": "^1.3.0" } }, "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A=="],
|
||||
|
||||
"word-wrap": ["word-wrap@1.2.5", "", {}, "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA=="],
|
||||
|
||||
"wordwrap": ["wordwrap@1.0.0", "", {}, "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q=="],
|
||||
|
||||
"wrap-ansi": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="],
|
||||
|
||||
"wrap-ansi-cjs": ["wrap-ansi@7.0.0", "", { "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" } }, "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q=="],
|
||||
|
||||
"wrappy": ["wrappy@1.0.2", "", {}, "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ=="],
|
||||
|
||||
"ws": ["ws@8.18.1", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w=="],
|
||||
"ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="],
|
||||
|
||||
"xml-crypto": ["xml-crypto@6.0.0", "", { "dependencies": { "@xmldom/is-dom-node": "^1.0.1", "@xmldom/xmldom": "^0.8.10", "xpath": "^0.0.33" } }, "sha512-L3RgnkaDrHaYcCnoENv4Idzt1ZRj5U1z1BDH98QdDTQfssScx8adgxhd9qwyYo+E3fXbQZjEQH7aiXHLVgxGvw=="],
|
||||
|
||||
@@ -2867,10 +2879,14 @@
|
||||
|
||||
"@prisma/schema-files-loader/fs-extra": ["fs-extra@11.1.1", "", { "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-MGIE4HOvQCeUCzmlHs0vXpih4ysz4wg9qiSAu6cd42lVwPbTM1TjV7RusoyQqMmk/95gdQZX72u+YW+c3eEpFQ=="],
|
||||
|
||||
"@puppeteer/browsers/semver": ["semver@7.7.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA=="],
|
||||
"@puppeteer/browsers/debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="],
|
||||
|
||||
"@puppeteer/browsers/semver": ["semver@7.7.2", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA=="],
|
||||
|
||||
"@rollup/pluginutils/estree-walker": ["estree-walker@2.0.2", "", {}, "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="],
|
||||
|
||||
"@so-ric/colorspace/color": ["color@5.0.2", "", { "dependencies": { "color-convert": "^3.0.1", "color-string": "^2.0.0" } }, "sha512-e2hz5BzbUPcYlIRHo8ieAhYgoajrJr+hWoceg6E345TPsATMUKqDgzt8fSXZJJbxfpiPzkWyphz8yn8At7q3fA=="],
|
||||
|
||||
"@sveltejs/vite-plugin-svelte/vitefu": ["vitefu@0.2.5", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0" }, "optionalPeers": ["vite"] }, "sha512-SgHtMLoqaeeGnd2evZ849ZbACbnwQCIwRH57t18FxcXoZop0uQu0uzlIhJBlF/eWVzuce0sHeqPcDo+evVcg8Q=="],
|
||||
|
||||
"@types/ssh2/@types/node": ["@types/node@18.19.86", "", { "dependencies": { "undici-types": "~5.26.4" } }, "sha512-fifKayi175wLyKyc5qUfyENhQ1dCNI1UNjp653d8kuYcPQN5JhX3dGuP/XmvPTg/xRBn1VTLpbmi+H/Mr7tLfQ=="],
|
||||
@@ -2917,8 +2933,6 @@
|
||||
|
||||
"co-body/qs": ["qs@6.14.0", "", { "dependencies": { "side-channel": "^1.1.0" } }, "sha512-YWWTjgABSKcvs/nWBi9PycY/JiPJqOD4JA6o9Sej2AtvSGarXxKC3OQSk4pAarbdQlKAh5D4FCQkJNkW+GAn3w=="],
|
||||
|
||||
"colorspace/color": ["color@3.2.1", "", { "dependencies": { "color-convert": "^1.9.3", "color-string": "^1.6.0" } }, "sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA=="],
|
||||
|
||||
"csvtojson/strip-bom": ["strip-bom@2.0.0", "", { "dependencies": { "is-utf8": "^0.2.0" } }, "sha512-kwrX1y7czp1E69n2ajbG65mIo9dqvJ+8aBQXOGVxqwvNbsXdFM6Lq37dLAY3mknUwru8CfcCbfOLL/gMo+fi3g=="],
|
||||
|
||||
"cypress/buffer": ["buffer@5.7.1", "", { "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" } }, "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ=="],
|
||||
@@ -2965,6 +2979,8 @@
|
||||
|
||||
"gray-matter/js-yaml": ["js-yaml@3.14.1", "", { "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g=="],
|
||||
|
||||
"handlebars/source-map": ["source-map@0.6.1", "", {}, "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g=="],
|
||||
|
||||
"hasha/type-fest": ["type-fest@0.8.1", "", {}, "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA=="],
|
||||
|
||||
"ignore-walk/minimatch": ["minimatch@5.1.6", "", { "dependencies": { "brace-expansion": "^2.0.1" } }, "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g=="],
|
||||
@@ -3063,6 +3079,8 @@
|
||||
|
||||
"proxy-agent/proxy-from-env": ["proxy-from-env@1.1.0", "", {}, "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg=="],
|
||||
|
||||
"puppeteer-core/debug": ["debug@4.4.1", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ=="],
|
||||
|
||||
"rc-align/rc-util": ["rc-util@4.21.1", "", { "dependencies": { "add-dom-event-listener": "^1.1.0", "prop-types": "^15.5.10", "react-is": "^16.12.0", "react-lifecycles-compat": "^3.0.4", "shallowequal": "^1.1.0" } }, "sha512-Z+vlkSQVc1l8O2UjR3WQ+XdWlhj5q9BMQNLk2iOBch75CqPfrJyGtcWMcnhRlNuDu0Ndtt4kLVO8JI8BrABobg=="],
|
||||
|
||||
"rc-animate/rc-util": ["rc-util@4.21.1", "", { "dependencies": { "add-dom-event-listener": "^1.1.0", "prop-types": "^15.5.10", "react-is": "^16.12.0", "react-lifecycles-compat": "^3.0.4", "shallowequal": "^1.1.0" } }, "sha512-Z+vlkSQVc1l8O2UjR3WQ+XdWlhj5q9BMQNLk2iOBch75CqPfrJyGtcWMcnhRlNuDu0Ndtt4kLVO8JI8BrABobg=="],
|
||||
@@ -3159,6 +3177,10 @@
|
||||
|
||||
"@prisma/internals/globby/slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="],
|
||||
|
||||
"@so-ric/colorspace/color/color-convert": ["color-convert@3.1.2", "", { "dependencies": { "color-name": "^2.0.0" } }, "sha512-UNqkvCDXstVck3kdowtOTWROIJQwafjOfXSmddoDrXo4cewMKmusCeF22Q24zvjR8nwWib/3S/dfyzPItPEiJg=="],
|
||||
|
||||
"@so-ric/colorspace/color/color-string": ["color-string@2.1.2", "", { "dependencies": { "color-name": "^2.0.0" } }, "sha512-RxmjYxbWemV9gKu4zPgiZagUxbH3RQpEIO77XoSSX0ivgABDZ+h8Zuash/EMFLTI4N9QgFPOJ6JQpPZKFxa+dA=="],
|
||||
|
||||
"@types/ssh2/@types/node/undici-types": ["undici-types@5.26.5", "", {}, "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA=="],
|
||||
|
||||
"@typescript-eslint/typescript-estree/globby/slash": ["slash@3.0.0", "", {}, "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q=="],
|
||||
@@ -3179,8 +3201,6 @@
|
||||
|
||||
"boxen/wrap-ansi/strip-ansi": ["strip-ansi@7.1.0", "", { "dependencies": { "ansi-regex": "^6.0.1" } }, "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ=="],
|
||||
|
||||
"colorspace/color/color-convert": ["color-convert@1.9.3", "", { "dependencies": { "color-name": "1.1.3" } }, "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg=="],
|
||||
|
||||
"express/debug/ms": ["ms@2.0.0", "", {}, "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A=="],
|
||||
|
||||
"express/send/encodeurl": ["encodeurl@1.0.2", "", {}, "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w=="],
|
||||
@@ -3327,12 +3347,14 @@
|
||||
|
||||
"@prisma/internals/@prisma/debug/debug/ms": ["ms@2.1.2", "", {}, "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="],
|
||||
|
||||
"@so-ric/colorspace/color/color-convert/color-name": ["color-name@2.0.2", "", {}, "sha512-9vEt7gE16EW7Eu7pvZnR0abW9z6ufzhXxGXZEVU9IqPdlsUiMwJeJfRtq0zePUmnbHGT9zajca7mX8zgoayo4A=="],
|
||||
|
||||
"@so-ric/colorspace/color/color-string/color-name": ["color-name@2.0.2", "", {}, "sha512-9vEt7gE16EW7Eu7pvZnR0abW9z6ufzhXxGXZEVU9IqPdlsUiMwJeJfRtq0zePUmnbHGT9zajca7mX8zgoayo4A=="],
|
||||
|
||||
"boxen/string-width/strip-ansi/ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="],
|
||||
|
||||
"boxen/wrap-ansi/strip-ansi/ansi-regex": ["ansi-regex@6.1.0", "", {}, "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA=="],
|
||||
|
||||
"colorspace/color/color-convert/color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="],
|
||||
|
||||
"form-render/color/color-convert/color-name": ["color-name@1.1.3", "", {}, "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw=="],
|
||||
|
||||
"npm-packlist/glob/minimatch/brace-expansion": ["brace-expansion@2.0.1", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA=="],
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
version: '3'
|
||||
services:
|
||||
database:
|
||||
container_name: database
|
||||
image: postgres:17.5
|
||||
|
||||
build: ./
|
||||
restart: always
|
||||
env_file:
|
||||
|
||||
10
package.json
10
package.json
@@ -29,6 +29,7 @@
|
||||
"@pdfme/common": "^5.2.16",
|
||||
"@pdfme/generator": "^5.2.16",
|
||||
"@pdfme/ui": "^5.2.16",
|
||||
"@svelte-plugins/datepicker": "^1.0.11",
|
||||
"@trpc/client": "^10.45.2",
|
||||
"@trpc/server": "^10.45.2",
|
||||
"astro": "^4.16.17",
|
||||
@@ -40,7 +41,8 @@
|
||||
"express": "^4.21.2",
|
||||
"flag-icons": "^6.15.0",
|
||||
"fontkit": "^2.0.4",
|
||||
"highlight.run": "^9.14.0",
|
||||
"handlebars": "^4.7.8",
|
||||
"heic2any": "^0.0.4",
|
||||
"is-base64": "^1.1.0",
|
||||
"js-cookie": "^3.0.5",
|
||||
"js-interpolate": "^1.3.2",
|
||||
@@ -48,11 +50,11 @@
|
||||
"jwt-decode": "^4.0.0",
|
||||
"mime": "^4.0.6",
|
||||
"moment": "^2.30.1",
|
||||
"moment-timezone": "^0.5.46",
|
||||
"moment-timezone": "^0.6.0",
|
||||
"nodemailer": "^6.10.0",
|
||||
"pdf-lib": "^1.17.1",
|
||||
"postcss-nested": "^7.0.2",
|
||||
"puppeteer": "^24.7.2",
|
||||
"puppeteer": "^24.15.0",
|
||||
"radix-svelte-icons": "^1.0.0",
|
||||
"sass": "^1.83.4",
|
||||
"sharp": "^0.33.5",
|
||||
@@ -67,7 +69,7 @@
|
||||
"tailwindcss": "^3.4.17",
|
||||
"trpc-openapi": "^1.2.0",
|
||||
"uuid": "^9.0.1",
|
||||
"winston": "^3.17.0",
|
||||
"winston": "^3.18.3",
|
||||
"zod": "^3.24.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
49
prisma/migrations/20250804180940_provisionen/migration.sql
Normal file
49
prisma/migrations/20250804180940_provisionen/migration.sql
Normal file
@@ -0,0 +1,49 @@
|
||||
/*
|
||||
Warnings:
|
||||
|
||||
- The `fenster_art_1` column on the `BedarfsausweisWohnen` table would be dropped and recreated. This will lead to data loss if there is data in the column.
|
||||
- The `fenster_art_2` column on the `BedarfsausweisWohnen` table would be dropped and recreated. This will lead to data loss if there is data in the column.
|
||||
- The `dachfenster_art` column on the `BedarfsausweisWohnen` table would be dropped and recreated. This will lead to data loss if there is data in the column.
|
||||
- The `haustuer_art` column on the `BedarfsausweisWohnen` table would be dropped and recreated. This will lead to data loss if there is data in the column.
|
||||
- The `dach_daemmung` column on the `BedarfsausweisWohnen` table would be dropped and recreated. This will lead to data loss if there is data in the column.
|
||||
- The `decke_daemmung` column on the `BedarfsausweisWohnen` table would be dropped and recreated. This will lead to data loss if there is data in the column.
|
||||
- The `aussenwand_daemmung` column on the `BedarfsausweisWohnen` table would be dropped and recreated. This will lead to data loss if there is data in the column.
|
||||
- The `boden_daemmung` column on the `BedarfsausweisWohnen` table would be dropped and recreated. This will lead to data loss if there is data in the column.
|
||||
|
||||
*/
|
||||
-- AlterEnum
|
||||
ALTER TYPE "BenutzerRolle" ADD VALUE 'RESELLER';
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "BedarfsausweisWohnen" DROP COLUMN "fenster_art_1",
|
||||
ADD COLUMN "fenster_art_1" DOUBLE PRECISION,
|
||||
DROP COLUMN "fenster_art_2",
|
||||
ADD COLUMN "fenster_art_2" DOUBLE PRECISION,
|
||||
DROP COLUMN "dachfenster_art",
|
||||
ADD COLUMN "dachfenster_art" DOUBLE PRECISION,
|
||||
DROP COLUMN "haustuer_art",
|
||||
ADD COLUMN "haustuer_art" DOUBLE PRECISION,
|
||||
DROP COLUMN "dach_daemmung",
|
||||
ADD COLUMN "dach_daemmung" DOUBLE PRECISION,
|
||||
DROP COLUMN "decke_daemmung",
|
||||
ADD COLUMN "decke_daemmung" DOUBLE PRECISION,
|
||||
DROP COLUMN "aussenwand_daemmung",
|
||||
ADD COLUMN "aussenwand_daemmung" DOUBLE PRECISION,
|
||||
DROP COLUMN "boden_daemmung",
|
||||
ADD COLUMN "boden_daemmung" DOUBLE PRECISION;
|
||||
|
||||
-- CreateTable
|
||||
CREATE TABLE "Provisionen" (
|
||||
"id" TEXT NOT NULL,
|
||||
"ausweisart" TEXT NOT NULL,
|
||||
"provision_prozent" DOUBLE PRECISION NOT NULL,
|
||||
"provision_betrag" DOUBLE PRECISION NOT NULL,
|
||||
"benutzer_id" VARCHAR(11),
|
||||
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
"updated_at" TIMESTAMP(3) NOT NULL,
|
||||
|
||||
CONSTRAINT "Provisionen_pkey" PRIMARY KEY ("id")
|
||||
);
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Provisionen" ADD CONSTRAINT "Provisionen_benutzer_id_fkey" FOREIGN KEY ("benutzer_id") REFERENCES "benutzer"("id") ON DELETE SET NULL ON UPDATE CASCADE;
|
||||
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
Warnings:
|
||||
|
||||
- The primary key for the `Provisionen` table will be changed. If it partially fails, the table could be left without primary key constraint.
|
||||
- The `id` column on the `Provisionen` table would be dropped and recreated. This will lead to data loss if there is data in the column.
|
||||
- Changed the type of `ausweisart` on the `Provisionen` table. No cast exists, the column would be dropped and recreated, which cannot be done if there is data, since the column is required.
|
||||
|
||||
*/
|
||||
-- AlterTable
|
||||
ALTER TABLE "Provisionen" DROP CONSTRAINT "Provisionen_pkey",
|
||||
DROP COLUMN "id",
|
||||
ADD COLUMN "id" SERIAL NOT NULL,
|
||||
DROP COLUMN "ausweisart",
|
||||
ADD COLUMN "ausweisart" "Ausweisart" NOT NULL,
|
||||
ADD CONSTRAINT "Provisionen_pkey" PRIMARY KEY ("id");
|
||||
@@ -0,0 +1,2 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "benutzer" ADD COLUMN "partner_code" TEXT;
|
||||
12
prisma/migrations/20250924181725_flaeche_float/migration.sql
Normal file
12
prisma/migrations/20250924181725_flaeche_float/migration.sql
Normal file
@@ -0,0 +1,12 @@
|
||||
/*
|
||||
Warnings:
|
||||
|
||||
- Added the required column `ausweistyp` to the `Provisionen` table without a default value. This is not possible if the table is not empty.
|
||||
|
||||
*/
|
||||
-- AlterTable
|
||||
ALTER TABLE "Aufnahme" ALTER COLUMN "flaeche" SET DATA TYPE DOUBLE PRECISION,
|
||||
ALTER COLUMN "nutzflaeche" SET DATA TYPE DOUBLE PRECISION;
|
||||
|
||||
-- AlterTable
|
||||
ALTER TABLE "Provisionen" ADD COLUMN "ausweistyp" "AusweisTyp" NOT NULL;
|
||||
@@ -0,0 +1,5 @@
|
||||
-- AlterTable
|
||||
ALTER TABLE "Bild" ADD COLUMN "benutzer_id" TEXT;
|
||||
|
||||
-- AddForeignKey
|
||||
ALTER TABLE "Bild" ADD CONSTRAINT "Bild_benutzer_id_fkey" FOREIGN KEY ("benutzer_id") REFERENCES "benutzer"("id") ON DELETE NO ACTION ON UPDATE NO ACTION;
|
||||
10
prisma/migrations/20250925141939_float_to_int/migration.sql
Normal file
10
prisma/migrations/20250925141939_float_to_int/migration.sql
Normal file
@@ -0,0 +1,10 @@
|
||||
/*
|
||||
Warnings:
|
||||
|
||||
- You are about to alter the column `flaeche` on the `Aufnahme` table. The data in that column could be lost. The data in that column will be cast from `DoublePrecision` to `Integer`.
|
||||
- You are about to alter the column `nutzflaeche` on the `Aufnahme` table. The data in that column could be lost. The data in that column will be cast from `DoublePrecision` to `Integer`.
|
||||
|
||||
*/
|
||||
-- AlterTable
|
||||
ALTER TABLE "Aufnahme" ALTER COLUMN "flaeche" SET DATA TYPE INTEGER,
|
||||
ALTER COLUMN "nutzflaeche" SET DATA TYPE INTEGER;
|
||||
9
prisma/migrations/20251017220401_/migration.sql
Normal file
9
prisma/migrations/20251017220401_/migration.sql
Normal file
@@ -0,0 +1,9 @@
|
||||
/*
|
||||
Warnings:
|
||||
|
||||
- You are about to drop the column `anrede` on the `benutzer` table. All the data in the column will be lost.
|
||||
|
||||
*/
|
||||
-- AlterTable
|
||||
ALTER TABLE "benutzer" DROP COLUMN "anrede",
|
||||
ADD COLUMN "empfaenger" VARCHAR(100);
|
||||
@@ -41,19 +41,19 @@ model BedarfsausweisWohnen {
|
||||
volumen Float?
|
||||
dicht Boolean?
|
||||
fenster_flaeche_1 Float?
|
||||
fenster_art_1 String?
|
||||
fenster_art_1 Float?
|
||||
fenster_flaeche_2 Float?
|
||||
fenster_art_2 String?
|
||||
fenster_art_2 Float?
|
||||
dachfenster_flaeche Float?
|
||||
dachfenster_art String?
|
||||
dachfenster_art Float?
|
||||
haustuer_flaeche Float?
|
||||
haustuer_art String?
|
||||
haustuer_art Float?
|
||||
dach_bauart String? @db.VarChar
|
||||
decke_bauart String? @db.VarChar
|
||||
dach_daemmung String?
|
||||
decke_daemmung String?
|
||||
aussenwand_daemmung String?
|
||||
boden_daemmung String?
|
||||
dach_daemmung Float?
|
||||
decke_daemmung Float?
|
||||
aussenwand_daemmung Float?
|
||||
boden_daemmung Float?
|
||||
aussenwand_bauart String? @db.VarChar
|
||||
boden_bauart String? @db.VarChar
|
||||
warmwasser_verteilung String? @db.VarChar
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
enum BenutzerRolle {
|
||||
USER
|
||||
ADMIN
|
||||
RESELLER
|
||||
}
|
||||
|
||||
model Benutzer {
|
||||
@@ -16,10 +17,11 @@ model Benutzer {
|
||||
ort String? @db.VarChar(50)
|
||||
adresse String? @db.VarChar(150)
|
||||
telefon String? @db.VarChar(50)
|
||||
anrede String? @db.VarChar(50)
|
||||
empfaenger String? @db.VarChar(100)
|
||||
rolle BenutzerRolle @default(USER)
|
||||
firma String?
|
||||
lex_office_id String?
|
||||
partner_code String?
|
||||
|
||||
verified Boolean @default(false)
|
||||
|
||||
@@ -50,6 +52,8 @@ model Benutzer {
|
||||
events Event[]
|
||||
|
||||
@@map("benutzer")
|
||||
Provisionen Provisionen[]
|
||||
bilder Bild[]
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -15,6 +15,8 @@ model Bild {
|
||||
created_at DateTime @default(now())
|
||||
updated_at DateTime @updatedAt @default(now())
|
||||
|
||||
benutzer_id String?
|
||||
benutzer Benutzer? @relation(fields: [benutzer_id], references: [id], onDelete: NoAction, onUpdate: NoAction)
|
||||
aufnahme_id String?
|
||||
aufnahme Aufnahme? @relation(fields: [aufnahme_id], references: [id], onDelete: NoAction, onUpdate: NoAction)
|
||||
}
|
||||
11
prisma/schema/Provisionen.prisma
Normal file
11
prisma/schema/Provisionen.prisma
Normal file
@@ -0,0 +1,11 @@
|
||||
model Provisionen {
|
||||
id Int @id @default(autoincrement())
|
||||
ausweisart Ausweisart
|
||||
ausweistyp AusweisTyp
|
||||
provision_prozent Float
|
||||
provision_betrag Float
|
||||
benutzer_id String? @db.VarChar(11)
|
||||
benutzer Benutzer? @relation(fields: [benutzer_id], references: [id])
|
||||
created_at DateTime @default(now())
|
||||
updated_at DateTime @updatedAt
|
||||
}
|
||||
@@ -2,40 +2,77 @@
|
||||
|
||||
# === Configuration ===
|
||||
BUCKET_NAME="ibc-db-backup"
|
||||
ENDPOINT_URL="https://s3-eu-central-1.ionoscloud.com"
|
||||
ENDPOINT_URL="https://s3.eu-central-3.ionoscloud.com"
|
||||
LOCAL_DOWNLOAD_DIR="./" # Where to save the file
|
||||
DATABASE_NAME=database
|
||||
|
||||
# === Get latest file from IONOS S3 bucket ===
|
||||
LATEST_FILE=$(aws s3api list-objects-v2 \
|
||||
# === Check if a custom file is given as a command line argument ===
|
||||
if [ $# -eq 1 ]; then
|
||||
CUSTOM_FILE="$1"
|
||||
echo "🔍 Using custom file: $CUSTOM_FILE"
|
||||
# Check if file exists locally
|
||||
if [ ! -f "$CUSTOM_FILE" ]; then
|
||||
# Check if the file exists on the remote
|
||||
if ! aws s3api head-object --bucket "$BUCKET_NAME" --key "$CUSTOM_FILE" --endpoint-url "$ENDPOINT_URL" > /dev/null 2>&1; then
|
||||
echo "❌ Custom file does not exist in S3 bucket or locally."
|
||||
exit 1
|
||||
else
|
||||
echo "📥 Downloading $CUSTOM_FILE from S3"
|
||||
aws s3 cp "s3://$BUCKET_NAME/$CUSTOM_FILE" "$LOCAL_DOWNLOAD_DIR" \
|
||||
--endpoint-url "$ENDPOINT_URL"
|
||||
fi
|
||||
fi
|
||||
|
||||
LATEST_FILE="$CUSTOM_FILE"
|
||||
FILENAME=$(basename "$LATEST_FILE")
|
||||
if [[ "$FILENAME" == *.br ]]; then
|
||||
echo "🗜️ Detected compressed file: $FILENAME"
|
||||
# Remove the .br suffix for the SQL file
|
||||
SQL_FILE="${FILENAME%.br}" # Remove .br suffix
|
||||
brotli -d "$FILENAME"
|
||||
else
|
||||
SQL_FILE=$FILENAME
|
||||
fi
|
||||
else
|
||||
echo "🔍 No custom file provided, searching for latest .sql.br file in S3"
|
||||
|
||||
# === Get latest file from IONOS S3 bucket ===
|
||||
LATEST_FILE=$(aws s3api list-objects-v2 \
|
||||
--bucket "$BUCKET_NAME" \
|
||||
--prefix "data-dump" \
|
||||
--endpoint-url "$ENDPOINT_URL" \
|
||||
--query 'Contents | sort_by(@, &LastModified) | [-1].Key' \
|
||||
--output text)
|
||||
|
||||
# === Check if file was found ===
|
||||
if [ "$LATEST_FILE" == "None" ] || [ -z "$LATEST_FILE" ]; then
|
||||
# === Check if file was found ===
|
||||
if [ "$LATEST_FILE" == "None" ] || [ -z "$LATEST_FILE" ]; then
|
||||
echo "❌ No matching .sql.br file found."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
FILENAME=$(basename "$LATEST_FILE")
|
||||
SQL_FILE="${FILENAME%.br}" # Remove .br suffix
|
||||
|
||||
echo "📥 Downloading $LATEST_FILE"
|
||||
aws s3 cp "s3://$BUCKET_NAME/$LATEST_FILE" "$LOCAL_DOWNLOAD_DIR" \
|
||||
fi
|
||||
echo "🔍 Latest file found: $LATEST_FILE"
|
||||
FILENAME=$(basename "$LATEST_FILE")
|
||||
SQL_FILE="${FILENAME%.br}" # Remove .br suffix
|
||||
echo "📥 Downloading $LATEST_FILE"
|
||||
aws s3 cp "s3://$BUCKET_NAME/$LATEST_FILE" "$LOCAL_DOWNLOAD_DIR" \
|
||||
--endpoint-url "$ENDPOINT_URL"
|
||||
|
||||
# === Decompress with Brotli ===
|
||||
echo "🗜️ Decompressing $FILENAME -> $SQL_FILE"
|
||||
brotli -d "$FILENAME"
|
||||
brotli -d "$FILENAME"
|
||||
echo "🗜️ Decompressed to $SQL_FILE"
|
||||
fi
|
||||
|
||||
|
||||
# === Import into Postgres inside Docker ===
|
||||
echo "🐘 Importing into PostgreSQL (online-energieausweis-database-1:main)"
|
||||
docker exec -i "online-energieausweis-database-1" env PGPASSWORD="hHMP8cd^N3SnzGRR" \
|
||||
psql -U "main" -d "main" < "$SQL_FILE"
|
||||
echo "🐘 Importing into PostgreSQL ($DATABASE_NAME:main)"
|
||||
docker exec -i "$DATABASE_NAME" env PGPASSWORD="hHMP8cd^N3SnzGRR" \
|
||||
psql -v ON_ERROR_STOP=0 -U main -d main < "$SQL_FILE"
|
||||
|
||||
|
||||
|
||||
echo "✅ Import complete."
|
||||
|
||||
# === Optional: Clean up
|
||||
rm "$FILENAME" "$SQL_FILE"
|
||||
# If custom file was provided, do not delete it
|
||||
if [ -z "$CUSTOM_FILE" ]; then
|
||||
echo "🧹 Cleaning up downloaded files..."
|
||||
rm "$FILENAME" "$SQL_FILE"
|
||||
fi
|
||||
@@ -12,27 +12,28 @@ export const createCaller = createCallerFactory({
|
||||
"admin/nicht-ausstellen": await import("../src/pages/api/admin/nicht-ausstellen.ts"),
|
||||
"admin/registriernummer": await import("../src/pages/api/admin/registriernummer.ts"),
|
||||
"admin/stornieren": await import("../src/pages/api/admin/stornieren.ts"),
|
||||
"ausweise": await import("../src/pages/api/ausweise/index.ts"),
|
||||
"aufnahme": await import("../src/pages/api/aufnahme/index.ts"),
|
||||
"auth/access-token": await import("../src/pages/api/auth/access-token.ts"),
|
||||
"auth/passwort-vergessen": await import("../src/pages/api/auth/passwort-vergessen.ts"),
|
||||
"auth/refresh-token": await import("../src/pages/api/auth/refresh-token.ts"),
|
||||
"ausweise": await import("../src/pages/api/ausweise/index.ts"),
|
||||
"bedarfsausweis-gewerbe/[id]": await import("../src/pages/api/bedarfsausweis-gewerbe/[id].ts"),
|
||||
"bedarfsausweis-gewerbe": await import("../src/pages/api/bedarfsausweis-gewerbe/index.ts"),
|
||||
"bedarfsausweis-wohnen/[id]": await import("../src/pages/api/bedarfsausweis-wohnen/[id].ts"),
|
||||
"bedarfsausweis-wohnen": await import("../src/pages/api/bedarfsausweis-wohnen/index.ts"),
|
||||
"bilder/[id]": await import("../src/pages/api/bilder/[id].ts"),
|
||||
"geg-nachweis-gewerbe/[id]": await import("../src/pages/api/geg-nachweis-gewerbe/[id].ts"),
|
||||
"geg-nachweis-gewerbe": await import("../src/pages/api/geg-nachweis-gewerbe/index.ts"),
|
||||
"bilder/[id]": await import("../src/pages/api/bilder/[id].ts"),
|
||||
"geg-nachweis-wohnen/[id]": await import("../src/pages/api/geg-nachweis-wohnen/[id].ts"),
|
||||
"geg-nachweis-wohnen": await import("../src/pages/api/geg-nachweis-wohnen/index.ts"),
|
||||
"bedarfsausweis-wohnen/[id]": await import("../src/pages/api/bedarfsausweis-wohnen/[id].ts"),
|
||||
"bedarfsausweis-wohnen": await import("../src/pages/api/bedarfsausweis-wohnen/index.ts"),
|
||||
"objekt": await import("../src/pages/api/objekt/index.ts"),
|
||||
"rechnung/[id]": await import("../src/pages/api/rechnung/[id].ts"),
|
||||
"rechnung/anfordern": await import("../src/pages/api/rechnung/anfordern.ts"),
|
||||
"rechnung": await import("../src/pages/api/rechnung/index.ts"),
|
||||
"ticket": await import("../src/pages/api/ticket/index.ts"),
|
||||
"user/autocreate": await import("../src/pages/api/user/autocreate.ts"),
|
||||
"user": await import("../src/pages/api/user/index.ts"),
|
||||
"user/self": await import("../src/pages/api/user/self.ts"),
|
||||
"ticket": await import("../src/pages/api/ticket/index.ts"),
|
||||
"verbrauchsausweis-wohnen/[id]": await import("../src/pages/api/verbrauchsausweis-wohnen/[id].ts"),
|
||||
"verbrauchsausweis-wohnen": await import("../src/pages/api/verbrauchsausweis-wohnen/index.ts"),
|
||||
"verbrauchsausweis-gewerbe/[id]": await import("../src/pages/api/verbrauchsausweis-gewerbe/[id].ts"),
|
||||
|
||||
@@ -1,14 +1,6 @@
|
||||
import { api } from "astro-typesafe-api/client";
|
||||
import Cookies from "js-cookie";
|
||||
import { API_ACCESS_TOKEN_COOKIE_NAME } from "#lib/constants.js";
|
||||
import {
|
||||
AufnahmeClient,
|
||||
BedarfsausweisWohnenClient,
|
||||
BildClient,
|
||||
ObjektClient,
|
||||
VerbrauchsausweisGewerbeClient,
|
||||
VerbrauchsausweisWohnenClient,
|
||||
} from "#components/Ausweis/types.js";
|
||||
import {
|
||||
Aufnahme,
|
||||
BedarfsausweisWohnen,
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
import { dialogs } from "../../../svelte-dialogs.config";
|
||||
import { loginClient } from "#lib/login";
|
||||
import { addNotification } from "#components/Notifications/shared";
|
||||
|
||||
export async function spawnLoginPrompt() {
|
||||
const result = await dialogs.prompt(
|
||||
[
|
||||
{
|
||||
label: "Email",
|
||||
type: "email",
|
||||
required: true,
|
||||
placeholder: "Email",
|
||||
name: "email"
|
||||
},
|
||||
{
|
||||
label: "Passwort",
|
||||
type: "password",
|
||||
name: "passwort",
|
||||
required: true,
|
||||
placeholder: "********",
|
||||
},
|
||||
],
|
||||
{
|
||||
title: "Login",
|
||||
submitButtonText: "Einloggen",
|
||||
cancelButtonText: "Abbrechen",
|
||||
}
|
||||
);
|
||||
|
||||
if (!result) return false;
|
||||
|
||||
const [email, passwort] = result;
|
||||
|
||||
const loginResult = await loginClient(email, passwort);
|
||||
|
||||
if (loginResult === null) {
|
||||
addNotification({
|
||||
type: "error",
|
||||
message: "Einloggen fehlgeschlagen",
|
||||
dismissable: true,
|
||||
subtext: "Bitte überprüfen Sie ihre Eingaben und versuchen es erneut.",
|
||||
timeout: 5000,
|
||||
})
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
import { dialogs } from "../../../svelte-dialogs.config.js";
|
||||
import { addNotification } from "#components/Notifications/shared.js";
|
||||
import { api } from "astro-typesafe-api/client";
|
||||
|
||||
export async function spawnSignupPrompt() {
|
||||
const result = await dialogs.prompt(
|
||||
[
|
||||
{
|
||||
label: "Vorname",
|
||||
type: "text",
|
||||
required: true,
|
||||
placeholder: "Vorname",
|
||||
name: "vorname"
|
||||
},
|
||||
{
|
||||
label: "Name",
|
||||
type: "text",
|
||||
required: true,
|
||||
placeholder: "Name",
|
||||
name: "name"
|
||||
},
|
||||
{
|
||||
label: "Email",
|
||||
type: "email",
|
||||
required: true,
|
||||
placeholder: "Email",
|
||||
name: "email"
|
||||
},
|
||||
{
|
||||
label: "Passwort",
|
||||
type: "password",
|
||||
name: "passwort",
|
||||
required: true,
|
||||
placeholder: "********",
|
||||
},
|
||||
],
|
||||
{
|
||||
title: "Registrieren",
|
||||
submitButtonText: "Registrieren",
|
||||
cancelButtonText: "Abbrechen",
|
||||
}
|
||||
);
|
||||
|
||||
if (!result) return false;
|
||||
|
||||
const [vorname, name, email, passwort] = result;
|
||||
|
||||
try {
|
||||
const response = await api.user.PUT.fetch({
|
||||
email,
|
||||
passwort,
|
||||
vorname,
|
||||
name,
|
||||
});
|
||||
|
||||
return true;
|
||||
} catch(e) {
|
||||
addNotification({
|
||||
type: "error",
|
||||
message: "Registrieren fehlgeschlagen",
|
||||
dismissable: true,
|
||||
subtext: "Ein Fehler ist aufgetreten, vielleicht wird die angegebene Email bereits verwendet.",
|
||||
timeout: 5000,
|
||||
})
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -170,7 +170,6 @@ export async function benutzerSpeichern(benutzer: Partial<Benutzer>): Promise<st
|
||||
email: benutzer.email,
|
||||
passwort: "",
|
||||
adresse: benutzer.adresse ?? null,
|
||||
anrede: benutzer.anrede ?? null,
|
||||
firma: benutzer.firma ?? null,
|
||||
vorname: benutzer.vorname ?? null,
|
||||
ort: benutzer.ort ?? null,
|
||||
|
||||
133
src/components/Abrechnung/AbrechnungTable.svelte
Normal file
133
src/components/Abrechnung/AbrechnungTable.svelte
Normal file
@@ -0,0 +1,133 @@
|
||||
<script lang="ts">
|
||||
import { Aufnahme, BedarfsausweisWohnen, Enums, Objekt, Provisionen, Rechnung, VerbrauchsausweisGewerbe, VerbrauchsausweisWohnen } from "#lib/server/prisma.js";
|
||||
import moment from "moment-timezone"
|
||||
import { DatePicker } from "@svelte-plugins/datepicker"
|
||||
import { getProvision } from "#lib/provision.js";
|
||||
export let bestellungen: (Rechnung & {
|
||||
ausweis: (VerbrauchsausweisWohnen | BedarfsausweisWohnen | VerbrauchsausweisGewerbe) & { aufnahme: Aufnahme & { objekt: Objekt }}
|
||||
})[];
|
||||
export let provisionen: Provisionen[];
|
||||
export let email: string;
|
||||
export let startdatum: Date;
|
||||
export let enddatum: Date;
|
||||
moment.locale("de");
|
||||
moment.tz.setDefault("Europe/Berlin");
|
||||
|
||||
const bestellungenNachMonat: Record<string, (typeof bestellungen)> = {};
|
||||
for (const bestellung of bestellungen) {
|
||||
const monat = moment(bestellung.created_at).format("Y-MM");
|
||||
if (monat in bestellungenNachMonat) {
|
||||
bestellungenNachMonat[monat].push(bestellung)
|
||||
} else {
|
||||
bestellungenNachMonat[monat] = [bestellung]
|
||||
}
|
||||
}
|
||||
|
||||
// Wir brauchen alle Monate zwischen dem ersten Mal, dass der partner_code benutzt wurde bis zum heutigen Zeitpunkt.
|
||||
function getMonthlyPeriods(from: Date, to: Date): moment.Moment[] {
|
||||
const start = moment(from).startOf('month');
|
||||
const end = moment(to).endOf('month');
|
||||
|
||||
const monthsArray: moment.Moment[] = [];
|
||||
const current = start.clone();
|
||||
|
||||
while (current.isBefore(end)) {
|
||||
monthsArray.push(current.clone());
|
||||
current.add(1, 'month');
|
||||
}
|
||||
|
||||
return monthsArray.reverse(); // Most recent month first
|
||||
}
|
||||
|
||||
const periods = getMonthlyPeriods(startdatum, enddatum)
|
||||
|
||||
|
||||
let isOpen = false;
|
||||
$: formatiertesStartDatum = moment(startdatum).format("DD.MM.YYYY");
|
||||
$: formatiertesEndDatum = moment(enddatum).format("DD.MM.YYYY");
|
||||
function toggleDatePicker() {
|
||||
isOpen = !isOpen;
|
||||
}
|
||||
|
||||
const onChange = ({ startDate, endDate }: { startDate: number, endDate: number }) => {
|
||||
window.location.href = `/dashboard/abrechnung?start=${moment(startDate).format("YYYY-MM-DD")}&end=${moment(endDate).format("YYYY-MM-DD")}`;
|
||||
};
|
||||
</script>
|
||||
|
||||
<div class="fixed top-0 left-0 right-0 bg-white p-4 shadow z-10">
|
||||
<div class="flex justify-between items-center">
|
||||
<DatePicker bind:isOpen bind:startDate={startdatum} bind:endDate={enddatum} enableFutureDates={false} isRange={true} onDateChange={onChange} isMultipane={true}>
|
||||
<input type="text" class="w-min" readonly value={`${formatiertesStartDatum} - ${formatiertesEndDatum}`} on:click={toggleDatePicker} />
|
||||
</DatePicker>
|
||||
<p>Abrechnungsübersicht für <strong>{email}</strong></p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<main class="my-24 flex flex-col justify-center max-w-6xl mx-auto px-4">
|
||||
{#if !bestellungen || bestellungen.length === 0}
|
||||
<p class="text-center text-gray-500">Keine Bestellungen gefunden.</p>
|
||||
{/if}
|
||||
{#each periods as dt}
|
||||
{@const jahrMonat = dt.format("Y-MM")}
|
||||
{#if jahrMonat in bestellungenNachMonat && bestellungenNachMonat[jahrMonat].length > 0}
|
||||
<!-- Echo dropdown foreach month. -->
|
||||
{@const provisionMonat = bestellungenNachMonat[jahrMonat].reduce((acc, bestellung) => {
|
||||
const { provision_prozent, provision_betrag } = getProvision(bestellung.ausweis.ausweisart, bestellung.ausweis.ausweistyp, provisionen);
|
||||
return acc + provision_betrag;
|
||||
}, 0)}
|
||||
|
||||
<details class="group" open>
|
||||
<summary class="flex justify-between items-center cursor-pointer p-4 bg-gray-100 hover:bg-gray-200">
|
||||
<span class="font-semibold">{moment(dt).format("MMMM YYYY")}</span>
|
||||
<div class="flex flex-row gap-4 items-center">
|
||||
<a href={`/dashboard/abrechnung/monatlich.pdf?d=${moment(dt).format("YYYY-MM")}`} target="_blank" rel="noreferrer noopener">PDF generieren</a>
|
||||
<span class="text-gray-500">{provisionMonat.toFixed(2)} €</span>
|
||||
<svg class="w-4 h-4 transition-transform duration-300 group-open:rotate-180" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="6 9 12 15 18 9"></polyline>
|
||||
</svg>
|
||||
</div>
|
||||
</summary>
|
||||
<div class="p-4">
|
||||
<table class="w-full mb-4 border-collapse border border-gray-300">
|
||||
<thead>
|
||||
<tr class="bg-primary text-white w-full">
|
||||
<td class="text-center p-2 font-bold">ID</td>
|
||||
<td class="text-center p-2 font-bold">Datum</td>
|
||||
<td class="text-center p-2 font-bold">Ausweis</td>
|
||||
<td class="text-center p-2 font-bold">Provision in %</td>
|
||||
<td class="text-center p-2 font-bold">Betrag Netto</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="text-sm">
|
||||
{#each bestellungenNachMonat[jahrMonat] as bestellung}
|
||||
{@const provisionBestellung = getProvision(bestellung.ausweis.ausweisart, bestellung.ausweis.ausweistyp, provisionen)}
|
||||
<tr class="border-b border-gray-300 hover:bg-gray-100">
|
||||
<td class="text-center py-2 px-4 w-24" style="font-family: monospace;">{bestellung.ausweis.id}</td>
|
||||
<td class="text-center py-2 font-bold w-32">{moment(bestellung.created_at).format("DD.MM.YYYY HH:mm")}</td>
|
||||
<td class="text-center py-2 w-32">{bestellung.ausweis.ausweisart} {bestellung.ausweis.ausweistyp}</td>
|
||||
<td class="text-center py-2 w-32">{provisionBestellung?.provision_prozent || 0} %</td>
|
||||
<td class="text-right py-2 w-24" style="font-family: monospace;">{provisionBestellung?.provision_betrag.toFixed(2)} €</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</details>
|
||||
{:else if !bestellungenNachMonat[jahrMonat] || bestellungenNachMonat[jahrMonat].length === 0}
|
||||
<details class="group">
|
||||
<summary class="flex justify-between items-center cursor-pointer p-4 bg-gray-100 hover:bg-gray-200">
|
||||
<span class="font-semibold">{moment(dt).format("MMMM YYYY")}</span>
|
||||
<div class="flex flex-row gap-4 items-center">
|
||||
<span class="text-gray-500">Keine Bestellungen gefunden</span>
|
||||
<svg class="w-4 h-4 transition-transform duration-300 group-open:rotate-180" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
||||
<polyline points="6 9 12 15 18 9"></polyline>
|
||||
</svg>
|
||||
</div>
|
||||
</summary>
|
||||
<div class="p-4 border-t border-gray-200">
|
||||
<p class="text-gray-500">Für diesen Monat liegen uns keine Bestellungen über ihren Resellercode vor.</p>
|
||||
</div>
|
||||
</details>
|
||||
{/if}
|
||||
{/each}
|
||||
</main>
|
||||
@@ -1,109 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { BedarfsausweisWohnen, Enums, Rechnung, VerbrauchsausweisGewerbe, VerbrauchsausweisWohnen } from "#lib/server/prisma.js";
|
||||
import moment from "moment";
|
||||
import { format } from "sharp";
|
||||
|
||||
export let bestellungen: (Rechnung & {
|
||||
verbrauchsausweis_wohnen: VerbrauchsausweisWohnen | null,
|
||||
verbrauchsausweis_gewerbe: VerbrauchsausweisGewerbe | null,
|
||||
bedarfsausweis_wohnen: BedarfsausweisWohnen | null,
|
||||
})[];
|
||||
export let provisionen: Record<Enums.Ausweisart, number>;
|
||||
export let partnerCodeErstesMal: Date;
|
||||
|
||||
const bestellungenNachMonat: Record<string, (typeof bestellungen)> = {};
|
||||
for (const bestellung of bestellungen) {
|
||||
const monat = moment(bestellung.created_at).format("Y-m");
|
||||
if (monat in bestellungenNachMonat) {
|
||||
bestellungenNachMonat[monat].push(bestellung)
|
||||
} else {
|
||||
bestellungenNachMonat[monat] = [bestellung]
|
||||
}
|
||||
}
|
||||
|
||||
// Wir brauchen alle Monate zwischen dem ersten Mal, dass der partner_code benutzt wurde bis zum heutigen Zeitpunkt.
|
||||
const months: Record<string, string> = {
|
||||
"01": "Januar", "02": "Februar", "03": "März", "04": "April",
|
||||
"05": "Mai", "06": "Juni", "07": "Juli", "08": "August",
|
||||
"09": "September", "10": "Oktober", "11": "November", "12": "Dezember"
|
||||
};
|
||||
|
||||
function getMonthlyPeriods(minTime?: Date): moment.Moment[] {
|
||||
const min = minTime ? moment(minTime) : moment();
|
||||
const start = min.clone().startOf('month');
|
||||
|
||||
const end = moment().add(1, 'month').startOf('month');
|
||||
|
||||
const monthsArray: moment.Moment[] = [];
|
||||
const current = start.clone();
|
||||
|
||||
while (current.isBefore(end)) {
|
||||
monthsArray.push(current.clone());
|
||||
current.add(1, 'month');
|
||||
}
|
||||
|
||||
return monthsArray.reverse(); // Most recent month first
|
||||
}
|
||||
|
||||
const periods = getMonthlyPeriods(partnerCodeErstesMal)
|
||||
</script>
|
||||
|
||||
{#each periods as dt}
|
||||
{@const jahrMonat = dt.format("Y-m")}
|
||||
{#if jahrMonat in bestellungenNachMonat && bestellungenNachMonat[jahrMonat].length > 0}
|
||||
<!-- Echo dropdown foreach month. -->
|
||||
{@const provisionMonat = bestellungenNachMonat[jahrMonat].reduce((acc, bestellung) => {
|
||||
if (bestellung.verbrauchsausweis_wohnen) {
|
||||
return acc + provisionen[Enums.Ausweisart.VerbrauchsausweisWohnen];
|
||||
}
|
||||
if (bestellung.bedarfsausweis_wohnen) {
|
||||
return acc + provisionen[Enums.Ausweisart.BedarfsausweisWohnen];
|
||||
}
|
||||
if (bestellung.verbrauchsausweis_gewerbe) {
|
||||
return acc + provisionen[Enums.Ausweisart.VerbrauchsausweisGewerbe];
|
||||
}
|
||||
|
||||
return acc;
|
||||
}) * 1.19}
|
||||
|
||||
<div onclick="$(this).nextUntil('.dropdown_month').filter('table').toggle(); $('#betrag_gesamt').html('Abrechnungsbetrag $month_name: <b>$provision_month €</b>')" class='dropdown_month'>
|
||||
<p>$month_name $year_name - Klicke, um Tabelle anzuzeigen</p>
|
||||
<a target='_blank' rel='noreferrer noopener' href='/user/abrechnung/pdf.php?month={dt.format("m")}&year={dt.format("Y")}'>PDF Ansehen</a>
|
||||
</div>
|
||||
<table id='QTT' style='margin-top: 0 !important; display:none;'>
|
||||
<thead>
|
||||
<tr>
|
||||
<td style='text-align:center;'>ID</td>
|
||||
<td style='text-align:center;'>DATUM</td>
|
||||
<td style='width:11em;text-align:center;'>GEBÄUDEADRESSE </td>
|
||||
<td style='width:11em;text-align:center;'>PLZ </td>
|
||||
<td style='width:11em;text-align:center;'>ORT </td>
|
||||
<td style='text-align:center;'>AUSWEIS</td>
|
||||
<td style='width:5em;text-align:center;'>BETRAG NETTO</td>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{#each bestellungenNachMonat[jahrMonat] as bestellung}
|
||||
{@const provisionBestellung = bestellung.verbrauchsausweis_wohnen ? provisionen[Enums.Ausweisart.VerbrauchsausweisWohnen] : bestellung.verbrauchsausweis_gewerbe ? provisionen[Enums.Ausweisart.VerbrauchsausweisGewerbe] : provisionen[Enums.Ausweisart.BedarfsausweisWohnen]}
|
||||
<tr>
|
||||
<td style='width:1em;text-align:center;'>{bestellung.id}</td>
|
||||
<td style='width:9em;text-align:center;font-weight:bold;'>{moment(bestellung.created_at).format("Y/m/d")}</td>
|
||||
<td style='width:8em;text-align:left;'>{bestellung["objekt_strasse"]}</td>
|
||||
<td style='width:5em;text-align:center;'>{bestellung["objekt_plz"]}</td>
|
||||
<td style='width:6em;text-align:left;'>{bestellung["objekt_ort"]}</td>
|
||||
<td style='width:3em;text-align:center;'>{bestellung['ausweisart']}</td>
|
||||
<td style='width:8em;text-align:right;'>{provisionBestellung} €</td>
|
||||
</tr>
|
||||
{/each}
|
||||
</table>
|
||||
{/if}
|
||||
{/each}
|
||||
<!-- foreach ($period as $dt) {
|
||||
$year_month = $dt->format("Y-m");
|
||||
$month_name = $months[$dt->format("m")];
|
||||
if ((new DateTime(date("m/d/Y", strtotime($EEtimestamp))))->format("d") - (new DateTime(date("m/d/Y", strtotime($SStimestamp))))->format("d") == 1) {
|
||||
$Pall = $dt->format("d/m/Y") . ' bis ' . (new DateTime($today))->format("d/m/Y");
|
||||
} -->
|
||||
|
||||
|
||||
<!-- } -->
|
||||
@@ -75,7 +75,7 @@ xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8
|
||||
>
|
||||
<option disabled selected value={null}>Bitte auswählen</option>
|
||||
|
||||
{#if ausweisart==Enums.Ausweisart.VerbrauchsausweisWohnen || ausweisart === Enums.Ausweisart.GEGNachweisWohnen || ausweisart === Enums.Ausweisart.BedarfsausweisWohnen || ausweisart === Enums.Ausweisart.BedarfsausweisGewerbe}
|
||||
{#if ausweisart==Enums.Ausweisart.VerbrauchsausweisWohnen || ausweisart === Enums.Ausweisart.GEGNachweisWohnen || ausweisart === Enums.Ausweisart.BedarfsausweisWohnen}
|
||||
<option value="Einfamilienhaus">Einfamilienhaus</option>
|
||||
<option value="Freistehendes Einfamilienhaus">Freistehendes Einfamilienhaus</option>
|
||||
<option value="Freistehendes Zweifamilienhaus">Freistehendes Zweifamilienhaus</option>
|
||||
@@ -87,7 +87,7 @@ xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8
|
||||
<option value="Atrium-Bungalow">Atrium-Bungalow</option>
|
||||
<option value="Winkelbungalow">Winkelbungalow</option>
|
||||
|
||||
{:else if ausweisart==Enums.Ausweisart.VerbrauchsausweisGewerbe || ausweisart=== Enums.Ausweisart.GEGNachweisGewerbe}
|
||||
{:else if ausweisart==Enums.Ausweisart.VerbrauchsausweisGewerbe || ausweisart=== Enums.Ausweisart.GEGNachweisGewerbe || ausweisart === Enums.Ausweisart.BedarfsausweisGewerbe}
|
||||
<option value="Verwaltungsgebäude (allgemein)">Verwaltungsgebäude (allgemein)</option>
|
||||
<option value="Parlaments- und Gerichtsgebäude">Parlaments- und Gerichtsgebäude</option>
|
||||
<option value="Ministerien u. Ämter u. Behörden">Ministerien u. Ämter u. Behörden</option>
|
||||
@@ -186,6 +186,7 @@ xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8
|
||||
onlyUnique={true}
|
||||
minlength={4}
|
||||
maxlength={4}
|
||||
required={true}
|
||||
onFocusIn={() => {
|
||||
addNotification({
|
||||
message: "Info",
|
||||
@@ -249,6 +250,7 @@ xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8
|
||||
minlength={4}
|
||||
maxlength={4}
|
||||
onlyUnique={true}
|
||||
required={true}
|
||||
onFocusIn={() => {
|
||||
addNotification({
|
||||
message: "Info",
|
||||
@@ -287,6 +289,7 @@ xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8
|
||||
onlyUnique={true}
|
||||
minlength={4}
|
||||
maxlength={4}
|
||||
required={true}
|
||||
onFocusIn={() => {
|
||||
addNotification({
|
||||
message: "Info",
|
||||
|
||||
@@ -44,11 +44,17 @@
|
||||
// Wir holen uns die Daten aus dem Formular
|
||||
const data = new FormData(form);
|
||||
// Und gleichen diese mit allen Feldern ab die "required" sind, damit stellen wir sicher, dass alles richtig ausgefüllt wurde.
|
||||
(form.querySelectorAll("select[name][required],input[name][required]") as NodeListOf<HTMLInputElement | HTMLSelectElement>).forEach((element) => {
|
||||
(form.querySelectorAll("select[name][required], input[name][required]") as NodeListOf<HTMLInputElement | HTMLSelectElement>).forEach((element) => {
|
||||
const value = data.get(element.getAttribute("name") as string)
|
||||
|
||||
if (!value && element.required) {
|
||||
element.setCustomValidity("Eine Auswahl ist verpflichtend.")
|
||||
element.dataset["isinvalid"] = "true"
|
||||
|
||||
element.addEventListener("change", () => {
|
||||
element.setCustomValidity("")
|
||||
element.dataset["isinvalid"] = "false"
|
||||
})
|
||||
} else {
|
||||
element.setCustomValidity("")
|
||||
}
|
||||
@@ -178,7 +184,15 @@ sm:grid-cols-[1fr_min-content_min-content_min-content] sm:justify-self-end">
|
||||
|
||||
<Overlay bind:hidden={loginOverlayHidden}>
|
||||
<div class="bg-white w-full max-w-screen-sm py-8">
|
||||
<EmbeddedAuthFlowModule onLogin={loginAction} email={""}></EmbeddedAuthFlowModule>
|
||||
<EmbeddedAuthFlowModule onLogin={loginAction} email={""} route="signup" title={
|
||||
{
|
||||
login: "Ausweisdaten speichern",
|
||||
signup: "Ausweisdaten speichern"
|
||||
}
|
||||
} buttonText={{
|
||||
login: "Speichern",
|
||||
signup: "Speichern"
|
||||
}}></EmbeddedAuthFlowModule>
|
||||
</div>
|
||||
</Overlay>
|
||||
|
||||
@@ -214,8 +228,8 @@ grid-cols-1 gap-x-2 gap-y-4
|
||||
/>
|
||||
|
||||
<div class="text-center xs:text-left justify-self-stretch">
|
||||
<b>Verbrauchsausweis online</b><br>inkl. ausführlicher telefonischer
|
||||
Beratung
|
||||
<b>Selbsteingabe online</b><br>inkl. ausführlicher telefonischer
|
||||
Beratung!
|
||||
</div>
|
||||
|
||||
<div class="text-center xs:text-right">
|
||||
@@ -234,7 +248,7 @@ grid-cols-1 gap-x-2 gap-y-4
|
||||
/>
|
||||
|
||||
<div class="text-center xs:text-left justify-self-stretch">
|
||||
<b>Verbrauchsausweis offline</b><br>Sie schicken uns 3 Verbrauchsabrechnungen zu)
|
||||
<b>Wir übernehmen die Eingabe</b><br>Sie übermitteln die nötigen Unterlagen per Upload oder E-Mail.
|
||||
</div>
|
||||
|
||||
<div class="text-center xs:text-right">
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
<script lang="ts">
|
||||
import HelpLabel from "#components/labels/HelpLabel.svelte";
|
||||
import Inputlabel from "#components/labels/InputLabel.svelte";
|
||||
import PlzSuche from "#components/PlzSuche.svelte";
|
||||
|
||||
|
||||
import ZipSearch from "#components/PlzSuche.svelte";
|
||||
import { Enums } from "#lib/client/prisma.js";
|
||||
import { AufnahmeClient, ObjektClient } from "./types.js";
|
||||
|
||||
@@ -11,6 +10,24 @@
|
||||
export let objekt: ObjektClient;
|
||||
|
||||
export let ausweisart: Enums.Ausweisart;
|
||||
|
||||
function onlyAllowIntegerInput(event: KeyboardEvent) {
|
||||
const charCode = event.which || event.keyCode;
|
||||
// Allow only numbers (0-9) and control keys (e.g., backspace)
|
||||
if (charCode < 48 || charCode > 57) {
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
function onlyAllowPastingIntegers(event: ClipboardEvent) {
|
||||
const clipboardData = event.clipboardData || (window as any).clipboardData;
|
||||
const pastedData = clipboardData.getData("Text");
|
||||
if (!/^\d+$/.test(pastedData)) {
|
||||
event.preventDefault();
|
||||
}
|
||||
}
|
||||
|
||||
let ortInput: HTMLInputElement;
|
||||
</script>
|
||||
|
||||
<div
|
||||
@@ -53,9 +70,12 @@ xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8
|
||||
<div class="input-noHelp">
|
||||
<Inputlabel title="PLZ *"></Inputlabel>
|
||||
|
||||
<ZipSearch
|
||||
<PlzSuche
|
||||
bind:zip={objekt.plz}
|
||||
bind:city={objekt.ort}
|
||||
onchange={(e) => {
|
||||
ortInput.dispatchEvent(new Event('change'));
|
||||
}}
|
||||
name="plz"
|
||||
/>
|
||||
</div>
|
||||
@@ -69,6 +89,7 @@ xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8
|
||||
readonly={false}
|
||||
required
|
||||
bind:value={objekt.ort}
|
||||
bind:this={ortInput}
|
||||
type="text"
|
||||
/>
|
||||
|
||||
@@ -97,10 +118,11 @@ xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8
|
||||
data-test="flaeche"
|
||||
maxlength="4"
|
||||
type="number"
|
||||
step="1"
|
||||
required
|
||||
autocomplete="off"
|
||||
data-rule-minlength="2"
|
||||
data-msg-minlength="min. 2 Zeichen"
|
||||
on:keypress={onlyAllowIntegerInput}
|
||||
on:paste={onlyAllowPastingIntegers}
|
||||
bind:value={aufnahme.flaeche}
|
||||
/>
|
||||
|
||||
@@ -124,7 +146,10 @@ xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8
|
||||
data-test="nutzflaeche"
|
||||
maxlength="4"
|
||||
type="number"
|
||||
step="1"
|
||||
required
|
||||
on:keypress={onlyAllowIntegerInput}
|
||||
on:paste={onlyAllowPastingIntegers}
|
||||
bind:value={aufnahme.nutzflaeche}
|
||||
/>
|
||||
|
||||
|
||||
@@ -186,7 +186,7 @@ xl:grid-cols-2 xl:gap-x-8 xl:gap-y-8
|
||||
>
|
||||
<option>Bitte auswählen</option>
|
||||
{#each arrayRange(2.1, 4.5, 0.1) as step}
|
||||
<option value={step}>{step.toFixed(2)} m</option>
|
||||
<option value={step} selected={ausweis.geschosshoehe === step}>{step.toFixed(2)} m</option>
|
||||
{/each}
|
||||
</select>
|
||||
|
||||
|
||||
@@ -60,6 +60,8 @@
|
||||
return centeredValue;
|
||||
}
|
||||
|
||||
|
||||
|
||||
let translation_1 = 0;
|
||||
let translation_2 = 0;
|
||||
$: {
|
||||
@@ -74,6 +76,7 @@
|
||||
if (!result) {
|
||||
return;
|
||||
}
|
||||
|
||||
translation_1 = Math.max(
|
||||
0,
|
||||
Math.min(
|
||||
|
||||
@@ -16,7 +16,7 @@ const brennstoffe: [
|
||||
["Flüssiggas", "kg", 13.0, 1.1, 0.27],
|
||||
["Braunkohle", "kg", 5.5, 1.2, 0.43],
|
||||
["Holzhackschnitzel", "SRm", 650.0, 0.2, 0.02],
|
||||
["Strommix", "kWh", 1.0, 2.4, 0.56],
|
||||
["Strommix", "kWh", 1.0, 1.8, 0.56],
|
||||
["Fernwärme KWK FB", "kWh", 1.0, 0.7, 0.3],
|
||||
["Nahwärme KWK FB", "kWh", 1.0, 0.7, 0.3],
|
||||
["Heizöl EL", "kWh", 1.0, 1.1, 0.31],
|
||||
@@ -41,6 +41,10 @@ const brennstoffe: [
|
||||
["Fernwärme KWK EB", "kWh", 1.0, 0.0, 0.04],
|
||||
["Fernwärme HKW FB", "kWh", 1.0, 1.3, 0.4],
|
||||
["Fernwärme HKW EB", "kWh", 1.0, 0.1, 0.06],
|
||||
["Fernwärme Hamburg", "kWh", 1.0, 0.33, 0.064],
|
||||
["Fernwärme Erfurt", "kWh", 1.0, 0.3, 0],
|
||||
["Fernwärme Neumünster", "kWh", 1.0, 0.28, 0.0133],
|
||||
["Fernwärme Pforzheim", "kWh", 1.0, 0.25, 0],
|
||||
["Erdgas", "kWh", 1.0, 1.1, 0.24],
|
||||
["Heizöl", "kWh", 1.0, 1.1, 0.31],
|
||||
["Heizöl", "l", 10.0, 1.1, 0.31],
|
||||
|
||||
@@ -257,6 +257,12 @@
|
||||
|
||||
let bedarfsausweisFileInput: HTMLInputElement;
|
||||
let bedarfsausweisAdditionalInput: HTMLInputElement;
|
||||
|
||||
let dropdownOpen = false;
|
||||
|
||||
function toggleDropdown() {
|
||||
dropdownOpen = !dropdownOpen;
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="relative bg-base-200 border border-base-300 rounded-lg p-4 mx-2">
|
||||
@@ -273,30 +279,41 @@
|
||||
{/if}
|
||||
<div class="card-body">
|
||||
<div
|
||||
class="flex justify-end mb-2 dropdown dropdown-bottom absolute top-4 right-4"
|
||||
class="mb-2 dropdown dropdown-bottom absolute top-4 right-4 bg-base-100"
|
||||
>
|
||||
<button class="rounded-full p-2.5 hover:bg-base-100">
|
||||
<button class="rounded-full p-2.5 hover:bg-base-100" on:click={toggleDropdown}>
|
||||
<DotsVertical size={15} />
|
||||
</button>
|
||||
</div>
|
||||
{#if dropdownOpen}
|
||||
<ul
|
||||
tabindex="-1"
|
||||
class="dropdown-content z-[1] menu p-2 shadow bg-base-100 rounded-box w-64 gap-2"
|
||||
class="z-[1] menu p-2 shadow bg-base-100 rounded-box w-64 gap-2 border"
|
||||
>
|
||||
<li>
|
||||
<button on:click={ausweisStornieren}
|
||||
><CrossCircled size={15} />Ausweis Stornieren</button
|
||||
<!-- <div>
|
||||
<button on:click={ausweisStornieren} class="flex items-center gap-2" disabled
|
||||
><CrossCircled size={15} />Stornieren</button
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<button><Pencil2 size={15} /> Als Vorlage benutzen</button>
|
||||
</li>
|
||||
<li>
|
||||
<button on:click={() => hilfeModal.showModal()}
|
||||
</div>
|
||||
<div>
|
||||
<button class="flex items-center gap-2" disabled
|
||||
><Pencil2 size={15} /> Als Vorlage</button>
|
||||
</div>
|
||||
<div>
|
||||
<button on:click={() => hilfeModal.showModal()} class="flex items-center gap-2" disabled
|
||||
><QuestionMarkCircled size={15} /> Hilfe Erhalten</button
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
</div> -->
|
||||
{#if ausweis.bestellt && rechnung}
|
||||
<div>
|
||||
<a href="/dashboard/rechnung/aendern?rechnungid={rechnung.id}"><button> Adresse ändern</button
|
||||
></a>
|
||||
</div>
|
||||
{/if}
|
||||
</ul>
|
||||
{/if}
|
||||
|
||||
|
||||
<div class="flex flex-row flex-wrap items-center gap-2">
|
||||
{#if ausweis.ausgestellt}
|
||||
<span class="bg-green-600 px-2 py-0.5 text-sm font-semibold rounded-lg text-white">Ausgestellt</span>
|
||||
@@ -729,6 +746,7 @@
|
||||
<div class="card-body">
|
||||
<div class="flex flex-col flex-wrap items-left gap-2">
|
||||
|
||||
{#if aufnahme.bilder.length > 0}
|
||||
<h3 class="font-semibold text-lg">Bilder</h3>
|
||||
<div class="grid grid-cols-[1fr] md:grid-cols-[1fr,1fr,1fr] lg:grid-cols-[1fr,1fr,1fr] justify-start items-center gap-2">
|
||||
{#each aufnahme.bilder as bild, i (i)}
|
||||
@@ -736,6 +754,8 @@
|
||||
{/each}
|
||||
</div>
|
||||
<hr>
|
||||
{/if}
|
||||
{#if aufnahme.unterlagen.length > 0}
|
||||
<h3 class="font-semibold text-lg">Unterlagen</h3>
|
||||
<div class="text-sm">
|
||||
{#if aufnahme.unterlagen.length > 0}
|
||||
@@ -744,9 +764,11 @@
|
||||
{/each}
|
||||
{/if}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="dropdown dropdown-top items-end absolute bottom-4 right-4 z-50">
|
||||
<!-- Benachrichtigungen -->
|
||||
<!-- <div class="dropdown dropdown-top items-end absolute bottom-4 right-4 z-50">
|
||||
<div class="indicator">
|
||||
{#if Object.keys($notifications).length > 0}
|
||||
<span class="indicator-item badge badge-accent text-xs"
|
||||
@@ -765,7 +787,7 @@
|
||||
>
|
||||
<NotificationProvider component={DashboardNotification} />
|
||||
</ul>
|
||||
</div>
|
||||
</div> -->
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
CaretDown,
|
||||
MagnifyingGlass,
|
||||
} from "radix-svelte-icons";
|
||||
import { Benutzer } from "#lib/server/prisma.js";
|
||||
import { Benutzer, Enums } from "#lib/client/prisma.js";
|
||||
|
||||
export let lightTheme: boolean;
|
||||
export let benutzer: Benutzer;
|
||||
@@ -38,6 +38,12 @@
|
||||
{benutzer.name}
|
||||
</div>
|
||||
<div class="text-base-content text-sm flex">{benutzer.email}</div>
|
||||
{#if benutzer.rolle === Enums.BenutzerRolle.RESELLER}
|
||||
<div class="text-xs text-gray-500 flex">Reseller</div>
|
||||
{/if}
|
||||
{#if benutzer.rolle === Enums.BenutzerRolle.ADMIN}
|
||||
<div class="text-xs text-gray-500 flex">Admin</div>
|
||||
{/if}
|
||||
<a href="/auth/logout" class="text-xs">Logout</a>
|
||||
</div>
|
||||
</div>
|
||||
@@ -70,7 +76,9 @@
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
<a href="/dashboard/abrechnung" class="button ">Conversions</a>
|
||||
{#if benutzer.rolle === Enums.BenutzerRolle.RESELLER}
|
||||
<a href="/dashboard/abrechnung" class="button ">Monatliche Abrechnung</a>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<hr class="border-gray-600" />
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
<script lang="ts">
|
||||
import { BedarfsausweisWohnenClient, GEGNachweisWohnenClient, ObjektClient, UnterlageClient, VerbrauchsausweisGewerbeClient, VerbrauchsausweisWohnenClient } from "./Ausweis/types.js";
|
||||
import { BedarfsausweisWohnenClient, ObjektClient, VerbrauchsausweisGewerbeClient, VerbrauchsausweisWohnenClient } from "./Ausweis/types.js";
|
||||
import { Trash, Upload } from "radix-svelte-icons";
|
||||
import HelpLabel from "#components/labels/HelpLabel.svelte";
|
||||
|
||||
export let kategorie: string = "";
|
||||
export let files: Unterlage[] = [];
|
||||
export let files: (Unterlage & { preview?: boolean })[] = [];
|
||||
export let max: number = Infinity;
|
||||
export let min: number = 1;
|
||||
export let name: string = "";
|
||||
@@ -26,6 +26,8 @@
|
||||
|
||||
for (let i = 0; i < fileArray.length; i++) {
|
||||
const file = fileArray[i];
|
||||
files.push({ preview: true, kategorie, name: file.name, mime: file.type, aufnahme_id: null, id: "" });
|
||||
files = files;
|
||||
|
||||
if (i == max) {
|
||||
break;
|
||||
@@ -62,7 +64,13 @@
|
||||
name: file.name
|
||||
})
|
||||
|
||||
files.push({ id, kategorie, name: file.name, mime: mimeType, aufnahme_id: null });
|
||||
const placeholder = files.find((f) => f.name === fileArray[i].name && f.preview === true && f.kategorie === kategorie);
|
||||
if (!placeholder) {
|
||||
return;
|
||||
}
|
||||
placeholder!.preview = false;
|
||||
placeholder!.id = id;
|
||||
placeholder!.aufnahme_id = ausweis.id;
|
||||
files = files;
|
||||
|
||||
|
||||
@@ -110,6 +118,19 @@
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
{#each files as file, i}
|
||||
{#if file.kategorie == kategorie}
|
||||
{#if file.preview === true}
|
||||
<!-- Show loading spinner -->
|
||||
<div class="relative group">
|
||||
<div
|
||||
class="h-full max-h-96 w-full rounded-lg border-2 group-hover:contrast-50 object-cover transition-all text-center items-center justify-center flex p-4 text-opacity-75 text-black"
|
||||
>
|
||||
<div class="flex flex-row gap-4">
|
||||
<span>{file.name}</span>
|
||||
<span class="loader"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="relative group">
|
||||
<div
|
||||
class="h-full max-h-96 w-full rounded-lg border-2 group-hover:contrast-50 object-cover transition-all text-center items-center flex p-4 text-opacity-75 text-black"
|
||||
@@ -128,6 +149,7 @@
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
{/each}
|
||||
|
||||
<!-- Wir zeigen Platzhalter an, damit der Nutzer sieht wie viele Bilder er hochladen soll -->
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
import Cookies from "js-cookie";
|
||||
import { API_ACCESS_TOKEN_COOKIE_NAME } from "#lib/constants.js";
|
||||
|
||||
export let images: BildClient[] = [];
|
||||
export let images: (BildClient & { preview?: boolean })[] = [];
|
||||
export let max: number = Infinity;
|
||||
export let min: number = 1;
|
||||
export let name: string = "";
|
||||
@@ -35,6 +35,18 @@
|
||||
<div class="grid grid-cols-2 gap-2">
|
||||
{#each images as image, i}
|
||||
{#if image.kategorie == kategorie}
|
||||
{#if image.preview === true}
|
||||
<!-- Show loading spinner -->
|
||||
<div class="relative group">
|
||||
<div
|
||||
class="h-full max-h-96 w-full rounded-lg border-2 group-hover:contrast-50 object-cover transition-all text-center items-center justify-center flex p-4 text-opacity-75 text-black"
|
||||
>
|
||||
<div class="flex flex-row gap-4">
|
||||
<span class="loader"></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{:else}
|
||||
<div class="relative group">
|
||||
<img
|
||||
src="/bilder/{image.id}.jpg"
|
||||
@@ -51,24 +63,15 @@
|
||||
>
|
||||
<Trash size={20} color="#fff"></Trash>
|
||||
</button>
|
||||
<!-- <button
|
||||
type="button"
|
||||
class="rounded-full w-[30px] h-[30px] flex items-center justify-center p-0 bg-[rgba(0,0,0,0.4)]"
|
||||
on:click={async () => {
|
||||
let image = await rotateImage(images[i]);
|
||||
images[i] = image;
|
||||
images = images
|
||||
}}
|
||||
>
|
||||
<RotateCounterClockwise size={20} color="#fff"></RotateCounterClockwise>
|
||||
</button> -->
|
||||
</div>
|
||||
</div>
|
||||
{/if}
|
||||
{/if}
|
||||
{/each}
|
||||
|
||||
<!-- Wir zeigen Platzhalter an, damit der Nutzer sieht wie viele Bilder er hochladen soll -->
|
||||
{#each { length: Math.max(0, Math.min(max, 4) - images.filter(image => image.kategorie === kategorie).length) } as _, i}
|
||||
|
||||
<div class="relative group">
|
||||
<img
|
||||
src="/placeholder.png"
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
export let hidden: boolean = true;
|
||||
export let closeable: boolean = true;
|
||||
import { disableBodyScroll, enableBodyScroll } from 'body-scroll-lock';
|
||||
import { Cross1 } from 'radix-svelte-icons';
|
||||
|
||||
$: if (globalThis.window) {
|
||||
if (hidden) {
|
||||
@@ -12,13 +13,11 @@
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="fixed top-0 left-0 w-[100vw] h-[100vh] flex items-center justify-center bg-[rgba(0,0,0,0.8)] z-50" class:hidden={hidden} on:click|self={() => {
|
||||
hidden = closeable ? true : hidden;
|
||||
}}>
|
||||
<div class="fixed top-0 left-0 w-[100vw] h-[100vh] flex items-center justify-center bg-[rgba(0,0,0,0.8)] z-50" class:hidden={hidden}>
|
||||
{#if closeable}
|
||||
<button class="absolute top-4 left-4 text-white" on:click={() => {
|
||||
<button class="absolute top-4 right-4 text-white bg-gray-50 bg-opacity-25 px-4 py-2 rounded-lg" type="button" on:click={() => {
|
||||
hidden = true;
|
||||
}}>Schließen</button>
|
||||
}}><Cross1 size={20}></Cross1></button>
|
||||
{/if}
|
||||
<slot></slot>
|
||||
</div>
|
||||
@@ -5,7 +5,7 @@
|
||||
export let readonly: boolean = false;
|
||||
export let city: string | null | undefined;
|
||||
export let zip: string | null = "";
|
||||
|
||||
export let onchange: (event: Event) => void = () => {};
|
||||
|
||||
let hideZipDropdown: boolean = true;
|
||||
let zipCodes: inferOutput<API["postleitzahlen"]["GET"]> = [];
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
export let onFocusIn: () => any = () => {};
|
||||
export let onFocusOut: () => any = () => {};
|
||||
export let className: string = "";
|
||||
export let required: boolean = false;
|
||||
|
||||
function addTag(tag: string) {
|
||||
if ((onlyUnique && tags.indexOf(tag) > -1) || maxTags == tags.length) {
|
||||
@@ -48,6 +49,8 @@
|
||||
tag = ""
|
||||
}
|
||||
}
|
||||
|
||||
$: required = tags.length > 0 ? false : required;
|
||||
</script>
|
||||
|
||||
<div
|
||||
@@ -83,6 +86,7 @@
|
||||
class="input input-bordered h-10 px-2 py-1.5 {className}"
|
||||
{minlength}
|
||||
{maxlength}
|
||||
{required}
|
||||
disabled={disable}
|
||||
readonly={readonly}
|
||||
autocomplete="off"
|
||||
|
||||
@@ -2,9 +2,10 @@
|
||||
import { dialogs } from "svelte-dialogs";
|
||||
import TicketPopup from "./TicketPopup.svelte";
|
||||
import { addNotification } from "@ibcornelsen/ui";
|
||||
export let userEmail: string = "";
|
||||
|
||||
async function showTicketPopup() {
|
||||
const success = await dialogs.modal(TicketPopup);
|
||||
const success = await dialogs.modal(TicketPopup, { email: userEmail });
|
||||
|
||||
console.log(success);
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
<script lang="ts">
|
||||
import { api } from "astro-typesafe-api/client";
|
||||
import { getClose } from "svelte-dialogs";
|
||||
export let email: string = "";
|
||||
|
||||
const close = getClose();
|
||||
|
||||
@@ -12,7 +13,7 @@
|
||||
email: email,
|
||||
metadata: {
|
||||
category: category,
|
||||
phone: phone,
|
||||
telefon: telefon,
|
||||
},
|
||||
titel: title,
|
||||
})
|
||||
@@ -27,8 +28,8 @@
|
||||
let category = "";
|
||||
let title = "";
|
||||
let description = "";
|
||||
let email = "";
|
||||
let phone = "";
|
||||
//let email = "";
|
||||
let telefon = "";
|
||||
</script>
|
||||
|
||||
<form class="max-w-lg" on:submit={createTicket}>
|
||||
@@ -89,9 +90,9 @@
|
||||
<input
|
||||
class="input input-bordered"
|
||||
type="tel"
|
||||
placeholder="Ihre Telefonnumer"
|
||||
name="phone"
|
||||
bind:value={phone}
|
||||
placeholder="Ihre Telefonnummer"
|
||||
name="telefon"
|
||||
bind:value={telefon}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
<script lang="ts">
|
||||
import HelpLabel from "#components/labels/HelpLabel.svelte";
|
||||
import type { Enums } from "#lib/client/prisma.js";
|
||||
import Cookies from "js-cookie";
|
||||
import { tryCatch } from "#lib/tryCatch.js";
|
||||
import heic2any from "heic2any";
|
||||
|
||||
export let max: number = 2;
|
||||
export let min: number = 1;
|
||||
@@ -17,8 +19,9 @@
|
||||
} from "./Ausweis/types.js";
|
||||
import { api } from "astro-typesafe-api/client";
|
||||
import { addNotification } from "./Notifications/shared.js";
|
||||
import { API_ACCESS_TOKEN_COOKIE_NAME } from "#lib/constants.js";
|
||||
|
||||
export let images: BildClient[] = [];
|
||||
export let images: (BildClient & { preview?: boolean })[] = [];
|
||||
export let ausweis:
|
||||
| VerbrauchsausweisWohnenClient
|
||||
| VerbrauchsausweisGewerbeClient
|
||||
@@ -37,7 +40,7 @@
|
||||
for (let i = 0; i < files.length; i++) {
|
||||
const file = files[i];
|
||||
|
||||
if (file.type !== "image/jpeg" && file.type !== "image/png") {
|
||||
if (file.type !== "image/jpeg" && file.type !== "image/png" && file.type !== "image/webp" && file.type !== "image/heif" && file.type !== "image/heic") {
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -45,9 +48,12 @@
|
||||
break;
|
||||
}
|
||||
|
||||
images.push({ preview: true, benutzer_id: null, kategorie, created_at: new Date(), updated_at: new Date(), id: "" });
|
||||
images = images;
|
||||
|
||||
const reader = new FileReader();
|
||||
|
||||
reader.onload = () => {
|
||||
reader.onload = async () => {
|
||||
if (reader.readyState != reader.DONE) {
|
||||
return;
|
||||
}
|
||||
@@ -57,6 +63,18 @@
|
||||
}
|
||||
|
||||
let blob = new Blob([reader.result as ArrayBuffer]);
|
||||
|
||||
if (file.type === "image/heif" || file.type === "image/heic") {
|
||||
// Heic files are not supported by canvas, so we convert them to jpeg first
|
||||
// using an external library.
|
||||
// This is a workaround until all browsers support heic natively.
|
||||
// For more information see: https://caniuse.com/?search=heic
|
||||
// and https://developer.apple.com/documentation/imageio/reading_heif_and_heic_images_on_ios
|
||||
// and https://stackoverflow.com/questions/65887402/how-to-convert-heic-to-jpeg-in-javascript
|
||||
// and https://github.com/mbitsnbites/heic2any
|
||||
blob = await heic2any({ blob, toType: "image/jpeg", quality: 0.8 });
|
||||
}
|
||||
|
||||
let url = URL.createObjectURL(blob);
|
||||
let image = new Image();
|
||||
image.onload = async () => {
|
||||
@@ -85,6 +103,12 @@
|
||||
data: dataURL,
|
||||
kategorie,
|
||||
name: file.name
|
||||
}, {
|
||||
headers: {
|
||||
"Authorization": `Bearer ${Cookies.get(
|
||||
API_ACCESS_TOKEN_COOKIE_NAME
|
||||
)}`
|
||||
}
|
||||
}))
|
||||
|
||||
if (error) {
|
||||
@@ -96,8 +120,19 @@
|
||||
dismissable: true
|
||||
})
|
||||
} else {
|
||||
images.push({ id: result.id, kategorie });
|
||||
images = images;
|
||||
const index = images.findIndex((img) => img.preview === true);
|
||||
|
||||
if (index !== -1) {
|
||||
delete images[index];
|
||||
images = images.filter((img) => img);
|
||||
}
|
||||
images = [...images, {
|
||||
benutzer_id: null,
|
||||
created_at: new Date(),
|
||||
updated_at: new Date(),
|
||||
id: result.id,
|
||||
kategorie
|
||||
}];
|
||||
|
||||
if (i == Math.min(files.length, max) - 1) {
|
||||
this.value = "";
|
||||
@@ -128,6 +163,7 @@
|
||||
<div class="input-standard">
|
||||
<input
|
||||
type="file"
|
||||
accept="image/*"
|
||||
class="file-input file-input-ghost h-[38px]"
|
||||
bind:this={fileUpload}
|
||||
{name}
|
||||
@@ -144,6 +180,7 @@
|
||||
<div class="input-standard">
|
||||
<input
|
||||
type="file"
|
||||
accept="image/*"
|
||||
class="file-input file-input-ghost h-[38px]"
|
||||
bind:this={fileUpload}
|
||||
{name}
|
||||
|
||||
@@ -11,7 +11,7 @@ export async function auditEndEnergie(ausweis: VerbrauchsausweisWohnenClient | V
|
||||
if (aufnahme){
|
||||
if (aufnahme.flaeche && ausweis.verbrauch_1 && ausweis.verbrauch_2 && ausweis.verbrauch_3) {
|
||||
try {
|
||||
const response = await getKlimafaktoren(ausweis.startdatum, objekt.plz);
|
||||
const response = await getKlimafaktoren(ausweis.startdatum as Date, objekt.plz as string);
|
||||
// Alle Klimfaktoren konnten abgefragt werden.
|
||||
const eevva = await endEnergieVerbrauchVerbrauchsausweis_2016(ausweis, aufnahme, objekt);
|
||||
if (eevva){
|
||||
|
||||
@@ -39,63 +39,38 @@ const isNET = pathname.includes("immonet");
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<ul class="navlist">
|
||||
<li>
|
||||
<a
|
||||
href={`/${partner}/energieausweis-erstellen/verbrauchsausweis-wohngebaeude/`}
|
||||
><button class={tab === 0 ? "glow" : ""}
|
||||
>Verbrauchsausweis</button
|
||||
></a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href={`/${partner}/energieausweis-erstellen/verbrauchsausweis-gewerbe/`}
|
||||
><button class={tab === 1 ? "glow" : ""}
|
||||
>Verbrauchsausweis Gewerbe</button
|
||||
></a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href={`/${partner}/energieausweis-erstellen/bedarfsausweis-wohngebaeude/`}
|
||||
><button class={tab === 2 ? "glow" : ""}
|
||||
>Bedarfsausweis</button
|
||||
></a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href={`/${partner}/angebot-anfragen/bedarfsausweis-gewerbe-anfragen/`}
|
||||
><button class={tab === 3 ? "glow" : ""}
|
||||
>Bedarfsausweis Gewerbe</button
|
||||
></a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href={`/${partner}/angebot-anfragen/geg-nachweis-wohnen-anfragen/`}
|
||||
><button class={tab === 4 ? "glow" : ""}
|
||||
>GEG Nachweis Wohngebäude</button
|
||||
></a>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href={`/${partner}/angebot-anfragen/geg-nachweis-gewerbe-anfragen/`}
|
||||
><button class={tab === 5 ? "glow" : ""}
|
||||
>GEG Nachweis Gewerbe</button
|
||||
></a>
|
||||
</li>
|
||||
<li>
|
||||
<a href={`/${partner}/welcher-ausweis/${partner}`}
|
||||
><button class={tab === 6 ? "glow" : ""}
|
||||
>Welcher Ausweis</button
|
||||
></a>
|
||||
</li>
|
||||
</ul>
|
||||
<!-- Navigation als Liste (nur ab sm sichtbar) -->
|
||||
<ul class="navlist hidden xl:flex">
|
||||
<li><a href={`/${partner}/energieausweis-erstellen/verbrauchsausweis-wohngebaeude/`}><button class={tab === 0 ? "glow" : ""}>Verbrauchsausweis</button></a></li>
|
||||
<li><a href={`/${partner}/energieausweis-erstellen/verbrauchsausweis-gewerbe/`}><button class={tab === 1 ? "glow" : ""}>Verbrauchsausweis Gewerbe</button></a></li>
|
||||
<li><a href={`/${partner}/energieausweis-erstellen/bedarfsausweis-wohngebaeude/`}><button class={tab === 2 ? "glow" : ""}>Bedarfsausweis</button></a></li>
|
||||
<li><a href={`/${partner}/angebot-anfragen/bedarfsausweis-gewerbe-anfragen/`}><button class={tab === 3 ? "glow" : ""}>Bedarfsausweis Gewerbe</button></a></li>
|
||||
<li><a href={`/${partner}/angebot-anfragen/geg-nachweis-wohnen-anfragen/`}><button class={tab === 4 ? "glow" : ""}>GEG Nachweis Wohngebäude</button></a></li>
|
||||
<li><a href={`/${partner}/angebot-anfragen/geg-nachweis-gewerbe-anfragen/`}><button class={tab === 5 ? "glow" : ""}>GEG Nachweis Gewerbe</button></a></li>
|
||||
<li><a href={`/${partner}/welcher-ausweis/${partner}`}><button class={tab === 6 ? "glow" : ""}>Welcher Ausweis</button></a></li>
|
||||
</ul>
|
||||
|
||||
<!-- Navigation als Dropdown (nur bis sm sichtbar) -->
|
||||
<select
|
||||
class="xl:hidden border rounded p-2 w-[calc(100%-2rem)]"
|
||||
onchange="if (this.value) window.location.href=this.value"
|
||||
>
|
||||
<option value="">Auswahl treffen…</option>
|
||||
<option value={`/${partner}/energieausweis-erstellen/verbrauchsausweis-wohngebaeude/`} selected={tab === 0}>Verbrauchsausweis</option>
|
||||
<option value={`/${partner}/energieausweis-erstellen/verbrauchsausweis-gewerbe/`} selected={tab === 1}>Verbrauchsausweis Gewerbe</option>
|
||||
<option value={`/${partner}/energieausweis-erstellen/bedarfsausweis-wohngebaeude/`} selected={tab === 2}>Bedarfsausweis</option>
|
||||
<option value={`/${partner}/angebot-anfragen/bedarfsausweis-gewerbe-anfragen/`} selected={tab === 3}>Bedarfsausweis Gewerbe</option>
|
||||
<option value={`/${partner}/angebot-anfragen/geg-nachweis-wohnen-anfragen/`} selected={tab === 4}>GEG Nachweis Wohngebäude</option>
|
||||
<option value={`/${partner}/angebot-anfragen/geg-nachweis-gewerbe-anfragen/`} selected={tab === 5}>GEG Nachweis Gewerbe</option>
|
||||
<option value={`/${partner}/welcher-ausweis/${partner}`} selected={tab === 6}>Welcher Ausweis</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div
|
||||
id="titel"
|
||||
class="block w-full 2xl:h-[270px] lg:h-[148px] bg-cover px-24 py-20"
|
||||
class="block w-full lg:h-[270px] bg-cover !px-8 md:px-32 !py-8 md:py-16"
|
||||
style={`background-image: url('/images/partner/${partner}/hero-energieausweis.jpg');
|
||||
background-repeat:no-repeat; background-position:right;`}
|
||||
>
|
||||
@@ -186,7 +161,7 @@ background-repeat:no-repeat; background-position:right;`}
|
||||
font-family: "immo Sans";
|
||||
font-weight:400;
|
||||
|
||||
div{@apply w-fit bg-white/75 py-6 px-16 rounded-lg ring-2 ring-black/15 text-[1.45rem];box-shadow:8px 8px 16px rgba(0,0,0,0.5);}
|
||||
div{@apply w-fit bg-white/75 py-6 px-4 md:px-16 rounded-lg ring-2 ring-black/15 text-[1.45rem];box-shadow:8px 8px 16px rgba(0,0,0,0.5);}
|
||||
}
|
||||
|
||||
.header-button {
|
||||
|
||||
@@ -177,7 +177,10 @@ $: standardXL =
|
||||
|
||||
<WidgetCardTemplate
|
||||
name="Verbrauchsausweis Wohngebäude"
|
||||
price={PRICES.VerbrauchsausweisWohnen[Enums.AusweisTyp.Standard] + (standardXL ? 10 : 0)}
|
||||
price = {PRICES.VerbrauchsausweisWohnen[standardXL ? Enums.AusweisTyp.standardXL : Enums.AusweisTyp.Standard]}
|
||||
price1 = {PRICES.VerbrauchsausweisWohnen[standardXL ? Enums.AusweisTyp.BeratungXL : Enums.AusweisTyp.Beratung]}
|
||||
price2 = {PRICES.VerbrauchsausweisWohnen[standardXL ? Enums.AusweisTyp.OfflineXL : Enums.AusweisTyp.Offline]}
|
||||
|
||||
src={'https://online-energieausweis.org/images/partner/'+partner+'/wohngebaeude.svg'}
|
||||
alt="Wohnhaus Verbrauchsausweis"
|
||||
variant="einfach"
|
||||
@@ -190,8 +193,11 @@ $: standardXL =
|
||||
["Ungenau durch individuelles Heizverhalten.", false],
|
||||
["Wird nicht immer bei den Banken akzeptiert.", false]
|
||||
]}
|
||||
href_buy={"https://online-energieausweis.org/"+partner+"/energieausweis-erstellen/verbrauchsausweis-wohngebaeude/"}{standardXL ? '?ausweistyp=standardXL' : ''}
|
||||
href_overview={"https://online-energieausweis.org/"+partner+"/energieausweis-erstellen/verbrauchsausweis-wohngebaeude/produkt-uebersicht/"}{standardXL ? '?ausweistyp=standardXL' : ''}
|
||||
|
||||
href_buy1={`https://online-energieausweis.org/${partner}/energieausweis-erstellen/verbrauchsausweis-wohngebaeude/${standardXL ? '?ausweistyp=standardXL' : ''}`}
|
||||
href_buy2={`https://online-energieausweis.org/${partner}/energieausweis-erstellen/verbrauchsausweis-wohngebaeude/${standardXL ? '?ausweistyp=BeratungXL' : '?ausweistyp=Beratung'}`}
|
||||
href_buy3={`https://online-energieausweis.org/${partner}/energieausweis-erstellen/verbrauchsausweis-wohngebaeude/${standardXL ? '?ausweistyp=OfflineXL' : '?ausweistyp=Offline'}`}
|
||||
|
||||
></WidgetCardTemplate>
|
||||
|
||||
{/if}
|
||||
@@ -200,7 +206,9 @@ $: standardXL =
|
||||
|
||||
<WidgetCardTemplate
|
||||
name="Bedarfsausweis Wohngebäude"
|
||||
price={PRICES.BedarfsausweisWohnen[Enums.AusweisTyp.Standard] + (standardXL ? 25 : 0)}
|
||||
price = {PRICES.BedarfsausweisWohnen[standardXL ? Enums.AusweisTyp.standardXL : Enums.AusweisTyp.Standard]}
|
||||
price1 = {PRICES.BedarfsausweisWohnen[standardXL ? Enums.AusweisTyp.BeratungXL : Enums.AusweisTyp.Beratung]}
|
||||
price2 = {PRICES.BedarfsausweisWohnen[standardXL ? Enums.AusweisTyp.OfflineXL : Enums.AusweisTyp.Offline]}
|
||||
src={'https://online-energieausweis.org/images/partner/'+partner+'/wohngebaeude.svg'}
|
||||
alt="Wohnhaus Bedarfsausweis"
|
||||
variant="fundiert"
|
||||
@@ -213,8 +221,11 @@ $: standardXL =
|
||||
["Kann als Grundlage für den ISFP dienen.", true],
|
||||
["Objektivere Berechnungsmethode nach DIN 18599.", true],
|
||||
]}
|
||||
href_buy={"https://online-energieausweis.org/"+partner+"/energieausweis-erstellen/bedarfsausweis-wohngebaeude/"}{standardXL ? '?ausweistyp=standardXL' : ''}
|
||||
href_overview={"https://online-energieausweis.org/"+partner+"/energieausweis-erstellen/bedarfsausweis-wohngebaeude/produkt-uebersicht"}{standardXL ? '?ausweistyp=standardXL' : ''}
|
||||
|
||||
href_buy1={`https://online-energieausweis.org/${partner}/energieausweis-erstellen/bedarfsausweis-wohngebaeude/${standardXL ? '?ausweistyp=standardXL' : ''}`}
|
||||
href_buy2={`https://online-energieausweis.org/${partner}/energieausweis-erstellen/bedarfsausweis-wohngebaeude/${standardXL ? '?ausweistyp=BeratungXL' : '?ausweistyp=Beratung'}`}
|
||||
href_buy3={`https://online-energieausweis.org/${partner}/energieausweis-erstellen/bedarfsausweis-wohngebaeude/${standardXL ? '?ausweistyp=OfflineXL' : '?ausweistyp=Offline'}`}
|
||||
|
||||
></WidgetCardTemplate>
|
||||
|
||||
{/if}
|
||||
@@ -223,7 +234,9 @@ $: standardXL =
|
||||
|
||||
<WidgetCardTemplate
|
||||
name="Verbrauchsausweis Gewerbegebäude"
|
||||
price={PRICES.VerbrauchsausweisGewerbe[Enums.AusweisTyp.Standard] + (standardXL ? 15 : 0)}
|
||||
price = {PRICES.VerbrauchsausweisGewerbe[standardXL ? Enums.AusweisTyp.standardXL : Enums.AusweisTyp.Standard]}
|
||||
price1 = {PRICES.VerbrauchsausweisGewerbe[standardXL ? Enums.AusweisTyp.BeratungXL : Enums.AusweisTyp.Beratung]}
|
||||
price2 = {PRICES.VerbrauchsausweisGewerbe[standardXL ? Enums.AusweisTyp.OfflineXL : Enums.AusweisTyp.Offline]}
|
||||
src={'https://online-energieausweis.org/images/partner/'+partner+'/gewerbegebaeude.svg'}
|
||||
alt="Gewerbe Verbrauchsausweis"
|
||||
variant="einfach"
|
||||
@@ -237,8 +250,10 @@ $: standardXL =
|
||||
["Wird nicht immer bei den Banken akzeptiert.", false],
|
||||
["Ungenau durch individuelles Heizverhalten", false],
|
||||
]}
|
||||
href_buy={"https://online-energieausweis.org/"+partner+"/energieausweis-erstellen/verbrauchsausweis-gewerbe/"}{standardXL ? '?ausweistyp=standardXL' : ''}
|
||||
href_overview={"https://online-energieausweis.org/"+partner+"/energieausweis-erstellen/verbrauchsausweis-gewerbe/produkt-uebersicht/"}{standardXL ? '?ausweistyp=standardXL' : ''}
|
||||
|
||||
href_buy1={`https://online-energieausweis.org/${partner}/energieausweis-erstellen/verbrauchsausweis-gewerbe/${standardXL ? '?ausweistyp=standardXL' : ''}`}
|
||||
href_buy2={`https://online-energieausweis.org/${partner}/energieausweis-erstellen/verbrauchsausweis-gewerbe/${standardXL ? '?ausweistyp=BeratungXL' : '?ausweistyp=Beratung'}`}
|
||||
href_buy3={`https://online-energieausweis.org/${partner}/energieausweis-erstellen/verbrauchsausweis-gewerbe/${standardXL ? '?ausweistyp=OfflineXL' : '?ausweistyp=Offline'}`}
|
||||
></WidgetCardTemplate>
|
||||
|
||||
{/if}
|
||||
@@ -247,7 +262,9 @@ $: standardXL =
|
||||
|
||||
<WidgetCardTemplate
|
||||
name="Bedarfsausweis Gewerbegebäude"
|
||||
price={PRICES.BedarfsausweisGewerbe[Enums.AusweisTyp.Standard]}
|
||||
price = {PRICES.BedarfsausweisGewerbe[standardXL ? Enums.AusweisTyp.standardXL : Enums.AusweisTyp.Standard]}
|
||||
price1 = {PRICES.BedarfsausweisGewerbe[standardXL ? Enums.AusweisTyp.BeratungXL : Enums.AusweisTyp.Beratung]}
|
||||
price2 = {PRICES.BedarfsausweisGewerbe[standardXL ? Enums.AusweisTyp.OfflineXL : Enums.AusweisTyp.Offline]}
|
||||
src={'https://online-energieausweis.org/images/partner/'+partner+'/gewerbegebaeude.svg'}
|
||||
alt="Gewerbe Bedarfsausweis"
|
||||
variant="fundiert"
|
||||
@@ -261,8 +278,10 @@ $: standardXL =
|
||||
["Objektiveres, besser vergleichbares Ergebnis.", true],
|
||||
["Zulässig bei Leerstand oder fehlenden Verbräuchen", true],
|
||||
]}
|
||||
href_buy={"https://online-energieausweis.org/"+partner+"/angebot-anfragen/bedarfsausweis-gewerbe-anfragen/"}
|
||||
href_overview={"https://online-energieausweis.org/"+partner+"/angebot-anfragen/bedarfsausweis-gewerbe-anfragen/produkt-uebersicht/"}
|
||||
|
||||
href_buy1={`https://online-energieausweis.org/${partner}/angebot-anfragen/bedarfsausweis-gewerbe-anfragen/${standardXL ? '?ausweistyp=standardXL' : ''}`}
|
||||
href_buy2={`https://online-energieausweis.org/${partner}/angebot-anfragen/bedarfsausweis-gewerbe-anfragen/${standardXL ? '?ausweistyp=BeratungXL' : '?ausweistyp=Beratung'}`}
|
||||
|
||||
></WidgetCardTemplate>
|
||||
|
||||
{/if}
|
||||
@@ -271,7 +290,9 @@ $: standardXL =
|
||||
|
||||
<WidgetCardTemplate
|
||||
name="GEG-Nachweis Wohngebäude"
|
||||
price={PRICES.GEGNachweisWohnen[Enums.AusweisTyp.Standard]}
|
||||
price = {PRICES.GEGNachweisWohnen[standardXL ? Enums.AusweisTyp.standardXL : Enums.AusweisTyp.Standard]}
|
||||
price1 = {PRICES.GEGNachweisWohnen[standardXL ? Enums.AusweisTyp.BeratungXL : Enums.AusweisTyp.Beratung]}
|
||||
price2 = {PRICES.GEGNachweisWohnen[standardXL ? Enums.AusweisTyp.OfflineXL : Enums.AusweisTyp.Offline]}
|
||||
src={'https://online-energieausweis.org/images/partner/'+partner+'/wohngebaeude.svg'}
|
||||
alt="GEG-Nachweis-Wohnen"
|
||||
variant="Bauvorlage"
|
||||
@@ -285,8 +306,10 @@ $: standardXL =
|
||||
["Berechnung und Bilanzierung nach aktueller DIN 18599.", true],
|
||||
["Zonierung und Erstellung eines 3D Gebäudemodells.", true],
|
||||
]}
|
||||
href_buy={"https://online-energieausweis.org/"+partner+"/angebot-anfragen/geg-nachweis-wohnen-anfragen/"}
|
||||
href_overview={"https://online-energieausweis.org/"+partner+"/angebot-anfragen/geg-nachweis-wohnen-anfragen/produkt-uebersicht/"}
|
||||
|
||||
href_buy1={`https://online-energieausweis.org/${partner}/angebot-anfragen/geg-nachweis-wohnen-anfragen/${standardXL ? '?ausweistyp=standardXL' : ''}`}
|
||||
href_buy2={`https://online-energieausweis.org/${partner}/angebot-anfragen/geg-nachweis-wohnen-anfragen/${standardXL ? '?ausweistyp=BeratungXL' : '?ausweistyp=Beratung'}`}
|
||||
|
||||
|
||||
></WidgetCardTemplate>
|
||||
|
||||
@@ -296,7 +319,9 @@ $: standardXL =
|
||||
|
||||
<WidgetCardTemplate
|
||||
name="GEG-Nachweis Gewerbegebäude"
|
||||
price={PRICES.GEGNachweisGewerbe[Enums.AusweisTyp.Standard]}
|
||||
price = {PRICES.GEGNachweisGewerbe[standardXL ? Enums.AusweisTyp.standardXL : Enums.AusweisTyp.Standard]}
|
||||
price1 = {PRICES.GEGNachweisGewerbe[standardXL ? Enums.AusweisTyp.BeratungXL : Enums.AusweisTyp.Beratung]}
|
||||
price2 = {PRICES.GEGNachweisGewerbe[standardXL ? Enums.AusweisTyp.OfflineXL : Enums.AusweisTyp.Offline]}
|
||||
src={'https://online-energieausweis.org/images/partner/'+partner+'/gewerbegebaeude.svg'}
|
||||
alt="GEG-Nachweis-Gewerbe"
|
||||
variant="Bauvorlage"
|
||||
@@ -310,8 +335,10 @@ $: standardXL =
|
||||
["Berechnung und Bilanzierung nach aktueller DIN 18599.", true],
|
||||
["Mehrzonenmodell inkl. Erstellung eines 3D Gebäudemodells.", true],
|
||||
]}
|
||||
href_buy={"https://online-energieausweis.org/"+partner+"/angebot-anfragen/geg-nachweis-gewerbe-anfragen/"}
|
||||
href_overview={"https://online-energieausweis.org/"+partner+"/angebot-anfragen/geg-nachweis-gewerbe-anfragen/produkt-uebersicht/"}
|
||||
|
||||
href_buy1={`https://online-energieausweis.org/${partner}/angebot-anfragen/geg-nachweis-gewerbe-anfragen/${standardXL ? '?ausweistyp=standardXL' : ''}`}
|
||||
href_buy2={`https://online-energieausweis.org/${partner}/angebot-anfragen/geg-nachweis-gewerbe-anfragen/${standardXL ? '?ausweistyp=BeratungXL' : '?ausweistyp=Beratung'}`}
|
||||
|
||||
></WidgetCardTemplate>
|
||||
|
||||
{/if}
|
||||
|
||||
@@ -1,35 +1,34 @@
|
||||
<script lang="ts">
|
||||
import { fade } from "svelte/transition";
|
||||
export let price: number;
|
||||
|
||||
export let price1: number;
|
||||
export let price2: number;
|
||||
|
||||
export let name: string;
|
||||
export let variant: string;
|
||||
export let services: [string, boolean][];
|
||||
export let href_buy: string;
|
||||
export let href_overview: string;
|
||||
export let href_buy1: string;
|
||||
export let href_buy2: string;
|
||||
export let href_buy3: string = "";
|
||||
|
||||
export let src: string;
|
||||
export let alt: string;
|
||||
export let empfehlung: string;
|
||||
export let cta: string;
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="produktbox"
|
||||
transition:fade={{ duration: 0 }}
|
||||
>
|
||||
|
||||
{#if empfehlung === "ja"}
|
||||
<div class="empfehlung" aria-label="Empfohlenes Produkt">Empfehlung</div>
|
||||
{/if}
|
||||
<div class="produktbox" transition:fade={{ duration: 0 }}>
|
||||
{#if empfehlung === "ja"}
|
||||
<div class="empfehlung" aria-label="Empfohlenes Produkt">
|
||||
Empfehlung
|
||||
</div>
|
||||
{/if}
|
||||
<h2 class="titel sm:mb-2">{name}</h2>
|
||||
|
||||
|
||||
<div class="sumCent">
|
||||
<div class="variante">{variant}</div>
|
||||
<img
|
||||
class="image"
|
||||
{src}
|
||||
{alt}
|
||||
/>
|
||||
<img class="image" {src} {alt} />
|
||||
<div class="">
|
||||
<p class="price">
|
||||
ab {price} €
|
||||
@@ -38,67 +37,103 @@ transition:fade={{ duration: 0 }}
|
||||
|
||||
</div>
|
||||
|
||||
<div class="sumCent buttoncols">
|
||||
<a
|
||||
href={href_buy}
|
||||
class="buttoncol"
|
||||
aria-label="Jetzt {name} kaufen"
|
||||
target="_blank"
|
||||
>{cta}
|
||||
</a>
|
||||
|
||||
<a
|
||||
href={href_overview}
|
||||
class="buttoncol"
|
||||
aria-label="{name} Produkt-Übersicht"
|
||||
target="_blank"
|
||||
>Produkt-Übersicht</a
|
||||
>
|
||||
|
||||
</div>
|
||||
<hr class="col-span-2 w-full md:w-[50%] md:m-auto bg-[#ffcc00] h-[2px]" />
|
||||
|
||||
<div class="sumRows forServices">
|
||||
|
||||
{#each services as [service, check]}
|
||||
<div class="services">
|
||||
<span>{@html service}</span>
|
||||
<span class={check ? "check" : "check-no"}>{check ? "✔" : "✘"}</span>
|
||||
<span class={check ? "check" : "check-no"}
|
||||
>{check ? "✔" : "✘"}</span
|
||||
>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<hr class="col-span-2 w-full md:w-[50%] md:m-auto bg-[#ffcc00] h-[2px]" />
|
||||
|
||||
<div class="sumCent buttoncols"
|
||||
class:md:grid-cols-3={href_buy3}
|
||||
class:md:grid-cols-2={!href_buy3}>
|
||||
<a
|
||||
href={href_buy1}
|
||||
class="buttoncol"
|
||||
aria-label="Jetzt {name} kaufen"
|
||||
target="_blank"
|
||||
>mach selbst (<span class="inside-price">{price}</span> €)
|
||||
</a>
|
||||
|
||||
<a
|
||||
href={href_buy2}
|
||||
class="buttoncol"
|
||||
aria-label="{name} Produkt-Übersicht"
|
||||
target="_blank"
|
||||
>wir helfen (<span class="inside-price">{price1}</span> €)
|
||||
</a>
|
||||
|
||||
{#if href_buy3}
|
||||
<a
|
||||
href={href_buy3}
|
||||
class="buttoncol"
|
||||
aria-label="{name} Produkt-Übersicht"
|
||||
target="_blank"
|
||||
>wir machen (<span class="inside-price">{price2}</span> €)
|
||||
</a>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style lang="postcss">
|
||||
|
||||
.produktbox{@apply grid grid-cols-subgrid col-span-2 grid-rows-subgrid row-span-3 md:row-span-12 bg-black/5 rounded-lg
|
||||
.produktbox {
|
||||
@apply grid grid-cols-subgrid col-span-2 grid-rows-subgrid row-span-3 md:row-span-12 bg-black/5 rounded-lg
|
||||
px-2 py-2 mt-5;
|
||||
box-shadow:2px 2px 8px rgba(0,0,0,0.25);
|
||||
box-shadow: 2px 2px 8px rgba(0, 0, 0, 0.25);
|
||||
|
||||
.sumCent{@apply justify-self-center col-span-2}
|
||||
.sumRows{@apply hidden sm:grid grid-rows-subgrid row-span-5 items-center}
|
||||
.forServices{@apply grid-rows-subgrid row-span-5 items-center col-span-2 justify-center px-6}
|
||||
.sumCent {
|
||||
@apply justify-self-center col-span-2;
|
||||
}
|
||||
.sumRows {
|
||||
@apply hidden sm:grid grid-rows-subgrid row-span-5 items-center;
|
||||
}
|
||||
.forServices {
|
||||
@apply grid-rows-subgrid row-span-5 items-center col-span-2 justify-center px-6;
|
||||
}
|
||||
|
||||
.image{@apply w-[75%] mx-auto
|
||||
md:w-[75%] md:pl-0}
|
||||
.image {
|
||||
@apply w-[75%] mx-auto
|
||||
md:w-[75%] md:pl-0;
|
||||
}
|
||||
|
||||
.buttoncols{@apply grid grid-cols-1 gap-x-4 w-full mb-4
|
||||
md:grid-cols-2 md:w-[auto]}
|
||||
.buttoncols {
|
||||
@apply grid grid-cols-1 gap-x-4 w-full my-4
|
||||
md:grid-cols-3 md:w-[auto];
|
||||
}
|
||||
|
||||
.buttoncol{@apply mt-2 md:mt-0 text-center text-black bg-[#ffcc00] rounded-md px-3 py-1 no-underline
|
||||
hover:bg-[#222222] hover:text-white}
|
||||
.buttoncol {
|
||||
@apply mt-2 md:mt-0 text-center text-black bg-[#ffcc00] rounded-md px-3 py-1 no-underline
|
||||
hover:bg-[#222222] hover:text-white;
|
||||
}
|
||||
.inside-price {
|
||||
@apply font-bold;
|
||||
}
|
||||
|
||||
.price{@apply tracking-tighter text-[2rem] text-[#222222] pl-12 m-0 -mt-7 text-nowrap text-left;
|
||||
font-family: "Antique Olive Compact bold";}
|
||||
.price {
|
||||
@apply tracking-tighter text-[2rem] text-[#222222] pl-12 m-0 -mt-7 text-nowrap text-left;
|
||||
font-family: "Antique Olive Compact bold";
|
||||
}
|
||||
|
||||
.titel {@apply col-span-2 text-center [font-size:_clamp(20px,2.5vw,28px)]}
|
||||
.empfehlung{@apply -mt-4 absolute justify-self-end rounded-md bg-red-700 text-white w-fit h-fit px-2 py-1 rotate-1 text-[0.65rem] ring-4 ring-white mr-6}
|
||||
.titel {
|
||||
@apply col-span-2 text-center [font-size:_clamp(20px,2.5vw,28px)];
|
||||
}
|
||||
.empfehlung {
|
||||
@apply -mt-4 absolute justify-self-end rounded-md bg-red-700 text-white w-fit h-fit px-2 py-1 rotate-1 text-[0.65rem] ring-4 ring-white mr-6;
|
||||
}
|
||||
.variante {
|
||||
@apply w-fit italic col-span-2 -mt-2 -mb-4 text-[1rem] text-[#222222] justify-self-start ring-2 ring-[#ffcc00] rounded-md pl-[4px] pr-[6px] py-[0px];
|
||||
}
|
||||
|
||||
.services {
|
||||
@apply hidden text-start py-1 md:grid grid-rows-subgrid row-span-1 items-center md:grid-cols-[1fr_50px]
|
||||
@apply hidden text-start py-1 md:grid grid-rows-subgrid row-span-1 items-center md:grid-cols-[1fr_50px];
|
||||
}
|
||||
.services:not(:last-child) {
|
||||
@apply border-b-[1px] border-gray-200;
|
||||
@@ -109,7 +144,5 @@ hover:bg-[#222222] hover:text-white}
|
||||
.check-no {
|
||||
@apply justify-self-end self-center font-bold text-red-700;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -2,6 +2,11 @@ import { prisma } from "#lib/server/prisma.js";
|
||||
import moment from "moment";
|
||||
import csv from "csvtojson"
|
||||
|
||||
if (!process.env.prev_restart_delay && process.env.cron_restart) {
|
||||
console.log('skipping initial launch....');
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
// Als erstes schauen wir, welches das letzte Jahr ist, für das wir einen Verbrauchsausweis haben.
|
||||
// Das machen wir, indem wir die Ausweise nach Jahr und Monat sortieren und dann den letzten Eintrag nehmen.
|
||||
const newestDate = await prisma.klimafaktoren.findFirst({
|
||||
|
||||
@@ -19,6 +19,7 @@ export type Lueftungskonzept = (typeof Lueftungskonzept)[keyof typeof Lueftungsk
|
||||
export const BenutzerRolle = {
|
||||
USER: "USER",
|
||||
ADMIN: "ADMIN",
|
||||
RESELLER: "RESELLER",
|
||||
} as const;
|
||||
|
||||
export type BenutzerRolle = (typeof BenutzerRolle)[keyof typeof BenutzerRolle];
|
||||
|
||||
@@ -38,19 +38,19 @@ export const BedarfsausweisWohnenSchema = z.object({
|
||||
volumen: z.number().nullish(),
|
||||
dicht: z.boolean().nullish(),
|
||||
fenster_flaeche_1: z.number().nullish(),
|
||||
fenster_art_1: z.string().nullish(),
|
||||
fenster_art_1: z.number().nullish(),
|
||||
fenster_flaeche_2: z.number().nullish(),
|
||||
fenster_art_2: z.string().nullish(),
|
||||
fenster_art_2: z.number().nullish(),
|
||||
dachfenster_flaeche: z.number().nullish(),
|
||||
dachfenster_art: z.string().nullish(),
|
||||
dachfenster_art: z.number().nullish(),
|
||||
haustuer_flaeche: z.number().nullish(),
|
||||
haustuer_art: z.string().nullish(),
|
||||
haustuer_art: z.number().nullish(),
|
||||
dach_bauart: z.string().nullish(),
|
||||
decke_bauart: z.string().nullish(),
|
||||
dach_daemmung: z.string().nullish(),
|
||||
decke_daemmung: z.string().nullish(),
|
||||
aussenwand_daemmung: z.string().nullish(),
|
||||
boden_daemmung: z.string().nullish(),
|
||||
dach_daemmung: z.number().nullish(),
|
||||
decke_daemmung: z.number().nullish(),
|
||||
aussenwand_daemmung: z.number().nullish(),
|
||||
boden_daemmung: z.number().nullish(),
|
||||
aussenwand_bauart: z.string().nullish(),
|
||||
boden_bauart: z.string().nullish(),
|
||||
warmwasser_verteilung: z.string().nullish(),
|
||||
|
||||
@@ -13,10 +13,11 @@ export const BenutzerSchema = z.object({
|
||||
ort: z.string().nullish(),
|
||||
adresse: z.string().nullish(),
|
||||
telefon: z.string().nullish(),
|
||||
anrede: z.string().nullish(),
|
||||
empfaenger: z.string().nullish(),
|
||||
rolle: z.nativeEnum(BenutzerRolle),
|
||||
firma: z.string().nullish(),
|
||||
lex_office_id: z.string().nullish(),
|
||||
partner_code: z.string().nullish(),
|
||||
verified: z.boolean(),
|
||||
created_at: z.date(),
|
||||
updated_at: z.date(),
|
||||
|
||||
@@ -7,5 +7,6 @@ export const BildSchema = z.object({
|
||||
name: z.string(),
|
||||
created_at: z.date(),
|
||||
updated_at: z.date(),
|
||||
benutzer_id: z.string().nullish(),
|
||||
aufnahme_id: z.string().nullish(),
|
||||
})
|
||||
|
||||
@@ -12,6 +12,7 @@ export * from "./gegnachweiswohnen"
|
||||
export * from "./klimafaktoren"
|
||||
export * from "./objekt"
|
||||
export * from "./postleitzahlen"
|
||||
export * from "./provisionen"
|
||||
export * from "./rechnung"
|
||||
export * from "./refreshtokens"
|
||||
export * from "./tickets"
|
||||
|
||||
13
src/generated/zod/provisionen.ts
Normal file
13
src/generated/zod/provisionen.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import * as z from "zod"
|
||||
import { Ausweisart, AusweisTyp } from "@prisma/client"
|
||||
|
||||
export const ProvisionenSchema = z.object({
|
||||
id: z.number().int(),
|
||||
ausweisart: z.nativeEnum(Ausweisart),
|
||||
ausweistyp: z.nativeEnum(AusweisTyp),
|
||||
provision_prozent: z.number(),
|
||||
provision_betrag: z.number(),
|
||||
benutzer_id: z.string().nullish(),
|
||||
created_at: z.date(),
|
||||
updated_at: z.date(),
|
||||
})
|
||||
@@ -18,20 +18,6 @@ const { title } = Astro.props;
|
||||
---
|
||||
|
||||
<script>
|
||||
// import { H } from "highlight.run";
|
||||
|
||||
// if (import.meta.env.PROD) {
|
||||
// H.init("1jdkoe52", {
|
||||
// serviceName: "online-energieausweis",
|
||||
// backendUrl: "https://highlight-backend.online-energieausweis.org/public",
|
||||
// tracingOrigins: true,
|
||||
// networkRecording: {
|
||||
// enabled: true,
|
||||
// recordHeadersAndBody: true
|
||||
// }
|
||||
// });
|
||||
// }
|
||||
|
||||
window.addEventListener("scroll", () => {
|
||||
const skala = document.getElementById("skala");
|
||||
|
||||
|
||||
@@ -17,22 +17,6 @@ export interface Props {
|
||||
const { title } = Astro.props;
|
||||
---
|
||||
|
||||
<script>
|
||||
// import { H } from "highlight.run";
|
||||
|
||||
// if (import.meta.env.PROD) {
|
||||
// H.init("1jdkoe52", {
|
||||
// serviceName: "online-energieausweis",
|
||||
// backendUrl: "https://highlight-backend.online-energieausweis.org/public",
|
||||
// tracingOrigins: true,
|
||||
// networkRecording: {
|
||||
// enabled: true,
|
||||
// recordHeadersAndBody: true
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
</script>
|
||||
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
|
||||
@@ -34,22 +34,6 @@ const schema = JSON.stringify({
|
||||
});
|
||||
---
|
||||
|
||||
<script>
|
||||
// import { H } from "highlight.run";
|
||||
|
||||
// if (import.meta.env.PROD) {
|
||||
// H.init("1jdkoe52", {
|
||||
// serviceName: "online-energieausweis",
|
||||
// backendUrl: "https://highlight-backend.online-energieausweis.org/public",
|
||||
// tracingOrigins: true,
|
||||
// networkRecording: {
|
||||
// enabled: true,
|
||||
// recordHeadersAndBody: true
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
</script>
|
||||
|
||||
<!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
|
||||
@@ -45,31 +45,6 @@ const schema = JSON.stringify({
|
||||
let lightTheme = Astro.cookies.get("theme")?.value === "light";
|
||||
---
|
||||
|
||||
<script >
|
||||
// import { H } from "highlight.run";
|
||||
|
||||
// const user = JSON.parse(document.body.dataset.user);
|
||||
|
||||
// if (import.meta.env.PROD) {
|
||||
// H.init("1jdkoe52", {
|
||||
// serviceName: "online-energieausweis",
|
||||
// backendUrl:
|
||||
// "https://highlight-backend.online-energieausweis.org/public",
|
||||
// tracingOrigins: true,
|
||||
// networkRecording: {
|
||||
// enabled: true,
|
||||
// recordHeadersAndBody: true,
|
||||
// }
|
||||
// });
|
||||
|
||||
// if (user) {
|
||||
// H.identify(user.email, {
|
||||
// id: user.id
|
||||
// })
|
||||
// }
|
||||
// }
|
||||
</script>
|
||||
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
|
||||
@@ -19,19 +19,6 @@ const { title } = Astro.props;
|
||||
---
|
||||
|
||||
<script>
|
||||
// import { H } from "highlight.run";
|
||||
|
||||
// if (import.meta.env.PROD) {
|
||||
// H.init("1jdkoe52", {
|
||||
// serviceName: "online-energieausweis",
|
||||
// backendUrl: "https://highlight-backend.online-energieausweis.org/public",
|
||||
// tracingOrigins: true,
|
||||
// networkRecording: {
|
||||
// enabled: true,
|
||||
// recordHeadersAndBody: true
|
||||
// }
|
||||
// })
|
||||
// }
|
||||
|
||||
/*
|
||||
window.addEventListener("scroll", (event) => {
|
||||
@@ -150,7 +137,7 @@ height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
|
||||
|
||||
<Footer />
|
||||
<NotificationWrapper client:load />
|
||||
<TicketButton client:load></TicketButton>
|
||||
<TicketButton client:load userEmail={user?.email ?? ""}></TicketButton>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
|
||||
@@ -10,9 +10,8 @@ export async function endEnergieVerbrauchVerbrauchsausweisGewerbe_2016_Client(
|
||||
) {
|
||||
const startdatum = moment(ausweis.startdatum);
|
||||
let klimafaktoren = await getKlimafaktoren(
|
||||
objekt.plz as string,
|
||||
startdatum.toDate(),
|
||||
startdatum.add(2, "years").toDate()
|
||||
objekt.plz as string,
|
||||
);
|
||||
|
||||
if (!klimafaktoren || klimafaktoren.length === 0) {
|
||||
|
||||
@@ -13,12 +13,14 @@ export async function endEnergieVerbrauchVerbrauchsausweis_2016_Client(
|
||||
objekt: ObjektClient,
|
||||
) {
|
||||
const startdatum = moment(ausweis.startdatum);
|
||||
|
||||
let klimafaktoren = await getKlimafaktoren(
|
||||
objekt.plz as string,
|
||||
startdatum.toDate(),
|
||||
startdatum.add(2, "years").toDate()
|
||||
objekt.plz as string
|
||||
);
|
||||
|
||||
console.log("Klimafaktoren", klimafaktoren);
|
||||
|
||||
if (!klimafaktoren || klimafaktoren.length === 0) {
|
||||
klimafaktoren = [
|
||||
{
|
||||
|
||||
@@ -3,11 +3,10 @@ import { memoize } from "./Memoization.js";
|
||||
import { api } from "astro-typesafe-api/client"
|
||||
|
||||
export const getKlimafaktoren = memoize(async (date: Date, plz: string) => {
|
||||
if (!plz || !date) {
|
||||
if (!plz || plz.length < 5 || !date) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await api.klimafaktoren.GET.fetch({
|
||||
plz,
|
||||
genauigkeit: "years",
|
||||
@@ -17,7 +16,4 @@ export const getKlimafaktoren = memoize(async (date: Date, plz: string) => {
|
||||
enddatum: moment(date).add(2, "years").utc(true).toString(),
|
||||
});
|
||||
return response;
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,18 +1,21 @@
|
||||
type MemoizedFunction<T> = (...args: any[]) => T;
|
||||
type MemoizedFunction<T extends (...args: any[]) => Promise<any> | any> = (...args: Parameters<T>) => Promise<ReturnType<T>> | ReturnType<T>;
|
||||
|
||||
export function memoize<T>(func: (...args: any[]) => T): MemoizedFunction<T> {
|
||||
const cache = new Map<string, T>();
|
||||
export function memoize<T extends (...args: any[]) => Promise<any> | any>(func: T): MemoizedFunction<T> {
|
||||
const cache = new Map<string, ReturnType<T>>();
|
||||
|
||||
return (...args: any[]): T => {
|
||||
return (...args: Parameters<T>): Promise<ReturnType<T>> | ReturnType<T> => {
|
||||
const key = JSON.stringify(args);
|
||||
|
||||
if (cache.has(key)) {
|
||||
return cache.get(key)!;
|
||||
}
|
||||
|
||||
const result = func(...args);
|
||||
if (result instanceof Promise) {
|
||||
return result.then(resolved => {
|
||||
return resolved;
|
||||
});
|
||||
} else {
|
||||
cache.set(key, result);
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
25
src/lib/logger.ts
Normal file
25
src/lib/logger.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import winston, { format} from "winston";
|
||||
|
||||
const { combine, timestamp } = format;
|
||||
|
||||
const loggingFormat = format.printf(({ level, message, timestamp }) => {
|
||||
return `${timestamp} [${level}]: ${message}`;
|
||||
});
|
||||
|
||||
export const logger = winston.createLogger({
|
||||
level: "info",
|
||||
format: combine(
|
||||
timestamp(),
|
||||
loggingFormat
|
||||
),
|
||||
transports: [
|
||||
new winston.transports.File({ filename: "error.log", level: "error" }),
|
||||
new winston.transports.File({ filename: "combined.log" }),
|
||||
],
|
||||
});
|
||||
|
||||
if (process.env.NODE_ENV !== "production") {
|
||||
logger.add(new winston.transports.Console({
|
||||
format: combine(timestamp(), loggingFormat),
|
||||
}));
|
||||
}
|
||||
@@ -92,11 +92,14 @@ export async function authorizationMiddleware(input: any, ctx: TypesafeAPIContex
|
||||
}
|
||||
|
||||
export async function maybeAuthorizationMiddleware(input: any, ctx: TypesafeAPIContextWithRequest<any>) {
|
||||
let user = null;
|
||||
try {
|
||||
return authorizationMiddleware(input, ctx)
|
||||
user = await authorizationMiddleware(input, ctx)
|
||||
} catch(e) {
|
||||
return null;
|
||||
console.log(e);
|
||||
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
export const authorizationHeaders = {
|
||||
|
||||
14
src/lib/provision.ts
Normal file
14
src/lib/provision.ts
Normal file
@@ -0,0 +1,14 @@
|
||||
import { Enums, Provisionen } from "./client/prisma.js";
|
||||
|
||||
export function getProvision(ausweisart: Enums.Ausweisart, ausweistyp: Enums.AusweisTyp, provisionen: Provisionen[]): { provision_prozent: number, provision_betrag: number } {
|
||||
const provision = provisionen.find(p => p.ausweisart === ausweisart && p.ausweistyp === ausweistyp);
|
||||
return {
|
||||
provision_prozent: provision?.provision_prozent || 0,
|
||||
provision_betrag: provision?.provision_betrag || 0
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
export function getProductName(ausweisart: Enums.Ausweisart, ausweistyp: Enums.AusweisTyp): string {
|
||||
return `${Enums.Ausweisart[ausweisart]} ${Enums.AusweisTyp[ausweistyp]}`;
|
||||
}
|
||||
@@ -1,11 +1,27 @@
|
||||
import { AufnahmeClient, BedarfsausweisWohnenClient, BenutzerClient, BildClient, getAusweisartFromId, ObjektClient, RechnungClient, VerbrauchsausweisGewerbeClient, VerbrauchsausweisWohnenClient } from "#components/Ausweis/types.js";
|
||||
import {
|
||||
AufnahmeClient,
|
||||
BedarfsausweisWohnenClient,
|
||||
BenutzerClient,
|
||||
BildClient,
|
||||
getAusweisartFromId,
|
||||
ObjektClient,
|
||||
RechnungClient,
|
||||
VerbrauchsausweisGewerbeClient,
|
||||
VerbrauchsausweisWohnenClient,
|
||||
} from "#components/Ausweis/types.js";
|
||||
import { pdfDatenblattVerbrauchsausweisGewerbe } from "#lib/pdf/pdfDatenblattVerbrauchsausweisGewerbe.js";
|
||||
import { pdfDatenblattVerbrauchsausweisWohnen } from "#lib/pdf/pdfDatenblattVerbrauchsausweisWohnen.js";
|
||||
import { pdfVerbrauchsausweisGewerbe } from "#lib/pdf/pdfVerbrauchsausweisGewerbe.js";
|
||||
import { pdfVerbrauchsausweisWohnen } from "#lib/pdf/pdfVerbrauchsausweisWohnen.js";
|
||||
import { pdfAushangVerbrauchsausweisGewerbe } from "#lib/pdf/pdfAushangVerbrauchsausweisGewerbe.js";
|
||||
import { Enums, prisma, Rechnung } from "#lib/server/prisma.js";
|
||||
|
||||
import {
|
||||
BedarfsausweisWohnen,
|
||||
Enums,
|
||||
prisma,
|
||||
Rechnung,
|
||||
VerbrauchsausweisGewerbe,
|
||||
VerbrauchsausweisWohnen,
|
||||
} from "#lib/server/prisma.js";
|
||||
|
||||
/**
|
||||
* Gibt den richtigen Prisma Adapter für die Ausweisart basierend auf der UID zurück, oder null bei einer falschen UID.
|
||||
@@ -15,17 +31,17 @@ export function getPrismaAusweisAdapter(id: string) {
|
||||
const ausweisart = getAusweisartFromId(id);
|
||||
|
||||
if (ausweisart === Enums.Ausweisart.VerbrauchsausweisWohnen) {
|
||||
return prisma.verbrauchsausweisWohnen
|
||||
return prisma.verbrauchsausweisWohnen;
|
||||
} else if (ausweisart === Enums.Ausweisart.VerbrauchsausweisGewerbe) {
|
||||
return prisma.verbrauchsausweisGewerbe
|
||||
return prisma.verbrauchsausweisGewerbe;
|
||||
} else if (ausweisart === Enums.Ausweisart.BedarfsausweisWohnen) {
|
||||
return prisma.bedarfsausweisWohnen
|
||||
return prisma.bedarfsausweisWohnen;
|
||||
} else if (ausweisart === Enums.Ausweisart.GEGNachweisWohnen) {
|
||||
return prisma.gEGNachweisWohnen
|
||||
return prisma.gEGNachweisWohnen;
|
||||
} else if (ausweisart === Enums.Ausweisart.GEGNachweisGewerbe) {
|
||||
return prisma.gEGNachweisGewerbe
|
||||
return prisma.gEGNachweisGewerbe;
|
||||
} else if (ausweisart === Enums.Ausweisart.BedarfsausweisGewerbe) {
|
||||
return prisma.bedarfsausweisGewerbe
|
||||
return prisma.bedarfsausweisGewerbe;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,50 +49,166 @@ export function getPrismaAusweisAdapter(id: string) {
|
||||
* Gibt den richtigen Ansichtsausweis basierend auf der Ausweisart zurück.
|
||||
* @param ausweis
|
||||
*/
|
||||
export async function getAnsichtsausweis(ausweis: VerbrauchsausweisWohnenClient | VerbrauchsausweisGewerbeClient | BedarfsausweisWohnenClient, aufnahme: AufnahmeClient, objekt: ObjektClient, bilder: BildClient[], user: BenutzerClient, vorschau: boolean = true, ausweisart = getAusweisartFromId(ausweis.id)) {
|
||||
export async function getAnsichtsausweis(
|
||||
ausweis:
|
||||
| VerbrauchsausweisWohnenClient
|
||||
| VerbrauchsausweisGewerbeClient
|
||||
| BedarfsausweisWohnenClient,
|
||||
aufnahme: AufnahmeClient,
|
||||
objekt: ObjektClient,
|
||||
bilder: BildClient[],
|
||||
user: BenutzerClient,
|
||||
vorschau: boolean = true,
|
||||
ausweisart = getAusweisartFromId(ausweis.id)
|
||||
) {
|
||||
if (!ausweisart) {
|
||||
return null
|
||||
return null;
|
||||
}
|
||||
|
||||
if (ausweisart === Enums.Ausweisart.VerbrauchsausweisWohnen) {
|
||||
return await pdfVerbrauchsausweisWohnen(ausweis as VerbrauchsausweisWohnenClient, aufnahme, objekt, bilder, user, vorschau)
|
||||
return await pdfVerbrauchsausweisWohnen(
|
||||
ausweis as VerbrauchsausweisWohnenClient,
|
||||
aufnahme,
|
||||
objekt,
|
||||
bilder,
|
||||
user,
|
||||
vorschau
|
||||
);
|
||||
} else if (ausweisart === Enums.Ausweisart.VerbrauchsausweisGewerbe) {
|
||||
return await pdfVerbrauchsausweisGewerbe(ausweis as VerbrauchsausweisGewerbeClient, aufnahme, objekt, bilder, user, vorschau)
|
||||
return await pdfVerbrauchsausweisGewerbe(
|
||||
ausweis as VerbrauchsausweisGewerbeClient,
|
||||
aufnahme,
|
||||
objekt,
|
||||
bilder,
|
||||
user,
|
||||
vorschau
|
||||
);
|
||||
}
|
||||
|
||||
return null
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt das richtige Datenblatt basierend auf der Ausweisart zurück.
|
||||
* @param ausweis
|
||||
*/
|
||||
export async function getDatenblatt(ausweis: VerbrauchsausweisWohnenClient | VerbrauchsausweisGewerbeClient | BedarfsausweisWohnenClient, aufnahme: AufnahmeClient, objekt: ObjektClient, bilder: BildClient[], user: BenutzerClient, rechnung: Rechnung, ausweisart = getAusweisartFromId(ausweis.id)) {
|
||||
export async function getDatenblatt(
|
||||
ausweis:
|
||||
| VerbrauchsausweisWohnenClient
|
||||
| VerbrauchsausweisGewerbeClient
|
||||
| BedarfsausweisWohnenClient,
|
||||
aufnahme: AufnahmeClient,
|
||||
objekt: ObjektClient,
|
||||
bilder: BildClient[],
|
||||
user: BenutzerClient,
|
||||
rechnung: Rechnung,
|
||||
ausweisart = getAusweisartFromId(ausweis.id)
|
||||
) {
|
||||
if (!ausweisart) {
|
||||
return null
|
||||
return null;
|
||||
}
|
||||
|
||||
if (ausweisart === Enums.Ausweisart.VerbrauchsausweisWohnen) {
|
||||
return await pdfDatenblattVerbrauchsausweisWohnen(ausweis as VerbrauchsausweisWohnenClient, aufnahme, objekt, rechnung, bilder)
|
||||
return await pdfDatenblattVerbrauchsausweisWohnen(
|
||||
ausweis as VerbrauchsausweisWohnenClient,
|
||||
aufnahme,
|
||||
objekt,
|
||||
rechnung,
|
||||
bilder
|
||||
);
|
||||
} else if (ausweisart === Enums.Ausweisart.VerbrauchsausweisGewerbe) {
|
||||
return await pdfDatenblattVerbrauchsausweisGewerbe(ausweis as VerbrauchsausweisGewerbeClient, aufnahme, objekt, rechnung, bilder)
|
||||
return await pdfDatenblattVerbrauchsausweisGewerbe(
|
||||
ausweis as VerbrauchsausweisGewerbeClient,
|
||||
aufnahme,
|
||||
objekt,
|
||||
rechnung,
|
||||
bilder
|
||||
);
|
||||
}
|
||||
|
||||
return null
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gibt den richtigen Aushang basierend auf der Ausweisart zurück.
|
||||
* @param ausweis
|
||||
*/
|
||||
export async function getAushang(ausweis: VerbrauchsausweisWohnenClient | VerbrauchsausweisGewerbeClient | BedarfsausweisWohnenClient, aufnahme: AufnahmeClient, objekt: ObjektClient, bilder: BildClient[], user: BenutzerClient, vorschau: boolean = true, rechnung: Rechnung, ausweisart = getAusweisartFromId(ausweis.id)) {
|
||||
export async function getAushang(
|
||||
ausweis:
|
||||
| VerbrauchsausweisWohnenClient
|
||||
| VerbrauchsausweisGewerbeClient
|
||||
| BedarfsausweisWohnenClient,
|
||||
aufnahme: AufnahmeClient,
|
||||
objekt: ObjektClient,
|
||||
bilder: BildClient[],
|
||||
user: BenutzerClient,
|
||||
vorschau: boolean = true,
|
||||
rechnung: Rechnung,
|
||||
ausweisart = getAusweisartFromId(ausweis.id)
|
||||
) {
|
||||
if (!ausweisart || !rechnung.services.includes(Enums.Service.Aushang)) {
|
||||
return null
|
||||
return null;
|
||||
}
|
||||
|
||||
if (ausweisart === Enums.Ausweisart.VerbrauchsausweisGewerbe) {
|
||||
return await pdfAushangVerbrauchsausweisGewerbe(ausweis as VerbrauchsausweisGewerbeClient, aufnahme, objekt, bilder, user, vorschau)
|
||||
return await pdfAushangVerbrauchsausweisGewerbe(
|
||||
ausweis as VerbrauchsausweisGewerbeClient,
|
||||
aufnahme,
|
||||
objekt,
|
||||
bilder,
|
||||
user,
|
||||
vorschau
|
||||
);
|
||||
}
|
||||
|
||||
return null
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extrahiert die Ausweisfelder aus einem Objekt, das mehrere Ausweisarten enthält, und fasst sie in einem gemeinsamen Feld `ausweis` zusammen.
|
||||
* @param row Ein Objekt, das die Ausweisfelder enthält.
|
||||
* @returns Ein neues Objekt, das die Ausweisfelder extrahiert und in einem gemeinsamen Feld `ausweis` zusammenfasst.
|
||||
*/
|
||||
export function extrahiereAusweisAusFeldMitMehrerenAusweisen<T>(
|
||||
row: T & {
|
||||
bedarfsausweis_wohnen?: BedarfsausweisWohnen;
|
||||
verbrauchsausweis_wohnen?: VerbrauchsausweisWohnen;
|
||||
verbrauchsausweis_gewerbe?: VerbrauchsausweisGewerbe;
|
||||
}
|
||||
) {
|
||||
const {
|
||||
bedarfsausweis_wohnen,
|
||||
verbrauchsausweis_wohnen,
|
||||
verbrauchsausweis_gewerbe,
|
||||
...rest
|
||||
} = row;
|
||||
return {
|
||||
...rest,
|
||||
ausweis: {
|
||||
...(bedarfsausweis_wohnen ??
|
||||
verbrauchsausweis_wohnen ??
|
||||
verbrauchsausweis_gewerbe),
|
||||
ausweisart: bedarfsausweis_wohnen
|
||||
? Enums.Ausweisart.BedarfsausweisWohnen
|
||||
: verbrauchsausweis_wohnen
|
||||
? Enums.Ausweisart.VerbrauchsausweisWohnen
|
||||
: Enums.Ausweisart.VerbrauchsausweisGewerbe,
|
||||
ausweistyp: bedarfsausweis_wohnen
|
||||
? bedarfsausweis_wohnen.ausweistyp
|
||||
: verbrauchsausweis_wohnen
|
||||
? verbrauchsausweis_wohnen.ausweistyp
|
||||
: verbrauchsausweis_gewerbe?.ausweistyp || Enums.AusweisTyp.Standard,
|
||||
},
|
||||
} as {
|
||||
ausweis: (
|
||||
| BedarfsausweisWohnen
|
||||
| VerbrauchsausweisWohnen
|
||||
| VerbrauchsausweisGewerbe
|
||||
) & { ausweisart: Enums.Ausweisart };
|
||||
} & Omit<
|
||||
T,
|
||||
| "bedarfsausweis_wohnen"
|
||||
| "verbrauchsausweis_wohnen"
|
||||
| "verbrauchsausweis_gewerbe"
|
||||
>;
|
||||
}
|
||||
@@ -2,6 +2,7 @@ import os from "os"
|
||||
import fs from "fs"
|
||||
|
||||
export const PERSISTENT_DIR = `${os.homedir()}/persistent/online-energieausweis`
|
||||
export const SECRET = process.env.SECRET as string
|
||||
|
||||
if (!fs.existsSync(PERSISTENT_DIR)) {
|
||||
fs.mkdirSync(PERSISTENT_DIR, {recursive: true})
|
||||
|
||||
45
src/lib/server/mail/auto-registrierung.ts
Normal file
45
src/lib/server/mail/auto-registrierung.ts
Normal file
@@ -0,0 +1,45 @@
|
||||
import { transport } from "#lib/mail.js";
|
||||
import {
|
||||
Benutzer,
|
||||
} from "#lib/client/prisma.js";
|
||||
|
||||
export async function sendAutoRegisterMail(
|
||||
user: Benutzer,
|
||||
password: string
|
||||
) {
|
||||
await transport.sendMail({
|
||||
from: `"IBCornelsen" <info@online-energieausweis.org>`,
|
||||
to: user.email,
|
||||
subject: `Ihre Registrierung bei IBCornelsen`,
|
||||
bcc: "info@online-energieausweis.org",
|
||||
html: `<p>Sehr geehrte/r Kund/in,</p>
|
||||
<p>vielen Dank für Ihre Registrierung bei IBCornelsen. Ihr Benutzerkonto wurde erfolgreich erstellt.<br><br>
|
||||
|
||||
Nachfolgend finden Sie Ihre Zugangsdaten:<br><br>
|
||||
E-Mail: ${user.email}<br>
|
||||
Passwort: ${password}<br><br>
|
||||
|
||||
Sollten Sie diese Registrierung nicht vorgenommen haben, können Sie diese E-Mail einfach ignorieren. Ihr Benutzerkonto wird in diesem Fall nicht aktiviert.<br><br>
|
||||
|
||||
Falls Sie Fragen haben oder Unterstützung benötigen, stehen wir Ihnen gerne zur Verfügung. Kontaktieren Sie uns einfach unter <a href="mailto:support@online-energieausweis.org">support@online-energieausweis.org</a>.
|
||||
<p>
|
||||
Mit freundlichen Grüßen,
|
||||
<br>
|
||||
Dipl.-Ing. Jens Cornelsen
|
||||
<br>
|
||||
<br>
|
||||
<strong>IB Cornelsen</strong>
|
||||
<br>
|
||||
Katendeich 5A
|
||||
<br>
|
||||
21035 Hamburg
|
||||
<br>
|
||||
www.online-energieausweis.org
|
||||
<br>
|
||||
<br>
|
||||
fon 040 · 209339850
|
||||
<br>
|
||||
fax 040 · 209339859
|
||||
</p>`
|
||||
});
|
||||
}
|
||||
@@ -75,6 +75,6 @@ export async function sendInvoiceMail(
|
||||
name: rechnung.empfaenger || "",
|
||||
},
|
||||
bcc: "info@online-energieausweis.org",
|
||||
html: `<p>Sehr geehrte*r ${user.vorname} ${user.name},</p>` + getPaymentInvoiceBody(ausweis, rechnung, ausweisart),
|
||||
html: `<p>Sehr geehrte/r ${user.vorname} ${user.name},</p>` + getPaymentInvoiceBody(ausweis, rechnung, ausweisart),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -80,6 +80,6 @@ export async function sendPaymentSuccessMail(
|
||||
name: rechnung.empfaenger || "",
|
||||
},
|
||||
bcc: "info@online-energieausweis.org",
|
||||
html: `<p>Sehr geehrte*r ${user.vorname} ${user.name},</p>` + getPaymentSuccessBody(ausweis, rechnung, ausweisart),
|
||||
html: `<p>Sehr geehrte/r ${user.vorname} ${user.name},</p>` + getPaymentSuccessBody(ausweis, rechnung, ausweisart),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,31 +1,30 @@
|
||||
import { BASE_URI } from "#lib/constants.js";
|
||||
import { transport } from "#lib/mail.js";
|
||||
import {
|
||||
Benutzer,
|
||||
} from "#lib/client/prisma.js";
|
||||
import { encodeToken } from "#lib/auth/token.js";
|
||||
import { TokenType } from "#lib/auth/types.js";
|
||||
|
||||
export async function sendRegisterMail(
|
||||
user: Benutzer
|
||||
user: Benutzer,
|
||||
passwort: string
|
||||
) {
|
||||
const verificationJwt = encodeToken({
|
||||
typ: TokenType.Verify,
|
||||
exp: Date.now() + (15 * 60 * 1000),
|
||||
uid: user.uid
|
||||
})
|
||||
|
||||
await transport.sendMail({
|
||||
from: `"IBCornelsen" <info@online-energieausweis.org>`,
|
||||
to: user.email,
|
||||
subject: `Ihre Registrierung bei IBCornelsen`,
|
||||
bcc: "info@online-energieausweis.org",
|
||||
html: `<p>Sehr geehrte*r ${user.vorname} ${user.name},</p>
|
||||
html: `<p>Sehr geehrte/r ${user.vorname} ${user.name},</p>
|
||||
<p>vielen Dank für Ihre Registrierung bei IBCornelsen. Ihr Benutzerkonto wurde erfolgreich erstellt.<br><br>
|
||||
|
||||
Um Ihre Registrierung abzuschließen, klicken Sie bitte auf den folgenden Link, um Ihre E-Mail-Adresse zu bestätigen:<br><br>
|
||||
Nachfolgend finden Sie Ihre Zugangsdaten:<br><br>
|
||||
E-Mail: ${user.email}<br>
|
||||
Passwort: ${passwort}<br><br>
|
||||
|
||||
<a href="${BASE_URI}/auth/verify?t=${verificationJwt}">E-Mail-Adresse bestätigen</a><br></p>
|
||||
Aus Sicherheitsgründen empfehlen wir Ihnen, Ihr Passwort nach dem ersten Login zu ändern.<br><br>
|
||||
Um sich anzumelden, besuchen Sie bitte unsere Website unter <a href="https://online-energieausweis.org/auth/login">online-energieausweis.org/auth/login</a>.<br><br>
|
||||
|
||||
Sollten Sie diese Registrierung nicht vorgenommen haben, können Sie diese E-Mail einfach ignorieren. Ihr Benutzerkonto wird in diesem Fall nicht aktiviert.<br><br>
|
||||
|
||||
Falls Sie Fragen haben oder Unterstützung benötigen, stehen wir Ihnen gerne zur Verfügung. Kontaktieren Sie uns einfach unter <a href="mailto:support@online-energieausweis.org">support@online-energieausweis.org</a>.
|
||||
<p>
|
||||
Mit freundlichen Grüßen,
|
||||
<br>
|
||||
|
||||
@@ -7,7 +7,7 @@ export async function sendAusweisGespeichertMail(user: Benutzer, ausweis_id: str
|
||||
from: `"IBCornelsen" <info@online-energieausweis.org>`,
|
||||
to: user.email,
|
||||
subject: `Ihr Ausweis wurde gespeichert - IBCornelsen - (ID: ${ausweis_id})`,
|
||||
html: `<p>Sehr geehrte*r ${user.vorname} ${user.name},</p>
|
||||
html: `<p>Sehr geehrte/r ${user.vorname} ${user.name},</p>
|
||||
<p>Ihr Energieausweis wurde erfolgreich in Ihrem Konto gespeichert. Sie können ihn jederzeit in Ihrem Kundenbereich abrufen.<br><br>
|
||||
|
||||
Ihre Vorgänge und Ausweise können Sie in Ihrem Kundenkonto einsehen und bearbeiten:<br><br>
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
try {
|
||||
sent = true
|
||||
const response = await api.auth["passwort-vergessen"].GET.fetch({
|
||||
redirect,
|
||||
email
|
||||
})
|
||||
|
||||
@@ -57,7 +58,7 @@
|
||||
{#if showEmailSuccess}
|
||||
<div class="flex-row justify-between" style="margin-top: 10px">
|
||||
<a class="link link-hover"
|
||||
href="/auth/login{redirect ? `?redirect=${redirect}` : ""}">Einloggen</a
|
||||
href="/auth/login{redirect ? `?r=${redirect}` : ""}">Einloggen</a
|
||||
>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
let passwortWiederholen: string;
|
||||
|
||||
export let token: string;
|
||||
export let redirect: string | null = null;
|
||||
|
||||
let disabled = false;
|
||||
|
||||
@@ -28,7 +29,7 @@
|
||||
})
|
||||
|
||||
setTimeout(() => {
|
||||
window.location.href = "/auth/login"
|
||||
window.location.href = "/auth/login?r=" + encodeURIComponent(redirect ?? "/dashboard")
|
||||
}, 5000)
|
||||
} catch (e) {
|
||||
disabled = false
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
<script lang="ts">
|
||||
import {
|
||||
Reader,
|
||||
EnvelopeClosed,
|
||||
Cube,
|
||||
Person,
|
||||
} from "radix-svelte-icons";
|
||||
import { Tabs, Tab, TabList, TabPanel } from "../../components/Tabs/index.js";
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { addNotification } from "#components/Notifications/shared.js";
|
||||
import { loginClient } from "#lib/login.js";
|
||||
import EmbeddedLoginModule from "./EmbeddedLoginModule.svelte"
|
||||
import EmbeddedRegisterModule from "./EmbeddedRegisterModule.svelte"
|
||||
@@ -6,24 +7,32 @@
|
||||
export let onLogin: (response: Awaited<ReturnType<typeof loginClient>>) => any;
|
||||
export let email: string = "";
|
||||
export let password: string = "";
|
||||
|
||||
let route: "login" | "signup" = "login"
|
||||
|
||||
const navigate = (target: typeof route) => {
|
||||
route = target
|
||||
export let route: "login" | "signup" = "login"
|
||||
export let title: Record<typeof route, string> = {
|
||||
login: "Einloggen",
|
||||
signup: "Registrieren"
|
||||
}
|
||||
export let buttonText: Record<typeof route, string> = {
|
||||
login: "Einloggen",
|
||||
signup: "Registrieren"
|
||||
}
|
||||
|
||||
const loginData = {
|
||||
email,
|
||||
passwort: "",
|
||||
const navigate = (target: string) => {
|
||||
route = target as typeof route;
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if route == "login"}
|
||||
<EmbeddedLoginModule onLogin={onLogin} bind:email bind:password {navigate} />
|
||||
{:else}
|
||||
<EmbeddedRegisterModule bind:email bind:password onRegister={(response) => {
|
||||
email = response.email
|
||||
navigate("login")
|
||||
}} {navigate} />
|
||||
<EmbeddedLoginModule onLogin={onLogin} bind:email bind:password {navigate} title={title.login} buttonText={buttonText.login} />
|
||||
{:else if route == "signup"}
|
||||
<EmbeddedRegisterModule bind:email onRegister={(response) => {
|
||||
addNotification({
|
||||
message: "Registrierung erfolgreich",
|
||||
subtext: "Ein Passwort wurde an ihre Email Adresse gesendet, sie werden nun automatisch weitergeleitet..",
|
||||
type: "success",
|
||||
timeout: 6000,
|
||||
dismissable: true
|
||||
})
|
||||
onLogin(response)
|
||||
}} {navigate} title={title.signup} buttonText={buttonText.signup} />
|
||||
{/if}
|
||||
@@ -1,10 +1,11 @@
|
||||
<script lang="ts">
|
||||
import { addNotification } from "@ibcornelsen/ui";
|
||||
import { loginClient } from "#lib/login.js";
|
||||
|
||||
export let navigate: (target: string) => void;
|
||||
export let email: string;
|
||||
export let password: string;
|
||||
export let title: string = "Einloggen";
|
||||
export let buttonText: string = "Einloggen";
|
||||
|
||||
export let onLogin: (response: Awaited<ReturnType<typeof loginClient>>) => any;
|
||||
|
||||
@@ -13,21 +14,19 @@
|
||||
const response = await loginClient(email, password)
|
||||
|
||||
if (response === null) {
|
||||
addNotification({
|
||||
message: "Ups...",
|
||||
subtext: "Das hat leider nicht geklappt, haben sie ihr Passwort und ihre Email Adresse richtig eingegeben?",
|
||||
type: "error",
|
||||
timeout: 6000,
|
||||
dismissable: true
|
||||
})
|
||||
error = true;
|
||||
errorMessage = "Das hat leider nicht geklappt, haben sie ihr Passwort und ihre Email Adresse richtig eingegeben?"
|
||||
} else {
|
||||
onLogin(response);
|
||||
}
|
||||
}
|
||||
|
||||
let error = false;
|
||||
let errorMessage = "";
|
||||
</script>
|
||||
|
||||
<form style="width:50%;margin: 0 auto" on:submit={login} name="login">
|
||||
<h1 class="text-2xl font-semibold mb-6">Einloggen</h1>
|
||||
<form class="max-w-md mx-auto" on:submit={login} name="login">
|
||||
<h1 class="text-3xl mb-4 p-0">{title}</h1>
|
||||
<div class="flex flex-col gap-4">
|
||||
<div>
|
||||
<h4>Email</h4>
|
||||
@@ -37,6 +36,7 @@
|
||||
placeholder="Email"
|
||||
name="email"
|
||||
bind:value={email}
|
||||
on:focus={() => (error = false)}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
@@ -48,13 +48,19 @@
|
||||
placeholder="********"
|
||||
name="passwort"
|
||||
bind:value={password}
|
||||
on:focus={() => (error = false)}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<button class="button" type="submit">Einloggen</button>
|
||||
{#if error}
|
||||
<div class="bg-red-200 p-4 rounded-lg w-full">
|
||||
<p class="text-red-800">{errorMessage}</p>
|
||||
</div>
|
||||
{/if}
|
||||
<button class="button" type="submit">{buttonText}</button>
|
||||
<div class="flex flex-row justify-between" style="margin-top: 10px">
|
||||
<a on:click={() => navigate("signup")} class="cursor-pointer" data-cy="registrieren">Registrieren</a>
|
||||
<a href="/user/passwort_vergessen">Passwort Vergessen?</a>
|
||||
<a href="/auth/passwort-vergessen?r={window.location.href}">Passwort Vergessen?</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -1,95 +1,79 @@
|
||||
<script lang="ts">
|
||||
import { addNotification } from "@ibcornelsen/ui";
|
||||
import { loginClient } from "#lib/login.js";
|
||||
import { api } from "astro-typesafe-api/client";
|
||||
|
||||
export let navigate: (target: string) => void;
|
||||
export let onRegister: (response: { email: string, name: string, vorname: string }) => void;
|
||||
export let password: string;
|
||||
export let onRegister: (response: Awaited<ReturnType<typeof loginClient>>) => void;
|
||||
export let email: string;
|
||||
let vorname: string;
|
||||
let name: string;
|
||||
export let title: string = "Registrieren";
|
||||
export let buttonText: string = "Registrieren";
|
||||
let repeatEmail: string;
|
||||
|
||||
async function signUp(e: SubmitEvent) {
|
||||
async function signup(e: SubmitEvent) {
|
||||
e.preventDefault()
|
||||
|
||||
if (email !== repeatEmail) {
|
||||
error = true;
|
||||
errorMessage = "Die eingegebenen Email Adressen stimmen nicht überein.";
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await api.user.PUT.fetch({
|
||||
const { id, passwort } = await api.user.autocreate.PUT.fetch({
|
||||
email,
|
||||
passwort: password,
|
||||
vorname,
|
||||
name,
|
||||
});
|
||||
|
||||
onRegister({
|
||||
email,
|
||||
name,
|
||||
vorname
|
||||
})
|
||||
const response = await loginClient(email, passwort)
|
||||
onRegister(response);
|
||||
} catch (e) {
|
||||
addNotification({
|
||||
message: "Ups...",
|
||||
subtext:
|
||||
"Da ist wohl etwas schiefgelaufen. Diese Email Adresse ist bereits in Benutzung, haben sie vielleicht bereits ein Konto bei uns?",
|
||||
type: "error",
|
||||
timeout: 0,
|
||||
dismissable: true,
|
||||
});
|
||||
error = true;
|
||||
errorMessage = "Sie besitzen bereits ein Konto bei IBC. Bitte loggen Sie sich mit Ihrem Passwort ein oder vergeben sich über “Passwort vergessen” ein neues."
|
||||
navigate("login");
|
||||
}
|
||||
}
|
||||
|
||||
let error: boolean = false;
|
||||
let errorMessage: string = "";
|
||||
</script>
|
||||
|
||||
<form style="width:50%;margin: 0 auto" name="signup" on:submit={signUp}>
|
||||
<h1>Registrieren</h1>
|
||||
<form class="max-w-md mx-auto" name="signup" on:submit={signup}>
|
||||
<h1 class="text-3xl mb-4 p-0">{title}</h1>
|
||||
<p class="p-0 text-base">Ihre Ausweisdaten werden bei uns gespeichert und Ihnen wird ein vorläufiges Passwort erstellt. Sollte Ihre E-Mail bereits bei uns registriert sein, dann loggen Sie sich bitte mit Ihrem vorhandenen Passwort ein oder vergeben sich ein neues über “Passwort vergessen”.</p>
|
||||
<hr>
|
||||
<div class="flex flex-col gap-4">
|
||||
<div class="flex flex-row gap-4 w-full">
|
||||
<div class="w-1/2">
|
||||
<h4>Vorname</h4>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Vorname"
|
||||
name="vorname"
|
||||
class="px-2.5 py-1.5 rounded-lg border bg-gray-50"
|
||||
bind:value={vorname}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div class="w-1/2">
|
||||
<h4>Nachname</h4>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Nachname"
|
||||
name="nachname"
|
||||
class="px-2.5 py-1.5 rounded-lg border bg-gray-50"
|
||||
bind:value={name}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<h4>Email</h4>
|
||||
<input
|
||||
type="email"
|
||||
placeholder="Email"
|
||||
placeholder="max.mustermann@email.de"
|
||||
name="email"
|
||||
class="px-2.5 py-1.5 rounded-lg border bg-gray-50"
|
||||
bind:value={email}
|
||||
on:keyup={() => (email = email.toLowerCase())}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<h4>Passwort</h4>
|
||||
<div class="flex flex-col gap-2">
|
||||
<h4>Email erneut eingeben</h4>
|
||||
<input
|
||||
type="password"
|
||||
placeholder="********"
|
||||
name="passwort"
|
||||
class="px-2.5 py-1.5 rounded-lg border bg-gray-50"
|
||||
bind:value={password}
|
||||
type="text"
|
||||
placeholder="max.mustermann@email.de"
|
||||
name="email"
|
||||
class="input input-bordered text-base text-base-content font-medium"
|
||||
bind:value={repeatEmail}
|
||||
on:keyup={() => (repeatEmail = repeatEmail.toLowerCase())}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<button class="button" type="submit">Registrieren</button>
|
||||
<div class="flex-row justify-between" style="margin-top: 10px">
|
||||
{#if error}
|
||||
<div class="bg-red-200 p-4 rounded-lg w-full">
|
||||
<p class="text-red-800">{errorMessage}</p>
|
||||
</div>
|
||||
{/if}
|
||||
<button class="button" type="submit">{buttonText}</button>
|
||||
<div class="flex flex-row justify-between" style="margin-top: 10px">
|
||||
<button on:click={() => navigate("login")}>Einloggen</button>
|
||||
<a href="/user/passwort_vergessen">Passwort Vergessen?</a>
|
||||
<a href="/auth/passwort-vergessen?r={window.location.href}">Passwort Vergessen?</a>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -1,384 +0,0 @@
|
||||
<script lang="ts">
|
||||
import ZipSearch from "../components/PlzSuche.svelte";
|
||||
import Label from "../components/Label.svelte";
|
||||
import type {
|
||||
Bezahlmethoden,
|
||||
} from "#lib/client/prisma";
|
||||
import { Enums } from "#lib/client/prisma";
|
||||
import PaymentOption from "#components/PaymentOption.svelte";
|
||||
import CheckoutItem from "#components/CheckoutItem.svelte";
|
||||
import { BenutzerClient, VerbrauchsausweisWohnenClient } from "#components/Ausweis/types.js";
|
||||
import { PRICES } from "#lib/constants.js";
|
||||
import { RechnungClient } from "#components/Ausweis/types.js";
|
||||
import { api } from "astro-typesafe-api/client";
|
||||
|
||||
export let user: BenutzerClient;
|
||||
export let ausweis:
|
||||
| VerbrauchsausweisWohnenClient;
|
||||
// TODO: überarbeiten und zu inferProcedureOutput machen
|
||||
let rechnung: Partial<RechnungClient> = {
|
||||
email: user.email,
|
||||
empfaenger: user.vorname + " " + user.name,
|
||||
strasse: user.adresse,
|
||||
plz: user.plz,
|
||||
ort: user.ort,
|
||||
versand_empfaenger: user.vorname + " " + user.name,
|
||||
versand_strasse: user.adresse,
|
||||
versand_plz: user.plz,
|
||||
versand_ort: user.ort,
|
||||
telefon: user.telefon,
|
||||
};
|
||||
|
||||
let services = [
|
||||
{
|
||||
name: "Qualitätsdruck per Post (zusätzlich zur PDF Version) für 9€ inkl. MwSt.",
|
||||
id: Enums.Service.Qualitaetsdruck,
|
||||
price: 9,
|
||||
selected: false,
|
||||
},
|
||||
{
|
||||
name: "Aushang (für öffentliche Gebäude gesetzlich vorgeschrieben) für 10€ inkl. MwSt.",
|
||||
id: Enums.Service.Aushang,
|
||||
price: 10,
|
||||
selected: false,
|
||||
},
|
||||
{
|
||||
name: "Same Day Service (Bestellung Werktags vor 12:00 Uhr - Ausstellung bis 18:00 Uhr am gleichen Tag) für 29€ inkl. MwSt.",
|
||||
id: Enums.Service.SameDay,
|
||||
price: 29,
|
||||
selected: false,
|
||||
},
|
||||
{
|
||||
name: "Telefonische Energieeffizienzberatung für 75€ inkl. MwSt.",
|
||||
id: Enums.Service.Telefonberatung,
|
||||
price: 75,
|
||||
selected: false,
|
||||
},
|
||||
];
|
||||
|
||||
export let aktiveBezahlmethode: Bezahlmethoden =
|
||||
Enums.Bezahlmethoden.paypal;
|
||||
|
||||
async function createPayment(e: SubmitEvent) {
|
||||
e.preventDefault();
|
||||
|
||||
// TODO
|
||||
const response = await api.rechnung.PUT.fetch({
|
||||
...rechnung,
|
||||
ausweisart: Enums.Ausweisart.VerbrauchsausweisWohnen,
|
||||
ausweis_uid: ausweis.uid,
|
||||
bezahlmethode: aktiveBezahlmethode,
|
||||
services: services
|
||||
.filter((service) => service.selected)
|
||||
.map((service) => service.id),
|
||||
});
|
||||
|
||||
if (aktiveBezahlmethode === Enums.Bezahlmethoden.rechnung) {
|
||||
window.location.href = `/payment/success?r=${response.uid}&a=${ausweis.uid}`
|
||||
} else {
|
||||
window.location.href = response.checkout_url as string;
|
||||
}
|
||||
}
|
||||
|
||||
const priceTotal = services.reduce((acc, service) => {
|
||||
if (service.selected) {
|
||||
return acc + service.price;
|
||||
}
|
||||
return acc;
|
||||
}, 0) + PRICES[Enums.Ausweisart.VerbrauchsausweisWohnen][0];
|
||||
</script>
|
||||
|
||||
<form class="grid grid-cols-[2fr_1fr] gap-4 h-full" on:submit={createPayment}>
|
||||
<div>
|
||||
<h3 class="font-semibold">Ansprechpartner</h3>
|
||||
<div class="rounded-lg border p-4 border-base-300 bg-base-100">
|
||||
<div class="grid grid-cols-3 gap-4">
|
||||
<!-- Anrede -->
|
||||
<div>
|
||||
<Label>Anrede *</Label>
|
||||
<div>
|
||||
<select name="anrede" bind:value={user.anrede}>
|
||||
<option>bitte auswählen</option>
|
||||
<option value="Herr">Herr</option>
|
||||
<option value="Frau">Frau</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Vorname -->
|
||||
<div>
|
||||
<Label>Vorname *</Label>
|
||||
<input
|
||||
name="vorname"
|
||||
type="text"
|
||||
bind:value={user.vorname}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Nachname -->
|
||||
<div>
|
||||
<Label>Nachname *</Label>
|
||||
<input
|
||||
name="name"
|
||||
type="text"
|
||||
bind:value={user.name}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<!-- Telefon -->
|
||||
<div>
|
||||
<Label>Telefon</Label>
|
||||
<input
|
||||
name="telefon"
|
||||
bind:value={user.telefon}
|
||||
type="text"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Email -->
|
||||
<div>
|
||||
<Label>E-Mail *</Label>
|
||||
<input
|
||||
name="email"
|
||||
type="email"
|
||||
bind:value={user.email}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3 class="mt-8 font-semibold">Rechnungsadresse</h3>
|
||||
<div class="rounded-lg border p-4 border-base-300 bg-base-100">
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<Label>Empfänger *</Label>
|
||||
<input
|
||||
name="rechnung_empfaenger"
|
||||
type="text"
|
||||
bind:value={rechnung.empfaenger}
|
||||
required
|
||||
data-rule-maxlength="100"
|
||||
data-msg-maxlength="max. 100 Zeichen"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Zusatzzeile -->
|
||||
<div>
|
||||
<Label>Zusatzzeile</Label>
|
||||
<input
|
||||
name="rechnung_zusatzzeile"
|
||||
bind:value={rechnung.zusatzzeile}
|
||||
type="text"
|
||||
data-rule-maxlength="80"
|
||||
data-msg-maxlength="max. 80 Zeichen"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-3 gap-4">
|
||||
<!-- Strasse -->
|
||||
<div>
|
||||
<Label>Straße, Hausnummer *</Label>
|
||||
<input
|
||||
name="rechnung_strasse"
|
||||
bind:value={rechnung.strasse}
|
||||
type="text"
|
||||
required
|
||||
data-rule-maxlength="40"
|
||||
data-msg-maxlength="max. 40 Zeichen"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- PLZ -->
|
||||
<ZipSearch
|
||||
name="rechnung_plz"
|
||||
bind:zip={rechnung.plz}
|
||||
bind:city={rechnung.ort}
|
||||
/>
|
||||
|
||||
<!-- Ort -->
|
||||
<div>
|
||||
<Label>Ort *</Label>
|
||||
<input
|
||||
name="rechnung_ort"
|
||||
readonly
|
||||
type="text"
|
||||
required
|
||||
value={rechnung.ort}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<!-- Telefon -->
|
||||
<div>
|
||||
<Label>Telefon</Label>
|
||||
<input
|
||||
name="rechnung_telefon"
|
||||
bind:value={rechnung.telefon}
|
||||
type="text"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Email -->
|
||||
<div>
|
||||
<Label>E-Mail</Label>
|
||||
<input
|
||||
name="rechnung_email"
|
||||
bind:value={rechnung.email}
|
||||
type="email"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3 class="mt-8 font-semibold">Versandadresse</h3>
|
||||
<div class="rounded-lg border p-4 border-base-300 bg-base-100">
|
||||
<div class="flex flex-row gap-2 items-center">
|
||||
<input
|
||||
class="w-[15px] h-[15px]"
|
||||
type="checkbox"
|
||||
name="abweichende_versand_adresse"
|
||||
bind:checked={rechnung.abweichende_versand_adresse}
|
||||
/>
|
||||
<Label>Abweichende Versandadresse</Label>
|
||||
</div>
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<!-- Empfänger -->
|
||||
<div>
|
||||
<Label>Empfänger *</Label>
|
||||
<input
|
||||
name="versand_empfaenger"
|
||||
type="text"
|
||||
readonly={!rechnung.abweichende_versand_adresse}
|
||||
bind:value={rechnung.versand_empfaenger}
|
||||
required
|
||||
data-rule-maxlength="100"
|
||||
data-msg-maxlength="max. 100 Zeichen"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Zusatzzeile -->
|
||||
<div>
|
||||
<Label>Zusatzzeile</Label>
|
||||
<input
|
||||
name="versand_zusatzzeile"
|
||||
type="text"
|
||||
readonly={!rechnung.abweichende_versand_adresse}
|
||||
bind:value={rechnung.versand_zusatzzeile}
|
||||
data-rule-maxlength="80"
|
||||
data-msg-maxlength="max. 80 Zeichen"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="grid grid-cols-3 gap-4">
|
||||
<!-- Strasse -->
|
||||
<div>
|
||||
<Label>Straße, Hausnummer *</Label>
|
||||
<input
|
||||
name="versand_strasse"
|
||||
type="text"
|
||||
readonly={!rechnung.abweichende_versand_adresse}
|
||||
bind:value={rechnung.versand_strasse}
|
||||
required
|
||||
data-rule-maxlength="40"
|
||||
data-msg-maxlength="max. 40 Zeichen"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- PLZ -->
|
||||
<ZipSearch
|
||||
name="versand_plz"
|
||||
readonly={!rechnung.abweichende_versand_adresse}
|
||||
bind:zip={rechnung.versand_plz}
|
||||
bind:city={rechnung.versand_ort}
|
||||
/>
|
||||
|
||||
<!-- Ort -->
|
||||
<div>
|
||||
<Label>Ort *</Label>
|
||||
<input
|
||||
name="versand_ort"
|
||||
type="text"
|
||||
readonly
|
||||
required
|
||||
bind:value={rechnung.versand_ort}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3 class="mt-8 font-semibold">Bezahlmethode</h3>
|
||||
<div
|
||||
class="rounded-lg border p-4 border-base-300 bg-base-100 flex flex-row gap-4 justify-between"
|
||||
>
|
||||
<PaymentOption
|
||||
bezahlmethode={Enums.Bezahlmethoden.paypal}
|
||||
bind:aktiveBezahlmethode
|
||||
name={"PayPal"}
|
||||
icon={"/images/paypal.png"}
|
||||
></PaymentOption>
|
||||
<PaymentOption
|
||||
bezahlmethode={Enums.Bezahlmethoden.sofort}
|
||||
bind:aktiveBezahlmethode
|
||||
name={"Sofort"}
|
||||
icon={"/images/sofort.png"}
|
||||
></PaymentOption>
|
||||
<PaymentOption
|
||||
bezahlmethode={Enums.Bezahlmethoden.giropay}
|
||||
bind:aktiveBezahlmethode
|
||||
name={"Giropay"}
|
||||
icon={"/images/giropay.png"}
|
||||
></PaymentOption>
|
||||
<PaymentOption
|
||||
bezahlmethode={Enums.Bezahlmethoden.creditcard}
|
||||
bind:aktiveBezahlmethode
|
||||
name={"Kreditkarte"}
|
||||
icon={"/images/mastercard.png"}
|
||||
></PaymentOption>
|
||||
<PaymentOption
|
||||
bezahlmethode={Enums.Bezahlmethoden.rechnung}
|
||||
bind:aktiveBezahlmethode
|
||||
name={"Rechnung"}
|
||||
icon={"/images/rechnung.png"}
|
||||
></PaymentOption>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<h3 class="font-semibold">Zusammenfassung</h3>
|
||||
<div class="rounded-lg border p-4 border-base-300 bg-base-100">
|
||||
<CheckoutItem
|
||||
image={"https://www.gih.de/wp-content/uploads/2015/11/EnergieausweisW-E.jpg"}
|
||||
name={"Energieausweis"}
|
||||
description={"Verbrauchsausweis Wohnen"}
|
||||
price={45}
|
||||
quantity={1}
|
||||
removable={false}
|
||||
maxQuantity={1}
|
||||
/>
|
||||
<div class="mt-auto">
|
||||
<hr />
|
||||
<div class="flex flex-row items-center justify-between">
|
||||
<span class="opacity-75 text-sm">Netto</span>
|
||||
<span class="font-semibold text-sm">{Math.round(priceTotal * 0.81 * 100) / 100}€</span>
|
||||
</div>
|
||||
<div class="flex flex-row items-center justify-between">
|
||||
<span class="opacity-75 text-sm">19% MwSt</span>
|
||||
<span class="font-semibold text-sm">{Math.round(priceTotal * 0.19 * 100) / 100}}€</span>
|
||||
</div>
|
||||
<hr />
|
||||
<div class="flex flex-row items-center justify-between">
|
||||
<span class="opacity-75 text-sm">Gesamt</span>
|
||||
<span class="font-semibold text-sm">{Math.round(priceTotal)}€</span>
|
||||
</div>
|
||||
<p class="mt-8">Mit dem Klick auf "Bestellung Bestätigen" akzeptieren sie unsere <a href="/agb">AGB</a> und <a href="/impressum">Datenschutzbestimmungen</a>. Sie werden zu ihrem ausgewählten Bezahlprovider weitergeleitet, nach Bezahlung werden sie automatisch zu unserem Portal zurückgeleitet.</p>
|
||||
<button class="btn btn-secondary w-full mt-4"
|
||||
>Bestellung Bestätigen</button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
@@ -2,7 +2,7 @@
|
||||
import PerformanceScore from "#components/Ausweis/PerformanceScore.svelte";
|
||||
import Progressbar from "#components/Ausweis/Progressbar.svelte";
|
||||
import Bereich from "#components/labels/Bereich.svelte";
|
||||
import type { BedarfsausweisGewerbe, BedarfsausweisWohnen, Bezahlmethoden, GEGNachweisGewerbe, GEGNachweisWohnen, Unterlage, VerbrauchsausweisGewerbe, VerbrauchsausweisWohnen } from "#lib/client/prisma.js";
|
||||
import type { BedarfsausweisGewerbe, BedarfsausweisWohnen, Benutzer, Bezahlmethoden, GEGNachweisGewerbe, GEGNachweisWohnen, Unterlage, VerbrauchsausweisGewerbe, VerbrauchsausweisWohnen } from "#lib/client/prisma.js";
|
||||
import { Enums } from "#lib/client/prisma.js";
|
||||
import {
|
||||
API_ACCESS_TOKEN_COOKIE_NAME,
|
||||
@@ -34,7 +34,6 @@
|
||||
import { endEnergieVerbrauchVerbrauchsausweisGewerbe_2016_Client } from "#lib/Berechnungen/VerbrauchsausweisGewerbe/VerbrauchsausweisGewerbe_2016_Client.js";
|
||||
import { benutzerSpeichern } from "#client/lib/speichern.js";
|
||||
import { benutzerLesen } from "#client/lib/lesen.js";
|
||||
import { exclude } from "#lib/exclude.js";
|
||||
|
||||
export let user: Partial<BenutzerClient>;
|
||||
export let impersonatedUser: Partial<BenutzerClient> | null = null;
|
||||
@@ -46,9 +45,8 @@
|
||||
export let rechnung: RechnungClient | null = null;
|
||||
export let ausweisart: Enums.Ausweisart;
|
||||
export let aktiveBezahlmethode: Bezahlmethoden = Enums.Bezahlmethoden.paypal;
|
||||
|
||||
export let partner_code: string;
|
||||
|
||||
export let nurRechnungsadresseUpdate: Boolean | null = false;
|
||||
|
||||
let email: string, vorname: string, name: string, empfaenger: string, strasse: string, plz: string, ort: string, zusatzzeile: string, telefon: string;
|
||||
|
||||
@@ -57,7 +55,7 @@
|
||||
email = rechnung?.email || localStorage.getItem("kundendaten.email") || user.email || "";
|
||||
vorname = localStorage.getItem("kundendaten.vorname") || user.vorname || "";
|
||||
name = localStorage.getItem("kundendaten.name") || user.name || "";
|
||||
empfaenger = rechnung?.empfaenger || localStorage.getItem("kundendaten.empfaenger") || (user.vorname && user.name ? `${user.vorname} ${user.name}` : "")
|
||||
empfaenger = rechnung?.empfaenger || localStorage.getItem("kundendaten.empfaenger") || user.empfaenger
|
||||
strasse = rechnung?.strasse || localStorage.getItem("kundendaten.strasse") || user.adresse || "";
|
||||
plz = rechnung?.plz || localStorage.getItem("kundendaten.plz") || user.plz || "";
|
||||
ort = rechnung?.ort || localStorage.getItem("kundendaten.ort") || user.ort || "";
|
||||
@@ -67,9 +65,18 @@
|
||||
vorname = impersonatedUser.vorname || "";
|
||||
name = impersonatedUser.name || "";
|
||||
telefon = impersonatedUser.telefon || "";
|
||||
email = impersonatedUser.email || "";
|
||||
if (rechnung){
|
||||
empfaenger = rechnung?.empfaenger || "";
|
||||
strasse = rechnung?.strasse || "";
|
||||
plz = rechnung?.plz || "";
|
||||
ort = rechnung?.ort || "";
|
||||
zusatzzeile = rechnung?.zusatzzeile || "";
|
||||
email = rechnung?.email || "";
|
||||
}
|
||||
}
|
||||
|
||||
let abweichende_versand_adresse = JSON.parse(localStorage.getItem("kundendaten.abweichende_versand_adresse") || "false")
|
||||
let abweichende_versand_adresse = rechnung?.abweichende_versand_adresse ?? JSON.parse(localStorage.getItem("kundendaten.abweichende_versand_adresse") || "false")
|
||||
|
||||
let versand_email: string | undefined,
|
||||
versand_zusatzzeile: string | undefined,
|
||||
@@ -77,16 +84,13 @@
|
||||
versand_strasse: string | undefined,
|
||||
versand_plz: string | undefined,
|
||||
versand_ort: string | undefined;
|
||||
$: {
|
||||
if (!abweichende_versand_adresse) {
|
||||
versand_email = email
|
||||
versand_zusatzzeile = zusatzzeile
|
||||
versand_empfaenger = empfaenger
|
||||
versand_strasse = strasse
|
||||
versand_plz = plz
|
||||
versand_ort = ort
|
||||
}
|
||||
}
|
||||
|
||||
versand_email = "";//Todo Datenbankfeld fehlt noch
|
||||
versand_zusatzzeile = rechnung?.versand_zusatzzeile ?? zusatzzeile;
|
||||
versand_empfaenger = rechnung?.versand_empfaenger ?? empfaenger;
|
||||
versand_strasse = rechnung?.versand_strasse ?? strasse;
|
||||
versand_plz = rechnung?.versand_plz ?? plz;
|
||||
versand_ort = rechnung?.versand_ort ?? ort;
|
||||
|
||||
$: {
|
||||
// Wir speichern jede Änderung an den Kundendaten im localStorage ab.
|
||||
@@ -166,6 +170,7 @@
|
||||
|
||||
async function anfordern() {
|
||||
if (!form.checkValidity()) {
|
||||
displayFormValidity()
|
||||
addNotification({
|
||||
dismissable: true,
|
||||
message: "Fehlende Daten.",
|
||||
@@ -207,6 +212,12 @@
|
||||
}
|
||||
|
||||
try {
|
||||
const merged_versand_empfaenger = versand_empfaenger || empfaenger;
|
||||
const merged_versand_strasse = versand_strasse || strasse;
|
||||
const merged_versand_plz = versand_plz || plz;
|
||||
const merged_versand_ort = versand_ort || ort;
|
||||
const merged_versand_zusatzzeile = versand_zusatzzeile || zusatzzeile;
|
||||
|
||||
const { id } = await api.rechnung.anfordern.PUT.fetch(
|
||||
{
|
||||
email: email,
|
||||
@@ -214,10 +225,11 @@
|
||||
strasse: strasse,
|
||||
plz: plz,
|
||||
ort: ort,
|
||||
versand_empfaenger: versand_empfaenger,
|
||||
versand_strasse: versand_strasse,
|
||||
versand_plz: versand_plz,
|
||||
versand_ort: versand_ort,
|
||||
versand_empfaenger: merged_versand_empfaenger,
|
||||
versand_strasse: merged_versand_strasse,
|
||||
versand_plz: merged_versand_plz,
|
||||
versand_ort: merged_versand_ort,
|
||||
versand_zusatzzeile: merged_versand_zusatzzeile,
|
||||
telefon: telefon,
|
||||
nachweis_id: result.nachweis_id
|
||||
},
|
||||
@@ -274,6 +286,45 @@
|
||||
|
||||
resultUser = await benutzerSpeichern(benutzerObjekt);
|
||||
|
||||
|
||||
let id: string, checkout_url: string | undefined;
|
||||
|
||||
if (rechnung) {
|
||||
const merged_versand_empfaenger = versand_empfaenger || empfaenger;
|
||||
const merged_versand_strasse = versand_strasse || strasse;
|
||||
const merged_versand_plz = versand_plz || plz;
|
||||
const merged_versand_ort = versand_ort || ort;
|
||||
const merged_versand_zusatzzeile = versand_zusatzzeile || zusatzzeile;
|
||||
|
||||
const result = await api.rechnung._id.PATCH.fetch({
|
||||
bezahlmethode: aktiveBezahlmethode,
|
||||
abweichende_versand_adresse: abweichende_versand_adresse,
|
||||
empfaenger,
|
||||
strasse,
|
||||
plz,
|
||||
ort,
|
||||
telefon,
|
||||
email,
|
||||
zusatzzeile,
|
||||
versand_empfaenger: merged_versand_empfaenger,
|
||||
versand_strasse: merged_versand_strasse,
|
||||
versand_plz: merged_versand_plz,
|
||||
versand_ort: merged_versand_ort,
|
||||
versand_zusatzzeile: merged_versand_zusatzzeile
|
||||
}, {
|
||||
params: {
|
||||
id: rechnung.id
|
||||
},
|
||||
headers: {
|
||||
Authorization: `Bearer ${Cookies.get(API_ACCESS_TOKEN_COOKIE_NAME)}`,
|
||||
},
|
||||
});
|
||||
|
||||
id = result.id;
|
||||
checkout_url = result.checkout_url;
|
||||
}
|
||||
|
||||
|
||||
} catch(e) {
|
||||
addNotification({
|
||||
dismissable: true,
|
||||
@@ -294,8 +345,30 @@
|
||||
}
|
||||
}
|
||||
|
||||
async function bestellen(authuser = null) {
|
||||
function displayFormValidity() {
|
||||
(form.querySelectorAll("select[name][required], input[name][required]") as NodeListOf<HTMLInputElement | HTMLSelectElement>).forEach((element) => {
|
||||
if (element.willValidate && !element.checkValidity()) {
|
||||
element.dataset["isinvalid"] = "true"
|
||||
|
||||
const onChange = () => {
|
||||
console.log(element, element.value, element.checkValidity());
|
||||
|
||||
if (!element.checkValidity()) {
|
||||
return;
|
||||
}
|
||||
element.dataset["isinvalid"] = "false"
|
||||
element.removeEventListener("change", onChange)
|
||||
}
|
||||
|
||||
element.addEventListener("change", onChange)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
async function bestellen(authuser: Benutzer | null = null) {
|
||||
if (!form.checkValidity()) {
|
||||
displayFormValidity()
|
||||
|
||||
addNotification({
|
||||
dismissable: true,
|
||||
message: "Fehlende Daten.",
|
||||
@@ -361,6 +434,13 @@
|
||||
try {
|
||||
let id: string, checkout_url: string | undefined;
|
||||
|
||||
const merged_versand_empfaenger = versand_empfaenger || empfaenger;
|
||||
const merged_versand_strasse = versand_strasse || strasse;
|
||||
const merged_versand_plz = versand_plz || plz;
|
||||
const merged_versand_ort = versand_ort || ort;
|
||||
const merged_versand_zusatzzeile = versand_zusatzzeile || zusatzzeile;
|
||||
|
||||
|
||||
if (rechnung) {
|
||||
const result = await api.rechnung._id.PATCH.fetch({
|
||||
bezahlmethode: aktiveBezahlmethode,
|
||||
@@ -369,12 +449,13 @@
|
||||
strasse: strasse,
|
||||
plz: plz,
|
||||
ort: ort,
|
||||
versand_empfaenger: versand_empfaenger,
|
||||
versand_strasse: versand_strasse,
|
||||
versand_plz: versand_plz,
|
||||
versand_ort: versand_ort,
|
||||
telefon: telefon,
|
||||
versand_zusatzzeile: versand_zusatzzeile
|
||||
zusatzzeile: zusatzzeile,
|
||||
versand_empfaenger: merged_versand_empfaenger,
|
||||
versand_strasse: merged_versand_strasse,
|
||||
versand_plz: merged_versand_plz,
|
||||
versand_ort: merged_versand_ort,
|
||||
versand_zusatzzeile: merged_versand_zusatzzeile
|
||||
}, {
|
||||
params: {
|
||||
id: rechnung.id
|
||||
@@ -399,13 +480,16 @@
|
||||
strasse: strasse,
|
||||
plz: plz,
|
||||
ort: ort,
|
||||
versand_empfaenger: versand_empfaenger,
|
||||
versand_strasse: versand_strasse,
|
||||
versand_plz: versand_plz,
|
||||
versand_ort: versand_ort,
|
||||
zusatzzeile: zusatzzeile,
|
||||
versand_empfaenger: merged_versand_empfaenger,
|
||||
versand_strasse: merged_versand_strasse,
|
||||
versand_plz: merged_versand_plz,
|
||||
versand_ort: merged_versand_ort,
|
||||
versand_zusatzzeile: merged_versand_zusatzzeile,
|
||||
telefon: telefon,
|
||||
ausweis_id: ausweis.id,
|
||||
partner_code
|
||||
partner_code,
|
||||
abweichende_versand_adresse: abweichende_versand_adresse
|
||||
},
|
||||
{
|
||||
headers: {
|
||||
@@ -443,8 +527,17 @@
|
||||
let loginOverlayHidden = true;
|
||||
let loginAction = () => {};
|
||||
let form: HTMLFormElement;
|
||||
let ortInput: HTMLInputElement;
|
||||
|
||||
$: {
|
||||
if (ort && ortInput) {
|
||||
ortInput.value = ort
|
||||
ortInput.dispatchEvent(new Event("change"))
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if !nurRechnungsadresseUpdate}
|
||||
<div
|
||||
id="skala"
|
||||
class="bg-white grid grid-cols-1 gap-x-8 gap-y-4 px-0 sm:p-4
|
||||
@@ -475,7 +568,7 @@
|
||||
<Progressbar active={1} {ausweisart} ausweistyp={ausweis.ausweistyp} anliegen={"Energieausweis erstellen"} />
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
{/if}
|
||||
|
||||
<form id="formInput-2" bind:this={form}>
|
||||
<div id="formular-box" class="formular-boxen ring-0">
|
||||
@@ -600,6 +693,9 @@ xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8
|
||||
name="rechnung_plz"
|
||||
bind:zip={plz}
|
||||
bind:city={ort}
|
||||
onchange={(e) => {
|
||||
ortInput.dispatchEvent(new Event('change'));
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -611,6 +707,7 @@ xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8
|
||||
type="text"
|
||||
required
|
||||
bind:value={ort}
|
||||
bind:this={ortInput}
|
||||
/>
|
||||
|
||||
<div class="help-label">
|
||||
@@ -743,7 +840,6 @@ xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8
|
||||
<input
|
||||
name="versand_ort"
|
||||
type="text"
|
||||
readonly
|
||||
required
|
||||
bind:value={versand_ort}
|
||||
/>
|
||||
@@ -780,7 +876,7 @@ xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8
|
||||
</div>
|
||||
|
||||
<!-- E-mail -->
|
||||
|
||||
<!-- Versand Email wird derzeit nicht verwendet
|
||||
<div class="input-standard order-12 md:order-12 xl:order-12">
|
||||
<InputLabel title="E-mail *"></InputLabel>
|
||||
|
||||
@@ -797,7 +893,7 @@ xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8
|
||||
</HelpLabel>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
-->
|
||||
<!-- Telefon
|
||||
|
||||
<div class="input-standard order-[13] md:order-[13] xl:order-[13]">
|
||||
@@ -821,7 +917,7 @@ xl:grid-cols-3 xl:gap-x-8 xl:gap-y-8
|
||||
</Bereich
|
||||
>
|
||||
|
||||
{#if !gegAnfrage}
|
||||
{#if !gegAnfrage && !nurRechnungsadresseUpdate}
|
||||
<Bereich bereich="3" title="Bezahlmethode">
|
||||
<div
|
||||
id="bezahlung"
|
||||
@@ -932,7 +1028,7 @@ grid-cols-3 sm:grid-cols-5 justify-around justify-items-center items-center"
|
||||
{/if}
|
||||
|
||||
<!-- Falls wir es mit einem Ausweis zu tun haben und der Nutzer keine Hilfe bestellt hat dann zeigen wir eine Box mit sachen an die vielleicht unklar sind. -->
|
||||
{#if (ausweisart === Enums.Ausweisart.VerbrauchsausweisWohnen || ausweisart === Enums.Ausweisart.VerbrauchsausweisGewerbe || ausweisart === Enums.Ausweisart.BedarfsausweisWohnen) && (ausweis.ausweistyp === Enums.AusweisTyp.Standard || ausweis.ausweistyp === Enums.AusweisTyp.standardXL)}
|
||||
{#if (ausweisart === Enums.Ausweisart.VerbrauchsausweisWohnen || ausweisart === Enums.Ausweisart.VerbrauchsausweisGewerbe || ausweisart === Enums.Ausweisart.BedarfsausweisWohnen) && (ausweis.ausweistyp === Enums.AusweisTyp.Standard || ausweis.ausweistyp === Enums.AusweisTyp.standardXL) && (!nurRechnungsadresseUpdate)}
|
||||
<div class="grid grid-cols-1 sm:grid-cols-1 gap-x-6 my-6">
|
||||
<div class="pruefpunkte bereich-box bg-white">
|
||||
<h3>Bitte untenstehende Punkte bestätigen. Bitte gehen Sie gegebenenfalls zurück zum Formular und überprüfen bzw. korrigieren Ihre Eingaben.</h3>
|
||||
@@ -1095,7 +1191,7 @@ grid-cols-3 sm:grid-cols-5 justify-around justify-items-center items-center"
|
||||
{/if}
|
||||
<!-- Für alle -->
|
||||
<div class="pruefpunkt">
|
||||
<input type="checkbox"/>
|
||||
<input type="checkbox" required/>
|
||||
<div class="text-left">
|
||||
Ich habe die AGB und DSGVO im <a href="/impressum#agb" target="_blank" rel="noopener noreferrer">Impressum</a> gelesen und akzeptiert.
|
||||
</div>
|
||||
@@ -1115,6 +1211,7 @@ sm:grid-cols-[min-content_min-content_min-content] sm:justify-self-end sm:mt-8"
|
||||
|
||||
<button class="order-2 button" type="button" on:click={() => speichern()}>Speichern</button>
|
||||
|
||||
{#if !nurRechnungsadresseUpdate}
|
||||
{#if rechnung && rechnung.status === "paid"}
|
||||
<!-- Von einer GEG Anfrage sollte man sowieso nicht noch mal auf die Kundendaten Seite gelangen, also brauchen wir das hier nicht. -->
|
||||
<button
|
||||
@@ -1140,6 +1237,7 @@ sm:grid-cols-[min-content_min-content_min-content] sm:justify-self-end sm:mt-8"
|
||||
>
|
||||
{/if}
|
||||
{/if}
|
||||
{/if}
|
||||
|
||||
{#if user.rolle === Enums.BenutzerRolle.ADMIN}
|
||||
<button class="button text-sm" on:click={() => {
|
||||
@@ -1150,14 +1248,24 @@ sm:grid-cols-[min-content_min-content_min-content] sm:justify-self-end sm:mt-8"
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Overlay bind:hidden={loginOverlayHidden}>
|
||||
<div class="bg-white w-full max-w-screen-sm py-8">
|
||||
<EmbeddedAuthFlowModule onLogin={loginAction} email={email}></EmbeddedAuthFlowModule>
|
||||
</div>
|
||||
</Overlay>
|
||||
|
||||
|
||||
</form>
|
||||
|
||||
<Overlay bind:hidden={loginOverlayHidden}>
|
||||
<div class="bg-white w-full max-w-screen-sm py-8">
|
||||
<EmbeddedAuthFlowModule onLogin={loginAction} email={email} route="signup" title={
|
||||
{
|
||||
login: "Ausweis bestellen",
|
||||
signup: "Ausweis bestellen"
|
||||
}
|
||||
} buttonText={{
|
||||
login: "Bestellen",
|
||||
signup: "Bestellen"
|
||||
}}></EmbeddedAuthFlowModule>
|
||||
</div>
|
||||
</Overlay>
|
||||
|
||||
<NotificationWrapper></NotificationWrapper>
|
||||
|
||||
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
<script lang="ts">
|
||||
import { loginClient } from "#lib/login.js";
|
||||
import CrossCircled from "radix-svelte-icons/src/lib/icons/CrossCircled.svelte";
|
||||
import { fade } from "svelte/transition";
|
||||
|
||||
let email: string;
|
||||
let passwort: string;
|
||||
@@ -13,7 +11,8 @@
|
||||
const response = await loginClient(email, passwort);
|
||||
|
||||
if (response === null) {
|
||||
errorHidden = false;
|
||||
error = true
|
||||
errorMessage = "Das hat leider nicht geklappt, haben sie ihr Passwort und ihre Email Adresse richtig eingegeben?"
|
||||
} else {
|
||||
if (redirect) {
|
||||
window.location.href = redirect;
|
||||
@@ -24,7 +23,8 @@
|
||||
}
|
||||
}
|
||||
|
||||
let errorHidden = true;
|
||||
let error = false;
|
||||
let errorMessage = "";
|
||||
</script>
|
||||
|
||||
<div class="mx-auto w-1/3 bg-base-200 p-8 border border-base-300 rounded-lg">
|
||||
@@ -38,7 +38,7 @@
|
||||
placeholder="nutzer@email.com"
|
||||
name="email"
|
||||
bind:value={email}
|
||||
on:focus={() => (errorHidden = true)}
|
||||
on:focus={() => (error = false)}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
@@ -51,20 +51,19 @@
|
||||
placeholder="********"
|
||||
name="passwort"
|
||||
bind:value={passwort}
|
||||
on:focus={() => (errorHidden = true)}
|
||||
on:focus={() => (error = false)}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
{#if !errorHidden}
|
||||
<div role="alert" class="alert alert-error" in:fade out:fade={{delay: 400}}>
|
||||
<CrossCircled size={24} />
|
||||
<span class="font-semibold">Das hat leider nicht geklappt, haben sie ihr Passwort und ihre Email Adresse richtig eingegeben?</span>
|
||||
{#if error}
|
||||
<div class="bg-red-200 p-4 rounded-lg w-full">
|
||||
<p class="text-red-800">{errorMessage}</p>
|
||||
</div>
|
||||
{/if}
|
||||
<button class="button" type="submit">Einloggen</button>
|
||||
<div class="flex-row justify-between" style="margin-top: 10px">
|
||||
<a class="link link-hover" href="/auth/signup{redirect ? `?redirect=${redirect}` : ""}">Registrieren</a>
|
||||
<a class="link link-hover" href="/auth/passwort-vergessen{redirect ? `?redirect=${redirect}` : ""}"
|
||||
<div class="flex flex-row justify-between" style="margin-top: 10px">
|
||||
<a class="link link-hover" href="/auth/signup{redirect ? `?r=${redirect}` : ""}">Registrieren</a>
|
||||
<a class="link link-hover" href="/auth/passwort-vergessen{redirect ? `?r=${redirect}` : ""}"
|
||||
>Passwort Vergessen?</a
|
||||
>
|
||||
</div>
|
||||
|
||||
@@ -1,56 +1,58 @@
|
||||
<script lang="ts">
|
||||
import { addNotification } from "#components/Notifications/shared.js";
|
||||
import { CrossCircled } from "radix-svelte-icons";
|
||||
import { fade } from "svelte/transition";
|
||||
import { api } from "astro-typesafe-api/client";
|
||||
import NotificationProvider from "#components/NotificationProvider/NotificationProvider.svelte";
|
||||
import NotificationWrapper from "#components/Notifications/NotificationWrapper.svelte";
|
||||
import { loginClient } from "#lib/login.js";
|
||||
import PlzSuche from "#components/PlzSuche.svelte";
|
||||
|
||||
let passwort: string;
|
||||
let email: string;
|
||||
let vorname: string;
|
||||
let name: string;
|
||||
let repeatEmail: string;
|
||||
let adresse: string;
|
||||
let plz: string;
|
||||
let ort: string;
|
||||
let empfaenger: string;
|
||||
|
||||
export let redirect: string | null = null;
|
||||
|
||||
async function login(e: SubmitEvent) {
|
||||
e.preventDefault()
|
||||
if (passwort.length < 8) {
|
||||
addNotification({
|
||||
message: "Passwort muss mindestens 6 Zeichen enthalten.",
|
||||
dismissable: true,
|
||||
timeout: 3000,
|
||||
type: "error"
|
||||
})
|
||||
|
||||
if (email !== repeatEmail) {
|
||||
error = true;
|
||||
errorMessage = "Die eingegebenen Email Adressen stimmen nicht überein.";
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const { uid } = await api.user.PUT.fetch({
|
||||
email,
|
||||
passwort,
|
||||
const { id, passwort } = await api.user.PUT.fetch({
|
||||
vorname,
|
||||
name
|
||||
name, email,
|
||||
adresse, plz, ort, empfaenger
|
||||
})
|
||||
|
||||
await loginClient(email, passwort)
|
||||
|
||||
if (redirect) {
|
||||
window.location.href = redirect
|
||||
return
|
||||
}
|
||||
|
||||
window.location.href = "/auth/login";
|
||||
window.location.href = "/dashboard";
|
||||
} catch (e) {
|
||||
errorHidden = false;
|
||||
error = true
|
||||
errorMessage = "Sie besitzen bereits ein Konto bei IBC. Bitte loggen Sie sich mit Ihrem Passwort ein oder vergeben sich über “Passwort vergessen” ein neues."
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
let errorHidden = true;
|
||||
let error = false;
|
||||
let errorMessage = "";
|
||||
</script>
|
||||
|
||||
<div class="mx-auto w-1/3 bg-base-200 p-8 border border-base-300 rounded-lg">
|
||||
<h1 class="text-3xl mb-4">Registrieren</h1>
|
||||
<form class="flex flex-col gap-4" on:submit={login}>
|
||||
<p>Bitte geben sie Email, Ansprechpartner und Adressdaten ein, um einen Account beim IBC zu erstellen, ein Passwort wird ihnen per Email zugesendet, dieses können sie im Nachhinein jederzeit ändern.</p>
|
||||
<form class="flex flex-col gap-4 mt-8" on:submit={login}>
|
||||
<div class="flex flex-row gap-4 w-full">
|
||||
<div class="w-1/2 flex flex-col gap-2">
|
||||
<h4>Vorname</h4>
|
||||
@@ -75,43 +77,82 @@
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-row gap-4 w-full">
|
||||
<div class="w-1/2 flex flex-col gap-2">
|
||||
<h4>Straße</h4>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Straße"
|
||||
name="strasse"
|
||||
class="input input-bordered text-base text-base-content font-medium"
|
||||
bind:value={adresse}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div class="w-1/2 flex flex-col gap-2">
|
||||
<h4>PLZ</h4>
|
||||
<PlzSuche bind:zip={plz} bind:city={ort} name="plz" readonly={false} />
|
||||
</div>
|
||||
<div class="w-1/2 flex flex-col gap-2">
|
||||
<h4>Ort</h4>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Ort"
|
||||
name="ort"
|
||||
class="input input-bordered text-base text-base-content font-medium"
|
||||
bind:value={ort}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="flex flex-col gap-2">
|
||||
<h4>Empfänger</h4>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Max Mustermann"
|
||||
name="empfaenger"
|
||||
class="input input-bordered text-base text-base-content font-medium"
|
||||
bind:value={empfaenger}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div class="flex flex-col gap-2">
|
||||
<h4>Email</h4>
|
||||
<input
|
||||
type="text"
|
||||
placeholder="Email"
|
||||
placeholder="max.mustermann@email.de"
|
||||
name="email"
|
||||
class="input input-bordered text-base text-base-content font-medium"
|
||||
bind:value={email}
|
||||
on:keyup={() => (email = email.toLowerCase())}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div class="flex flex-col gap-2">
|
||||
<h4>Passwort</h4>
|
||||
<h4>Email erneut eingeben</h4>
|
||||
<input
|
||||
type="password"
|
||||
placeholder="********"
|
||||
minlength="8"
|
||||
name="passwort"
|
||||
type="text"
|
||||
placeholder="max.mustermann@email.de"
|
||||
name="email"
|
||||
class="input input-bordered text-base text-base-content font-medium"
|
||||
bind:value={passwort}
|
||||
bind:value={repeatEmail}
|
||||
on:keyup={() => (repeatEmail = repeatEmail.toLowerCase())}
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
{#if !errorHidden}
|
||||
<div role="alert" class="alert alert-error" in:fade out:fade={{delay: 400}}>
|
||||
<CrossCircled size={24} />
|
||||
<span class="font-semibold">Da ist wohl etwas schiefgelaufen. Diese Email Adresse ist bereits in Benutzung, haben sie vielleicht bereits ein Konto bei uns?</span>
|
||||
{#if error}
|
||||
<div class="bg-red-200 p-4 rounded-lg w-full">
|
||||
<p class="text-red-800">{errorMessage}</p>
|
||||
</div>
|
||||
{/if}
|
||||
<button type="submit" class="button"
|
||||
>Registrieren</button
|
||||
>
|
||||
<div class="flex-row justify-between" style="margin-top: 10px">
|
||||
>Registrieren</button>
|
||||
<div class="flex flex-row justify-between" style="margin-top: 10px">
|
||||
<a class="link link-hover"
|
||||
href="/auth/login{redirect ? `?redirect=${redirect}` : ""}">Einloggen</a
|
||||
href="/auth/login{redirect ? `?r=${redirect}` : ""}">Einloggen</a
|
||||
>
|
||||
<a class="link link-hover" href="/auth/passwort-vergessen{redirect ? `?redirect=${redirect}` : ""}">Passwort Vergessen?</a>
|
||||
<a class="link link-hover" href="/auth/passwort-vergessen{redirect ? `?r=${redirect}` : ""}">Passwort Vergessen?</a>
|
||||
</div>
|
||||
</form>
|
||||
<NotificationWrapper></NotificationWrapper>
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
---
|
||||
import AusweisLayout from "#layouts/AusweisLayoutDatenPartner.astro";
|
||||
import VerbrauchsausweisWohnenModule from "#modules/VerbrauchsausweisWohnen/VerbrauchsausweisWohnenModule.svelte";
|
||||
import { AufnahmeClient, BildClient, ObjektClient, UploadedGebaeudeBild, VerbrauchsausweisWohnenClient } from "#components/Ausweis/types";
|
||||
import { Aufnahme, BedarfsausweisGewerbe, Bild, Enums, Objekt, Unterlage, VerbrauchsausweisWohnen } from "#lib/server/prisma";
|
||||
import { getAufnahme, getBedarfsausweisGewerbe, getBilder, getObjekt, getUnterlagen, getVerbrauchsausweisWohnen } from "#lib/server/db";
|
||||
import { getCurrentUser } from "#lib/server/user";
|
||||
@@ -9,7 +7,7 @@ import BedarfsausweisGewerbeModule from "#modules/angebot-anfragen/Bedarfsauswei
|
||||
|
||||
const id = Astro.url.searchParams.get("id");
|
||||
const aufnahme_id = Astro.url.searchParams.get("aufnahme")
|
||||
let nachweistyp = Astro.url.searchParams.get("nachweistyp") as Enums.AusweisTyp || Enums.AusweisTyp.Standard;
|
||||
let nachweistyp = Astro.url.searchParams.get("ausweistyp") as Enums.AusweisTyp || Enums.AusweisTyp.Standard;
|
||||
|
||||
let nachweis: BedarfsausweisGewerbe = {} as BedarfsausweisGewerbe;
|
||||
let aufnahme: Aufnahme = {} as Aufnahme;
|
||||
@@ -22,7 +20,7 @@ const user = await getCurrentUser(Astro)
|
||||
if (id) {
|
||||
if (!user) {
|
||||
return Astro.redirect(
|
||||
`/auth/login?redirect=${Astro.url.toString()}`
|
||||
`/auth/login?r=${Astro.url.toString()}`
|
||||
);
|
||||
}
|
||||
|
||||
@@ -59,7 +57,7 @@ if (id) {
|
||||
} else if (aufnahme_id) {
|
||||
if (!user) {
|
||||
return Astro.redirect(
|
||||
`/auth/login?redirect=${Astro.url.toString()}`
|
||||
`/auth/login?r=${Astro.url.toString()}`
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ import GEGNachweisGewerbeModule from "#modules/angebot-anfragen/GEGNachweisGewer
|
||||
|
||||
const id = Astro.url.searchParams.get("id");
|
||||
const aufnahme_id = Astro.url.searchParams.get("aufnahme")
|
||||
let nachweistyp = Astro.url.searchParams.get("nachweistyp") as Enums.AusweisTyp || Enums.AusweisTyp.Standard;
|
||||
let nachweistyp = Astro.url.searchParams.get("ausweistyp") as Enums.AusweisTyp || Enums.AusweisTyp.Standard;
|
||||
|
||||
let nachweis: GEGNachweisGewerbe = {} as GEGNachweisGewerbe;
|
||||
let aufnahme: Aufnahme = {} as Aufnahme;
|
||||
@@ -20,7 +20,7 @@ const user = await getCurrentUser(Astro)
|
||||
if (id) {
|
||||
if (!user) {
|
||||
return Astro.redirect(
|
||||
`/auth/login?redirect=${Astro.url.toString()}`
|
||||
`/auth/login?r=${Astro.url.toString()}`
|
||||
);
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ if (id) {
|
||||
} else if (aufnahme_id) {
|
||||
if (!user) {
|
||||
return Astro.redirect(
|
||||
`/auth/login?redirect=${Astro.url.toString()}`
|
||||
`/auth/login?r=${Astro.url.toString()}`
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ const user = await getCurrentUser(Astro)
|
||||
if (id) {
|
||||
if (!user) {
|
||||
return Astro.redirect(
|
||||
`/auth/login?redirect=${Astro.url.toString()}`
|
||||
`/auth/login?r=${Astro.url.toString()}`
|
||||
);
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ if (id) {
|
||||
} else if (aufnahme_id) {
|
||||
if (!user) {
|
||||
return Astro.redirect(
|
||||
`/auth/login?redirect=${Astro.url.toString()}`
|
||||
`/auth/login?r=${Astro.url.toString()}`
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -57,7 +57,7 @@ let loadFromDatabase = false;
|
||||
|
||||
if (typ === AusstellungsTyp.Neuausstellung) {
|
||||
if (!user) {
|
||||
return Astro.redirect(`/auth/login?redirect=${Astro.url.toString()}`);
|
||||
return Astro.redirect(`/auth/login?r=${Astro.url.toString()}`);
|
||||
}
|
||||
|
||||
if (!ausweis_id) {
|
||||
@@ -115,7 +115,7 @@ if (typ === AusstellungsTyp.Neuausstellung) {
|
||||
loadFromDatabase = true;
|
||||
} else if (typ === AusstellungsTyp.Speichern) {
|
||||
if (!user) {
|
||||
return Astro.redirect(`/auth/login?redirect=${Astro.url.toString()}`);
|
||||
return Astro.redirect(`/auth/login?r=${Astro.url.toString()}`);
|
||||
}
|
||||
|
||||
if (!ausweis_id) {
|
||||
@@ -151,7 +151,7 @@ if (typ === AusstellungsTyp.Neuausstellung) {
|
||||
loadFromDatabase = true;
|
||||
} else if (typ === AusstellungsTyp.Alternativdokument) {
|
||||
if (!user) {
|
||||
return Astro.redirect(`/auth/login?redirect=${Astro.url.toString()}`);
|
||||
return Astro.redirect(`/auth/login?r=${Astro.url.toString()}`);
|
||||
}
|
||||
|
||||
if (!ausweis_id) {
|
||||
|
||||
@@ -20,7 +20,7 @@ const user = await getCurrentUser(Astro)
|
||||
if (id) {
|
||||
if (!user) {
|
||||
return Astro.redirect(
|
||||
`/auth/login?redirect=${Astro.url.toString()}`
|
||||
`/auth/login?r=${Astro.url.toString()}`
|
||||
);
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ if (id) {
|
||||
} else if (aufnahme_id) {
|
||||
if (!user) {
|
||||
return Astro.redirect(
|
||||
`/auth/login?redirect=${Astro.url.toString()}`
|
||||
`/auth/login?r=${Astro.url.toString()}`
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ const user = await getCurrentUser(Astro)
|
||||
if (id) {
|
||||
if (!user) {
|
||||
return Astro.redirect(
|
||||
`/auth/login?redirect=${Astro.url.toString()}`
|
||||
`/auth/login?r=${Astro.url.toString()}`
|
||||
);
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ if (id) {
|
||||
} else if (aufnahme_id) {
|
||||
if (!user) {
|
||||
return Astro.redirect(
|
||||
`/auth/login?redirect=${Astro.url.toString()}`
|
||||
`/auth/login?r=${Astro.url.toString()}`
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -21,7 +21,7 @@ const user = await getCurrentUser(Astro)
|
||||
if (id) {
|
||||
if (!user) {
|
||||
return Astro.redirect(
|
||||
`/auth/login?redirect=${Astro.url.toString()}`
|
||||
`/auth/login?r=${Astro.url.toString()}`
|
||||
);
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ if (id) {
|
||||
} else if (aufnahme_id) {
|
||||
if (!user) {
|
||||
return Astro.redirect(
|
||||
`/auth/login?redirect=${Astro.url.toString()}`
|
||||
`/auth/login?r=${Astro.url.toString()}`
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -294,7 +294,7 @@ export const GET = defineApiRoute({
|
||||
|
||||
if (rechnung.status === Enums.Rechnungsstatus.paid) {
|
||||
html = `
|
||||
<p>Sehr geehrte*r ${rechnung.empfaenger},</p>
|
||||
<p>Sehr geehrte/r ${rechnung.empfaenger},</p>
|
||||
|
||||
<p>im Anhang finden Sie Ihren geprüften Energieusweis inkl. Rechnung als PDF-Datei. ${
|
||||
post ? "Zusätzlich haben wir Ihren Ausweis per Post verschickt" : ""
|
||||
@@ -323,7 +323,7 @@ export const GET = defineApiRoute({
|
||||
</p>`;
|
||||
} else {
|
||||
html = `
|
||||
<p>Sehr geehrte*r ${rechnung.empfaenger},</p>
|
||||
<p>Sehr geehrte/r ${rechnung.empfaenger},</p>
|
||||
|
||||
<p>im Anhang finden Sie Ihren geprüften Energieusweis inkl. Rechnung als PDF-Datei. ${
|
||||
post ? "Zusätzlich haben wir Ihren Ausweis per Post verschickt" : ""
|
||||
|
||||
@@ -303,7 +303,7 @@ export const POST = defineApiRoute({
|
||||
|
||||
if (rechnung.status === Enums.Rechnungsstatus.paid) {
|
||||
html = `
|
||||
<p>Sehr geehrte*r ${rechnung.empfaenger},</p>
|
||||
<p>Sehr geehrte/r ${rechnung.empfaenger},</p>
|
||||
|
||||
<p>im Anhang finden Sie Ihren geprüften Energieusweis inkl. Rechnung als PDF-Datei. ${
|
||||
post ? "Zusätzlich haben wir Ihren Ausweis per Post verschickt" : ""
|
||||
@@ -332,7 +332,7 @@ export const POST = defineApiRoute({
|
||||
</p>`;
|
||||
} else {
|
||||
html = `
|
||||
<p>Sehr geehrte*r ${rechnung.empfaenger},</p>
|
||||
<p>Sehr geehrte/r ${rechnung.empfaenger},</p>
|
||||
|
||||
<p>im Anhang finden Sie Ihren geprüften Energieusweis inkl. Rechnung als PDF-Datei. ${
|
||||
post ? "Zusätzlich haben wir Ihren Ausweis per Post verschickt" : ""
|
||||
|
||||
@@ -120,7 +120,7 @@ export const PUT = defineApiRoute({
|
||||
to: rechnung.email || user.email,
|
||||
bcc: "info@online-energieausweis.org",
|
||||
subject: `Stornierung des Energieausweises vom Ingenieurbüro Cornelsen (ID: ${ausweis.id})`,
|
||||
html: `<p>Sehr geehrte*r ${user.vorname} ${user.name},</p>
|
||||
html: `<p>Sehr geehrte/r ${user.vorname} ${user.name},</p>
|
||||
|
||||
<p>Ihr Energieausweis wurde soeben storniert.
|
||||
<br>
|
||||
|
||||
@@ -64,7 +64,7 @@ export const GET = defineApiRoute({
|
||||
const { id } = ctx.params;
|
||||
|
||||
const aufnahme = await prisma.aufnahme.findUnique({
|
||||
where: user.rolle === Enums.BenutzerRolle.USER ? {
|
||||
where: user.rolle !== Enums.BenutzerRolle.ADMIN ? {
|
||||
id,
|
||||
benutzer_id: user.id
|
||||
} : { id },
|
||||
|
||||
@@ -63,7 +63,7 @@ export const GET = defineApiRoute({
|
||||
const { id } = context.params;
|
||||
|
||||
const aufnahme = await prisma.aufnahme.findUnique({
|
||||
where: user.rolle === Enums.BenutzerRolle.USER ? {
|
||||
where: user.rolle !== Enums.BenutzerRolle.ADMIN ? {
|
||||
id,
|
||||
benutzer_id: user.id
|
||||
} : { id },
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user