strip v1
This commit is contained in:
151
.drone.yml.bak
151
.drone.yml.bak
@@ -1,151 +0,0 @@
|
||||
kind: pipeline
|
||||
type: docker
|
||||
name: default
|
||||
|
||||
steps:
|
||||
##############################
|
||||
# Build and deploy docs
|
||||
##############################
|
||||
- name: build docs
|
||||
image: node:18
|
||||
pull: if-not-exists
|
||||
environment:
|
||||
FORCE_COLOR: "true"
|
||||
commands:
|
||||
- cd docs
|
||||
- yarn install
|
||||
- yarn docpress:build
|
||||
when:
|
||||
branch: [master]
|
||||
event: [push]
|
||||
|
||||
- name: deploy docs
|
||||
image: instrumentisto/rsync-ssh
|
||||
pull: if-not-exists
|
||||
environment:
|
||||
RSYNC_HOST: ftp1.webmakers.de
|
||||
RSYNC_PORT: 22222
|
||||
RSYNC_USER: webmakers_tibi_docs_rsync_master
|
||||
RSYNC_PASS:
|
||||
from_secret: rsync_master
|
||||
commands:
|
||||
- cd docs
|
||||
- >
|
||||
rsync -rlcgD --perms -i -u -v --stats --progress
|
||||
--delete
|
||||
-e "sshpass -p $${RSYNC_PASS} ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -p $${RSYNC_PORT}"
|
||||
_docpress/
|
||||
$${RSYNC_USER}@$${RSYNC_HOST}:./
|
||||
when:
|
||||
branch: [master]
|
||||
event: [push]
|
||||
|
||||
##############################
|
||||
# Demo project
|
||||
##############################
|
||||
- name: yarn install
|
||||
image: node:18
|
||||
pull: if-not-exists
|
||||
environment:
|
||||
FORCE_COLOR: "true"
|
||||
commands:
|
||||
- yarn install
|
||||
|
||||
- name: modify config
|
||||
image: alpine/git
|
||||
commands:
|
||||
- sed -i 's#\(sentryEnvironment.*\)".*"#\1"${DRONE_BRANCH}"#g' frontend/src/config.ts
|
||||
- sed -i 's#//\( sentry\\.init.*\)#\1#g' frontend/src/config.ts
|
||||
- export $(cat .env | xargs)
|
||||
- echo "PROJECT_RELEASE=$${RELEASE_PROJECT_SLUG}.r`git rev-list HEAD --count`-`git describe --all --long | sed 's+/+-+'`" >> .env
|
||||
- export $(cat .env | xargs)
|
||||
- cat .env
|
||||
- sed -i 's#\(const release = \).*#\1"'$${PROJECT_RELEASE}'"#g' api/hooks/config-client.js
|
||||
|
||||
- name: build
|
||||
image: node:18
|
||||
commands:
|
||||
- yarn build
|
||||
|
||||
- name: build ssr
|
||||
image: node:18
|
||||
commands:
|
||||
- yarn build:server
|
||||
|
||||
- name: build legacy
|
||||
image: node:18
|
||||
commands:
|
||||
- yarn build:legacy
|
||||
|
||||
- name: modify html
|
||||
image: bash
|
||||
commands:
|
||||
- bash scripts/preload-meta.sh frontend/spa.html
|
||||
- bash scripts/preload-meta.sh frontend/spa.html > frontend/_spa.html
|
||||
- cp frontend/_spa.html frontend/spa.html
|
||||
- export stamp=`date +%s`
|
||||
- echo $$stamp
|
||||
- sed -i s/__TIMESTAMP__/$$stamp/g frontend/spa.html
|
||||
#- sed -i s/__TIMESTAMP__/$$stamp/g frontend/serviceworker.js
|
||||
#- cat frontend/serviceworker.js
|
||||
- cp frontend/spa.html api/templates/spa.html
|
||||
- cat frontend/spa.html
|
||||
|
||||
# staging
|
||||
- name: copy api config to staging
|
||||
image: instrumentisto/rsync-ssh
|
||||
volumes:
|
||||
- name: data
|
||||
path: /data
|
||||
commands:
|
||||
- rsync -av api /data/
|
||||
- mkdir -p /data/frontend/dist
|
||||
- rsync -av frontend/dist/ /data/frontend/dist/
|
||||
when:
|
||||
branch: [dev]
|
||||
event: [push]
|
||||
|
||||
- name: review in staging
|
||||
image: docker/compose:1.22.0
|
||||
commands:
|
||||
- docker-compose -p ${DRONE_BRANCH}-${DRONE_REPO_NAME}-${DRONE_REPO_OWNER} up -d --build --remove-orphans
|
||||
volumes:
|
||||
- name: docker
|
||||
path: /var/run/docker.sock
|
||||
when:
|
||||
branch: [dev]
|
||||
event: [push]
|
||||
|
||||
# live
|
||||
- name: deploy master
|
||||
image: instrumentisto/rsync-ssh
|
||||
environment:
|
||||
RSYNC_USER: ""
|
||||
RSYNC_PASS:
|
||||
from_secret: rsync_master
|
||||
# remove if user and pass is set
|
||||
failure: ignore
|
||||
commands:
|
||||
- apk add --no-cache sshpass curl
|
||||
- scripts/deploy.sh ftp1.webmakers.de $${RSYNC_USER} $${RSYNC_PASS}
|
||||
# - curl -X POST "https://www....de/api/ssr?token=TowendQhi&clear=1"
|
||||
when:
|
||||
branch: [master]
|
||||
event: [push]
|
||||
|
||||
# - name: upload sourcemaps for glitchtip
|
||||
# image: node
|
||||
# environment:
|
||||
# GLITCHTIP_TOKEN:
|
||||
# from_secret: glitchtip_token
|
||||
# commands:
|
||||
# - yarn upload:sourcemaps
|
||||
|
||||
########
|
||||
volumes:
|
||||
- name: data
|
||||
host:
|
||||
path: /data/${DRONE_REPO_OWNER}/${DRONE_REPO_NAME}/${DRONE_BRANCH}
|
||||
- name: docker
|
||||
host:
|
||||
path: /var/run/docker.sock
|
||||
10
.env
10
.env
@@ -1,14 +1,14 @@
|
||||
PROJECT_NAME=bkdf-tibi-2024
|
||||
TIBI_PREFIX=bkdf_tibi_2024
|
||||
TIBI_NAMESPACE=bkdf_tibi_2024
|
||||
PROJECT_NAME=kontextwerk
|
||||
TIBI_PREFIX=kontextwerk
|
||||
TIBI_NAMESPACE=kontextwerk
|
||||
CODER_UID=100
|
||||
CODER_GID=101
|
||||
START_SCRIPT=:ssr
|
||||
RSYNC_HOST=ftp1.webmakers.de
|
||||
RSYNC_PORT=22222
|
||||
RSYNC_USER=BinKrassDuFass_rsync_master
|
||||
LIVE_URL=https://www.binkrassdufass.de
|
||||
LIVE_URL=https://www.kontextwerk.de
|
||||
SENTRY_URL=https://sentry.basehosts.de
|
||||
SENTRY_ORG=webmakers
|
||||
SENTRY_PROJECT=binkrassdufass-tibi-2024
|
||||
SENTRY_PROJECT=kontextwerk-tibi-2024
|
||||
|
||||
|
||||
126
README.md
126
README.md
@@ -1,126 +0,0 @@
|
||||
# Tibi Docs und Demo Projekt
|
||||
|
||||
Diese Repo enthält die Dokumentation zum TibiCMS und eine Demo-Projekt welches die Dokumentation begleitet und als Projetstarter fungiert.
|
||||
|
||||
## neues Projekt - Checkliste
|
||||
|
||||
- [x] neues Projekt im gitbase.de anlegen (cms/tibi_starter) als Vorlage verwenden
|
||||
- [ ] klonen
|
||||
- [ ] bereinigen
|
||||
|
||||
```sh
|
||||
git filter-branch -f --index-filter 'git rm -rf --cached --ignore-unmatch .yarn/cache' HEAD
|
||||
git filter-branch -f --index-filter 'git rm -rf --cached --ignore-unmatch docs' HEAD
|
||||
git push --force
|
||||
```
|
||||
|
||||
- [ ] anpassen
|
||||
|
||||
- `.env`
|
||||
- `docker-compose-local.yml` -> `TIBI_API_NAMESPACE: & PROJECT_NAME`
|
||||
- `.gitea/workflows/deploy.yaml` -> `name:`
|
||||
- `./api/hooks/config.js` -> `name:`
|
||||
- `api/...`
|
||||
|
||||
- [ ] upgraden
|
||||
|
||||
```sh
|
||||
mkdir tmp
|
||||
# evtl. zuvor: yarn install
|
||||
make yarn-upgrade
|
||||
make docker-pull
|
||||
|
||||
# falls Fehler auftreten, evtl. Berechtigungen fixen
|
||||
make fix-permissions
|
||||
```
|
||||
|
||||
- [ ] los programmieren
|
||||
|
||||
```sh
|
||||
make docker-start
|
||||
|
||||
# bei erstem fehlerhaften Start, evtl. Berechtigungen fixen:
|
||||
make fix-permissions
|
||||
```
|
||||
|
||||
- [ ] Projekt in Tibi bekannt machen:
|
||||
- <https://PROJEKTNAME-tibiadmin.code.testversion.online>
|
||||
- Pfad der API-Konfig: `/data/api/config.yml`
|
||||
- [ ] Website im Browser ansehen:
|
||||
- <https://PROJEKTNAME.code.testversion.online>
|
||||
- [ ] Testmails checken:
|
||||
|
||||
- <https://PROJEKTNAME-maildev.testversion.online>
|
||||
|
||||
- [ ] deploy
|
||||
1. [ ] Subdomain im basispanel anlegen auf `../frontend/`
|
||||
2. [ ] rsync-Account in basispanel anlegen auf `htdocs/`
|
||||
3. [ ] Passwort in Secrets eintragen: <https://drone.gitbase.de>
|
||||
4. [ ] `deploy.yaml` deploy rsync user anpassen
|
||||
5. [ ] pushen
|
||||
|
||||
## SSR Aktivierung
|
||||
|
||||
Per se ist alles vorbereitet damit SSR aktiviert werden kann, hier muss nur START_SCRIPT=:ssr in der .env datei einkommentiert werden.
|
||||
Es ist jedoch anzunehmen, dass während der entwicklung Dinge eingebaut wurden, dass die kompilierung schief geht. Um dies zu kontrollieren, muss man zunächst prüfen, ob Einträge in der SSR collection vorhanden sind. Gibt es keine Einträge, sollte man in die "/" Anfrage in den Netzwerkeinstellungen schauen. Hier ist in der Server Antwort im HTML template ein fehler zum debuggen zu finden!
|
||||
|
||||
# Git prozess - WICHTIG!
|
||||
|
||||
Der git Ablauf wurde mittels eines pre push Hooks modifiziert. Hier wird der aktuelle gepushte commit modifiziert, indem ein mongodump der aktuellen datenbank vor genommen wird und die kopie in ".gitea/actions/init-db/mongo-dump" abgespeichert wird.
|
||||
Der pre-push hook liegt in .git und sieht wie folgt aus:
|
||||
|
||||
```sh
|
||||
#!/bin/bash
|
||||
|
||||
ENV_FILE="$(git rev-parse --show-toplevel)/.env"
|
||||
echo "PRE-PUSH HOOK: Running mongodump and adding it to the commit..."
|
||||
# Check if the .env file exists
|
||||
if [ ! -f "$ENV_FILE" ]; then
|
||||
echo ".env file not found. Push cancelled."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Source the .env file
|
||||
source "$ENV_FILE"
|
||||
|
||||
# Name of the Docker container running MongoDB
|
||||
CONTAINER_NAME="${PROJECT_NAME}-mongo-1"
|
||||
|
||||
# Directory to store the mongodump
|
||||
OUTPUT_DIR=".gitea/actions/init-db/mongo-dump"
|
||||
DUMP_DIR_NAME="tmp/mongo-dump"
|
||||
|
||||
# Message to append to the commit
|
||||
APPEND_MSG="made a mongodump on current local database for tests to have data"
|
||||
|
||||
# Check if the container is running
|
||||
if ! docker ps | grep -q "$CONTAINER_NAME"; then
|
||||
echo "MongoDB container '$CONTAINER_NAME' is not running. Push cancelled."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Perform mongodump
|
||||
docker exec "$CONTAINER_NAME" mongodump --out /$DUMP_DIR_NAME && \
|
||||
docker cp "$CONTAINER_NAME":/$DUMP_DIR_NAME "$OUTPUT_DIR"
|
||||
|
||||
# Check for successful mongodump
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Mongodump failed. Push cancelled. Make sure that the mongo container is up and running, necessary for the Workflow, because it needs a seeded database, wich needs a mongodump beforehand."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Add the output to the current commit
|
||||
git add "$OUTPUT_DIR"
|
||||
|
||||
# Amend the commit with a modified message
|
||||
CURRENT_MSG=$(git log -1 --pretty=%B)
|
||||
NEW_MSG="$CURRENT_MSG $APPEND_MSG"
|
||||
git commit -m "$NEW_MSG"
|
||||
|
||||
# Continue with the push
|
||||
exit 0
|
||||
```
|
||||
|
||||
Diese Kopie wird gebraucht, um im Workflow unsere Lokale entwicklungsumgebung zu booten. Dieser workflow ist unter dem Namen lighthouse-evaluation in der deploy.yaml Datei zu finden. Es ist anzumerken, dass der Code funktioniert, er wurde in github getestet, jedoch in gitea das feature der Services noch nicht funktionsfähig ist, da Stand Februar 2024 das noch nicht unterstützt wird! Es wird jedoch sehr hilfreich sein, da
|
||||
a) bei jedem Push eine lighthouse analyse durchgeführt wird und das Ergebnis davon in nextcloud hochgeladen wird. Bei bedarf kann natürlich auch die Funktion hinzugefügt werden, dass das Ergebnis direkt an seine Email gesandt wird oder ähnliches.
|
||||
b) Man kann die Umgebung auch für Test zwecke verwenden. Wenn man also bspw. Cypress einbaut, so hat man direkt eine Umgebung um dies laufen zu lassen.
|
||||
@@ -1,253 +0,0 @@
|
||||
name: selfImprovementChallenge
|
||||
|
||||
meta:
|
||||
allowExportAll: true
|
||||
label:
|
||||
de: SelfImp. Challenge
|
||||
en: SelfImp. Challenge
|
||||
muiIcon: label
|
||||
backup:
|
||||
active: true
|
||||
collectionName: backups
|
||||
defaultSort:
|
||||
field: name
|
||||
order: ASC
|
||||
|
||||
views: &views
|
||||
- type: table
|
||||
columns:
|
||||
- source: title
|
||||
label:
|
||||
de: Titel
|
||||
en: Title
|
||||
filter: true
|
||||
|
||||
- source: type
|
||||
label:
|
||||
de: Typ
|
||||
en: Type
|
||||
filter: true
|
||||
|
||||
- source: slug
|
||||
label:
|
||||
de: Slug
|
||||
en: Slug
|
||||
filter: true
|
||||
tablist:
|
||||
activeTab: generalDetails
|
||||
tabs:
|
||||
- name: generalDetails
|
||||
label:
|
||||
de: Allgemeine Details
|
||||
en: General Details
|
||||
subFields:
|
||||
- source: activeAt
|
||||
- source: type
|
||||
- source: title
|
||||
- source: images
|
||||
- source: slug
|
||||
- name: introduction
|
||||
label:
|
||||
de: Kurzbeschreibung
|
||||
en: Short Description
|
||||
subFields:
|
||||
- source: introduction
|
||||
- source: howItWorks
|
||||
- source: blog
|
||||
|
||||
subNavigation:
|
||||
- name: modalForeign
|
||||
defaultSort:
|
||||
field: name
|
||||
order: ASC
|
||||
views: *views
|
||||
|
||||
defaultCallback:
|
||||
eval: |
|
||||
(entry) => {
|
||||
parent.selectEntry(entry)
|
||||
}
|
||||
|
||||
permissions:
|
||||
public:
|
||||
methods:
|
||||
get: true
|
||||
post: false
|
||||
put: false
|
||||
delete: false
|
||||
user:
|
||||
methods:
|
||||
get: true
|
||||
post: true
|
||||
put: true
|
||||
delete: true
|
||||
|
||||
fields:
|
||||
- name: slug
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: Slug
|
||||
en: Slug
|
||||
containerProps:
|
||||
layout:
|
||||
size:
|
||||
default: "col-6"
|
||||
small: "col-12"
|
||||
large: "col-6"
|
||||
- name: activeAt
|
||||
type: date
|
||||
meta:
|
||||
label:
|
||||
de: Aktiv ab
|
||||
en: Active at
|
||||
widget: date
|
||||
containerProps:
|
||||
layout:
|
||||
size:
|
||||
default: "col-6"
|
||||
small: "col-12"
|
||||
large: "col-6"
|
||||
- name: type
|
||||
type: number
|
||||
meta:
|
||||
label:
|
||||
de: Typ
|
||||
en: Type
|
||||
widget: select
|
||||
choices:
|
||||
- name: Krass Kraft
|
||||
id: 1
|
||||
- name: Crazy Calm
|
||||
id: 2
|
||||
- name: Crazy Crave Control
|
||||
id: 3
|
||||
- name: Krass Kreativ
|
||||
id: 4
|
||||
containerProps:
|
||||
layout:
|
||||
size:
|
||||
default: "col-6"
|
||||
small: "col-12"
|
||||
large: "col-6"
|
||||
- name: images
|
||||
type: object
|
||||
meta:
|
||||
label:
|
||||
de: Bilder
|
||||
en: Images
|
||||
subFields:
|
||||
- name: preview
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: Mobile
|
||||
en: Mobile
|
||||
widget: foreignKey
|
||||
foreign:
|
||||
collection: medialib
|
||||
id: id
|
||||
subNavigation: 0
|
||||
render:
|
||||
defaultCollectionViews: true
|
||||
- name: detailed
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: Desktop
|
||||
en: Desktop
|
||||
widget: foreignKey
|
||||
foreign:
|
||||
collection: medialib
|
||||
id: id
|
||||
subNavigation: 0
|
||||
render:
|
||||
defaultCollectionViews: true
|
||||
|
||||
- name: title
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: Titel
|
||||
en: Title
|
||||
|
||||
- name: introduction
|
||||
type: string[]
|
||||
meta:
|
||||
label:
|
||||
de: Kurzbeschreibung
|
||||
en: Short Description
|
||||
useDefaultArray: true
|
||||
widget: richtext
|
||||
|
||||
- name: howItWorks
|
||||
type: object
|
||||
meta:
|
||||
label:
|
||||
de: Wie es funktioniert
|
||||
en: How it works
|
||||
subFields:
|
||||
- name: invitation
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: Einladung
|
||||
en: Invitation
|
||||
|
||||
- !include fields/contentBlocks/stepNr.yml
|
||||
|
||||
- name: blog
|
||||
type: object
|
||||
meta:
|
||||
label:
|
||||
de: Blog
|
||||
en: Blog
|
||||
subFields:
|
||||
- name: blogId
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: Blog ID
|
||||
en: Blog ID
|
||||
widget: foreignKey
|
||||
foreign:
|
||||
collection: content
|
||||
id: id
|
||||
subNavigation: 0
|
||||
render:
|
||||
defaultCollectionViews: true
|
||||
|
||||
- name: thumbnail
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: Thumbnail
|
||||
en: Thumbnail
|
||||
widget: foreignKey
|
||||
foreign:
|
||||
collection: medialib
|
||||
id: id
|
||||
subNavigation: 0
|
||||
render:
|
||||
defaultCollectionViews: true
|
||||
|
||||
- name: sources
|
||||
type: object[]
|
||||
meta:
|
||||
label:
|
||||
de: Quellen
|
||||
en: Sources
|
||||
subFields:
|
||||
- name: source
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: Quelle
|
||||
en: Source
|
||||
|
||||
- name: url
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: URL
|
||||
en: URL
|
||||
@@ -1,360 +0,0 @@
|
||||
name: orderReturnRequest
|
||||
|
||||
meta:
|
||||
allowExportAll: true
|
||||
label:
|
||||
de: Retourenanfrage
|
||||
en: Order Return Request
|
||||
muiIcon: label
|
||||
backup:
|
||||
active: true
|
||||
collectionName: backups
|
||||
defaultSort:
|
||||
field: insertTime
|
||||
order: DESC
|
||||
tablist:
|
||||
activeTab: generalDetails
|
||||
tabs:
|
||||
- name: generalDetails
|
||||
label:
|
||||
de: Allgemeine Details
|
||||
en: General Details
|
||||
subFields:
|
||||
- source: status
|
||||
- source: notes
|
||||
- name: customerDetails
|
||||
label:
|
||||
de: Kunden Details
|
||||
en: Customer Details
|
||||
subFields:
|
||||
- source: email
|
||||
- source: bigCommerceId
|
||||
- name: products
|
||||
label:
|
||||
de: Produkte
|
||||
en: Products
|
||||
subFields:
|
||||
- source: products
|
||||
- name: returnShppingLabels
|
||||
label:
|
||||
de: Retourenlabels
|
||||
en: Return Shipping Labels
|
||||
subFields:
|
||||
- source: returnShppingLabels
|
||||
views: &views
|
||||
- type: table
|
||||
columns:
|
||||
- source: status
|
||||
filter: true
|
||||
- source: email
|
||||
filter: true
|
||||
- source: bigCommerceId
|
||||
filter:
|
||||
type: foreignKey
|
||||
- source: insertTime
|
||||
filter: true
|
||||
subNavigation:
|
||||
- name: modalForeign
|
||||
defaultSort:
|
||||
field: "path"
|
||||
order: "ASC"
|
||||
views: *views
|
||||
defaultCallback:
|
||||
eval: |
|
||||
//js
|
||||
(entry) => {
|
||||
parent.selectEntry(entry)
|
||||
}
|
||||
//!js
|
||||
|
||||
- name: new
|
||||
label:
|
||||
de: Neu
|
||||
en: New
|
||||
muiIcon: new
|
||||
defaultSort:
|
||||
field: insertTime
|
||||
order: DESC
|
||||
views: *views
|
||||
filter:
|
||||
status: pending
|
||||
|
||||
- name: inProgress
|
||||
label:
|
||||
de: In Bearbeitung
|
||||
en: In Progress
|
||||
muiIcon: inProgress
|
||||
defaultSort:
|
||||
field: insertTime
|
||||
order: DESC
|
||||
views: *views
|
||||
filter:
|
||||
status: approved
|
||||
|
||||
- name: done
|
||||
label:
|
||||
de: Abgeschlossen
|
||||
en: Done
|
||||
muiIcon: done
|
||||
defaultSort:
|
||||
field: insertTime
|
||||
order: DESC
|
||||
views: *views
|
||||
filter:
|
||||
status: refunded
|
||||
- name: failed
|
||||
label:
|
||||
de: Fehlgeschlagen
|
||||
en: Failed
|
||||
muiIcon: error
|
||||
defaultSort:
|
||||
field: insertTime
|
||||
order: DESC
|
||||
views: *views
|
||||
filter:
|
||||
status: failed
|
||||
- name: rejected
|
||||
label:
|
||||
de: Abgelehnt
|
||||
en: Rejected
|
||||
muiIcon: error
|
||||
defaultSort:
|
||||
field: insertTime
|
||||
order: DESC
|
||||
views: *views
|
||||
filter:
|
||||
status: rejected
|
||||
|
||||
permissions:
|
||||
public:
|
||||
methods:
|
||||
get: true
|
||||
post: true
|
||||
put: false
|
||||
delete: true
|
||||
user:
|
||||
methods:
|
||||
get: true
|
||||
post: true
|
||||
put: true
|
||||
delete: true
|
||||
|
||||
hooks:
|
||||
get:
|
||||
read:
|
||||
type: javascript
|
||||
file: hooks/orderReturnRequest/get_read.js
|
||||
post:
|
||||
create:
|
||||
type: javascript
|
||||
file: hooks/orderReturnRequest/post_create.js
|
||||
return:
|
||||
type: javascript
|
||||
file: hooks/orderReturnRequest/post_return.js
|
||||
put:
|
||||
update:
|
||||
type: javascript
|
||||
file: hooks/orderReturnRequest/put_update.js
|
||||
|
||||
delete:
|
||||
delete:
|
||||
type: javascript
|
||||
file: hooks/orderReturnRequest/delete_delete.js
|
||||
return:
|
||||
type: javascript
|
||||
file: hooks/orderReturnRequest/delete_return.js
|
||||
|
||||
fields:
|
||||
- name: status
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: Status
|
||||
en: Status
|
||||
widget: select
|
||||
choices:
|
||||
- name: pending
|
||||
id: pending
|
||||
|
||||
- name: approved
|
||||
id: approved
|
||||
|
||||
- name: rejected
|
||||
id: rejected
|
||||
|
||||
- name: refunded
|
||||
id: refunded
|
||||
|
||||
- name: failed
|
||||
id: failed
|
||||
|
||||
- name: email
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: E-Mail
|
||||
en: E-Mail
|
||||
|
||||
- name: bigCommerceId
|
||||
type: number
|
||||
meta:
|
||||
label:
|
||||
de: Bestellnummer
|
||||
en: Order ID
|
||||
widget: foreignKey
|
||||
filter:
|
||||
type: foreignKey
|
||||
foreign:
|
||||
collection: bigCommerceOrder
|
||||
id: bigCommerceId
|
||||
subNavigation: 0
|
||||
render:
|
||||
defaultCollectionViews: true
|
||||
|
||||
- name: returnShppingLabels
|
||||
type: object[]
|
||||
meta:
|
||||
label:
|
||||
de: Retourenlabels
|
||||
en: Return Shipping Labels
|
||||
subFields:
|
||||
- name: cost
|
||||
type: number
|
||||
meta:
|
||||
label:
|
||||
de: Kosten
|
||||
en: Cost
|
||||
- name: label
|
||||
type: file
|
||||
meta:
|
||||
label:
|
||||
de: Label
|
||||
en: Label
|
||||
widget: file
|
||||
|
||||
- name: products
|
||||
type: object[]
|
||||
meta:
|
||||
widget: containerLessObjectArray
|
||||
label:
|
||||
de: Produkte
|
||||
en: Products
|
||||
subFields:
|
||||
- name: baseProductId
|
||||
type: number
|
||||
meta:
|
||||
label:
|
||||
de: Produkt ID
|
||||
en: Product ID
|
||||
widget: foreignKey
|
||||
foreign:
|
||||
collection: bigCommerceProduct
|
||||
id: bigCommerceId
|
||||
subNavigation: 0
|
||||
render:
|
||||
defaultCollectionViews: true
|
||||
containerProps:
|
||||
layout:
|
||||
size:
|
||||
default: "col-6"
|
||||
small: "col-12"
|
||||
large: "col-6"
|
||||
- name: attachedImages
|
||||
type: string[]
|
||||
meta:
|
||||
label:
|
||||
de: Angehängte Bilder
|
||||
en: Attached Images
|
||||
widget: foreignKey
|
||||
containerProps:
|
||||
layout:
|
||||
size:
|
||||
default: "col-6"
|
||||
small: "col-12"
|
||||
large: "col-6"
|
||||
foreign:
|
||||
collection: medialib
|
||||
id: id
|
||||
subNavigation: 0
|
||||
render:
|
||||
defaultCollectionViews: true
|
||||
filter:
|
||||
type: foreignKey
|
||||
|
||||
- name: quantity
|
||||
type: number
|
||||
meta:
|
||||
label:
|
||||
de: Menge
|
||||
en: Quantity
|
||||
containerProps:
|
||||
layout:
|
||||
size:
|
||||
default: "col-6"
|
||||
small: "col-12"
|
||||
large: "col-4"
|
||||
- name: productId
|
||||
type: number
|
||||
meta:
|
||||
label:
|
||||
de: Produkt ID in Bestellung
|
||||
en: Product ID in Order
|
||||
containerProps:
|
||||
layout:
|
||||
size:
|
||||
default: "col-6"
|
||||
small: "col-12"
|
||||
large: "col-4"
|
||||
|
||||
- name: returnReason
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: Retourengrund
|
||||
en: Return Reason
|
||||
containerProps:
|
||||
layout:
|
||||
size:
|
||||
default: "col-6"
|
||||
small: "col-12"
|
||||
large: "col-4"
|
||||
|
||||
widget: select
|
||||
choices:
|
||||
- name: falsche Größe
|
||||
id: wrongSize
|
||||
- name: falsche Farbe
|
||||
id: wrongColor
|
||||
- name: falsches Produkt
|
||||
id: wrongProduct
|
||||
- name: beschädigt
|
||||
id: damaged
|
||||
- name: zu Groß
|
||||
id: tooLarge
|
||||
- name: zu Klein
|
||||
id: tooSmall
|
||||
- name: nicht Wie Beschrieben
|
||||
id: notAsDescribed
|
||||
- name: schlechte Qualität
|
||||
id: poorQuality
|
||||
- name: sieht Anders Aus
|
||||
id: looksDifferent
|
||||
- name: nicht Mehr Benötigt
|
||||
id: noLongerNeeded
|
||||
- name: schlechtes Preis Leistungs Verhältnis
|
||||
id: poorValue
|
||||
- name: andere
|
||||
id: other
|
||||
- name: notes
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: Notizen
|
||||
en: Notes
|
||||
widget: textarea
|
||||
containerProps:
|
||||
layout:
|
||||
size:
|
||||
default: "col-12"
|
||||
small: "col-12"
|
||||
large: "col-12"
|
||||
@@ -1,183 +0,0 @@
|
||||
name: orderRevokeRequest
|
||||
|
||||
meta:
|
||||
allowExportAll: true
|
||||
label:
|
||||
de: Bestellung Abbruch
|
||||
en: Revoke Order
|
||||
muiIcon: label
|
||||
backup:
|
||||
active: true
|
||||
collectionName: backups
|
||||
defaultSort:
|
||||
field: name
|
||||
order: ASC
|
||||
|
||||
views: &views
|
||||
- type: table
|
||||
columns:
|
||||
- source: status
|
||||
filter: true
|
||||
- source: email
|
||||
filter: true
|
||||
- source: bigCommerceId
|
||||
filter:
|
||||
type: foreignKey
|
||||
- source: insertTime
|
||||
filter: true
|
||||
tablist:
|
||||
activeTab: generalDetails
|
||||
tabs:
|
||||
- name: generalDetails
|
||||
label:
|
||||
de: Allgemeine Details
|
||||
en: General Details
|
||||
subFields:
|
||||
- source: email
|
||||
- source: bigCommerceId
|
||||
- source: printfulId
|
||||
- name: editable
|
||||
label:
|
||||
de: Bearbeitbar
|
||||
en: Editable
|
||||
|
||||
subFields:
|
||||
- source: status
|
||||
- source: notes
|
||||
subNavigation:
|
||||
- name: modalForeign
|
||||
defaultSort:
|
||||
field: "path"
|
||||
order: "ASC"
|
||||
views: *views
|
||||
defaultCallback:
|
||||
eval: |
|
||||
//js
|
||||
(entry) => {
|
||||
parent.selectEntry(entry)
|
||||
}
|
||||
//!js
|
||||
|
||||
- name: new
|
||||
label:
|
||||
de: Neu
|
||||
en: New
|
||||
muiIcon: new
|
||||
defaultSort:
|
||||
field: insertTime
|
||||
order: DESC
|
||||
views: *views
|
||||
filter:
|
||||
status: pending
|
||||
|
||||
- name: inProgress
|
||||
label:
|
||||
de: In Bearbeitung
|
||||
en: In Progress
|
||||
muiIcon: inProgress
|
||||
defaultSort:
|
||||
field: insertTime
|
||||
order: DESC
|
||||
views: *views
|
||||
filter:
|
||||
status: refunded
|
||||
permissions:
|
||||
public:
|
||||
methods:
|
||||
get: true
|
||||
post: true
|
||||
put: false
|
||||
delete: false
|
||||
user:
|
||||
methods:
|
||||
get: true
|
||||
post: true
|
||||
put: true
|
||||
delete: true
|
||||
|
||||
hooks:
|
||||
get:
|
||||
read:
|
||||
type: javascript
|
||||
file: hooks/orderRevokeRequest/get_read.js
|
||||
post:
|
||||
create:
|
||||
type: javascript
|
||||
file: hooks/orderRevokeRequest/post_create.js
|
||||
return:
|
||||
type: javascript
|
||||
file: hooks/orderRevokeRequest/post_return.js
|
||||
|
||||
fields:
|
||||
- name: status
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: Status
|
||||
en: Status
|
||||
widget: select
|
||||
choices:
|
||||
- name:
|
||||
de: Wartend
|
||||
en: pending
|
||||
id: pending
|
||||
- name:
|
||||
de: Zurückgezahlt
|
||||
en: Refunded
|
||||
id: refunded
|
||||
|
||||
containerProps:
|
||||
layout:
|
||||
size:
|
||||
default: "col-6"
|
||||
small: "col-12"
|
||||
large: "col-6"
|
||||
- name: email
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: E-Mail
|
||||
en: E-Mail
|
||||
containerProps:
|
||||
layout:
|
||||
size:
|
||||
default: "col-6"
|
||||
small: "col-12"
|
||||
large: "col-6"
|
||||
- name: printfulId
|
||||
type: number
|
||||
meta:
|
||||
label:
|
||||
de: Printful Bestellungs-ID
|
||||
en: Printful Order-ID
|
||||
|
||||
- name: bigCommerceId
|
||||
type: number
|
||||
meta:
|
||||
label:
|
||||
de: Bestellnummer
|
||||
en: Order ID
|
||||
widget: foreignKey
|
||||
filter:
|
||||
type: foreignKey
|
||||
foreign:
|
||||
collection: bigCommerceOrder
|
||||
id: bigCommerceId
|
||||
subNavigation: 0
|
||||
render:
|
||||
defaultCollectionViews: true
|
||||
- name: notes
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: Notizen
|
||||
en: Notes
|
||||
widget: string
|
||||
inputProps:
|
||||
multiline: true
|
||||
containerProps:
|
||||
layout:
|
||||
size:
|
||||
default: "col-12"
|
||||
small: "col-12"
|
||||
large: "col-12"
|
||||
@@ -1,68 +0,0 @@
|
||||
name: qrCode
|
||||
|
||||
meta:
|
||||
allowExportAll: true
|
||||
label:
|
||||
de: QR Codes
|
||||
en: QR Codes
|
||||
|
||||
muiIcon: label
|
||||
|
||||
defaultSort:
|
||||
field: name
|
||||
order: ASC
|
||||
backup:
|
||||
active: true
|
||||
collectionName: backups
|
||||
views: &views
|
||||
- type: table
|
||||
columns:
|
||||
- source: customer
|
||||
filter:
|
||||
type: foreignKey
|
||||
- source: createdAt
|
||||
filter: true
|
||||
|
||||
- source: id
|
||||
renderValue:
|
||||
raw: true
|
||||
eval: |
|
||||
(function(){
|
||||
const id = $.id;
|
||||
return `<button style="display: flex; align-items: center; padding: 8px 16px; background-color: #1D4ED8; color: white; font-weight: 600; border-radius: 6px; box-shadow: 0px 4px 6px rgba(0, 0, 0, 0.1); border: none; cursor: pointer; transition: background-color 0.3s ease;" onclick="event.stopPropagation(); event.preventDefault();navigator.clipboard.writeText('https://binkrassdufass.de/redirecttoprofile/${id}'); window.open('https://binkrassdufass.de/redirecttoprofile/${id}', '_blank')">Go to Profile <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-copy" style="margin-left: 8px;" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M4 2a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2zm2-1a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1zM2 5a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1v-1h1v1a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h1v1z"/></svg></button>` })()
|
||||
|
||||
permissions:
|
||||
public:
|
||||
methods:
|
||||
get: true
|
||||
post: false
|
||||
put: false
|
||||
delete: false
|
||||
user:
|
||||
methods:
|
||||
get: true
|
||||
post: true
|
||||
put: true
|
||||
delete: true
|
||||
|
||||
fields:
|
||||
- name: customer
|
||||
type: string
|
||||
index: [single]
|
||||
meta:
|
||||
label:
|
||||
de: Kunde
|
||||
en: Customer
|
||||
widget: foreignKey
|
||||
foreign:
|
||||
collection: bigCommerceCustomer
|
||||
id: id
|
||||
subNavigation: 0
|
||||
render:
|
||||
defaultCollectionViews: true
|
||||
filter:
|
||||
type: foreignKey
|
||||
indexes:
|
||||
- name: fulltext # Ein eindeutiger Name für den Index. Es ist optional, wird jedoch empfohlen, um den Index später leicht identifizieren zu können.
|
||||
key: # Bestimmt, auf welche Felder der Index angewendet werden soll. Dies kann ein einfacher String sein, wenn der Index nur ein Feld umfasst, oder ein Array von Strings, wenn der Index mehrere Felder umfasst.
|
||||
- $text:$**
|
||||
@@ -1,278 +0,0 @@
|
||||
name: bigCommerceCustomer
|
||||
# This is just so its easier to reference BigCommerce customer,
|
||||
# its not intended to be used as a actual reference,
|
||||
# just so its easier to reference customer in TibiCMS
|
||||
meta:
|
||||
label:
|
||||
de: Kunde
|
||||
en: Customer
|
||||
views:
|
||||
- type: table
|
||||
columns:
|
||||
- source: email
|
||||
filter: true
|
||||
|
||||
- source: username
|
||||
filter: true
|
||||
|
||||
- source: bigCommerceId
|
||||
filter: true
|
||||
- source: personalRecords.recording
|
||||
filter:
|
||||
type: foreignKey
|
||||
|
||||
tablist:
|
||||
activeTab: generalDetails
|
||||
tabs:
|
||||
- name: generalDetails
|
||||
label:
|
||||
de: Allgemeine Details
|
||||
en: General Details
|
||||
subFields:
|
||||
- source: email
|
||||
- source: username
|
||||
- source: bigCommerceId
|
||||
|
||||
- name: socialMedia
|
||||
label:
|
||||
de: Soziale Medien
|
||||
en: Social Media
|
||||
subFields:
|
||||
- source: socialMediaAccounts
|
||||
|
||||
- name: personalRecords
|
||||
label:
|
||||
de: Persönliche Daten
|
||||
en: Personal Records
|
||||
subFields:
|
||||
- source: personalRecords
|
||||
- name: admin
|
||||
label:
|
||||
de: Admin
|
||||
en: Admin
|
||||
subFields:
|
||||
- source: currentToken
|
||||
- source: locked
|
||||
|
||||
backup:
|
||||
active: true
|
||||
collectionName: backups
|
||||
subNavigation:
|
||||
- name: modal
|
||||
views:
|
||||
- type: table
|
||||
columns:
|
||||
- source: email
|
||||
defaultCallback: # Standard-Callback-Funktion, die ausgeführt wird, wenn keine andere spezifiziert ist.
|
||||
eval: | # Der Code wird als JavaScript evaluiert.
|
||||
//js
|
||||
(entry) => { // Diese Funktion nimmt den Eintrag (entry) als Argument.
|
||||
parent.selectEntry(entry) // Die Funktion selectEntry auf dem übergeordneten Objekt wird mit dem Eintrag als Argument aufgerufen.
|
||||
}
|
||||
//!js
|
||||
hooks:
|
||||
put:
|
||||
# check and return jwt
|
||||
update:
|
||||
type: javascript
|
||||
file: hooks/customer/put_update.js
|
||||
get:
|
||||
read:
|
||||
type: javascript
|
||||
file: hooks/customer/get_read.js
|
||||
|
||||
permissions:
|
||||
public:
|
||||
methods:
|
||||
get: true
|
||||
post: false
|
||||
put: true
|
||||
delete: false
|
||||
user:
|
||||
methods:
|
||||
get: true
|
||||
post: false
|
||||
put: false
|
||||
delete: false
|
||||
|
||||
token:${BIGCOMMERCE_WEBHOOK_TOKEN}:
|
||||
methods:
|
||||
get: true
|
||||
post: true
|
||||
put: true
|
||||
delete: true
|
||||
|
||||
fields:
|
||||
- name: bigCommerceId
|
||||
type: number
|
||||
meta:
|
||||
label:
|
||||
de: BigCommerce ID
|
||||
en: BigCommerce ID
|
||||
helperText:
|
||||
de: Die ID des Kunden in BigCommerce
|
||||
en: The ID of the customer in BigCommerce
|
||||
|
||||
- name: email
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: Angehängte E-Mail
|
||||
en: Attached Email
|
||||
helperText:
|
||||
de: Die E-Mail-Adresse des Kunden
|
||||
en: The email address of the customer
|
||||
|
||||
- name: username
|
||||
type: string
|
||||
index: [single, unique]
|
||||
meta:
|
||||
label:
|
||||
de: Benutzername
|
||||
en: Username
|
||||
helperText:
|
||||
de: Der Benutzername des Kunden
|
||||
en: The username of the customer
|
||||
|
||||
- name: locked
|
||||
type: boolean
|
||||
meta:
|
||||
label:
|
||||
de: Gesperrt
|
||||
en: Locked
|
||||
helperText:
|
||||
de: Ob der Kunde gesperrt ist
|
||||
en: Whether the customer is locked
|
||||
|
||||
- name: currentToken
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: Aktueller Token
|
||||
en: Current Token
|
||||
inputProps:
|
||||
disabled: true
|
||||
helperText:
|
||||
de: Der aktuelle Token des Kunden
|
||||
en: The current token of the customer
|
||||
|
||||
- name: socialMediaAccounts
|
||||
type: object
|
||||
meta:
|
||||
label:
|
||||
de: Soziale Medien
|
||||
en: Social Media
|
||||
helperText:
|
||||
de: Die sozialen Medien des Kunden
|
||||
en: The social media of the customer
|
||||
widget: containerLessObject
|
||||
subFields:
|
||||
- name: instagramLink
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: Instagram Link
|
||||
en: Instagram Link
|
||||
helperText:
|
||||
de: Der Instagram-Link des Kunden
|
||||
en: The Instagram link of the customer
|
||||
- name: facebookLink
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: Facebook Link
|
||||
en: Facebook Link
|
||||
helperText:
|
||||
de: Der Facebook-Link des Kunden
|
||||
en: The Facebook link of the customer
|
||||
- name: twitterLink
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: Twitter Link
|
||||
en: Twitter Link
|
||||
helperText:
|
||||
de: Der Twitter-Link des Kunden
|
||||
en: The Twitter link of the customer
|
||||
- name: tiktokLink
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: TikTok Link
|
||||
en: TikTok Link
|
||||
helperText:
|
||||
de: Der TikTok-Link des Kunden
|
||||
en: The TikTok link of the customer
|
||||
- name: youtubeLink
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: YouTube Link
|
||||
en: YouTube Link
|
||||
helperText:
|
||||
de: Der YouTube-Link des Kunden
|
||||
en: The YouTube link of the customer
|
||||
|
||||
- name: personalRecords
|
||||
type: object[]
|
||||
meta:
|
||||
label:
|
||||
de: Persönliche Daten
|
||||
en: Personal Records
|
||||
helperText:
|
||||
de: Die persönlichen Daten des Kunden
|
||||
en: The personal records of the customer
|
||||
subFields:
|
||||
- name: title
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: Titel
|
||||
en: Title
|
||||
- name: description
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: Beschreibung
|
||||
en: Description
|
||||
- name: ageAtRecording
|
||||
type: number
|
||||
meta:
|
||||
label:
|
||||
de: Alter bei Aufnahme
|
||||
en: Age at Recording
|
||||
- name: priority
|
||||
type: number
|
||||
meta:
|
||||
label:
|
||||
de: Priorität
|
||||
en: Priority
|
||||
- name: recording
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: Aufnahme
|
||||
en: Recording
|
||||
widget: foreignKey
|
||||
filter:
|
||||
type: foreignKey
|
||||
foreign:
|
||||
collection: medialib
|
||||
subNavigation: 0
|
||||
id: id
|
||||
render:
|
||||
defaultCollectionViews: true
|
||||
|
||||
- name: thumbnail
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: Vorschaubild
|
||||
en: Thumbnail
|
||||
widget: foreignKey
|
||||
foreign:
|
||||
collection: medialib
|
||||
subNavigation: 0
|
||||
id: id
|
||||
render:
|
||||
defaultCollectionViews: true
|
||||
@@ -1,281 +0,0 @@
|
||||
name: bigCommerceOrder
|
||||
# This is just so its easier to reference BigCommerce order,
|
||||
# its not intended to be used as a actual reference,
|
||||
# just so its easier to reference order in TibiCMS
|
||||
meta:
|
||||
label:
|
||||
de: Bestellung
|
||||
en: Order
|
||||
views: !include fieldLists/orderViews.yml
|
||||
backup:
|
||||
active: true
|
||||
collectionName: backups
|
||||
subNavigation:
|
||||
- name: modal
|
||||
views: !include fieldLists/orderViews.yml
|
||||
defaultCallback: # Standard-Callback-Funktion, die ausgeführt wird, wenn keine andere spezifiziert ist.
|
||||
eval: | # Der Code wird als JavaScript evaluiert.
|
||||
//js
|
||||
(entry) => { // Diese Funktion nimmt den Eintrag (entry) als Argument.
|
||||
parent.selectEntry(entry) // Die Funktion selectEntry auf dem übergeordneten Objekt wird mit dem Eintrag als Argument aufgerufen.
|
||||
}
|
||||
//!js
|
||||
|
||||
permissions:
|
||||
public:
|
||||
methods:
|
||||
get: true
|
||||
post: false
|
||||
put: false
|
||||
delete: false
|
||||
user:
|
||||
methods:
|
||||
get: true
|
||||
post: true
|
||||
put: true
|
||||
delete: false
|
||||
|
||||
token:${BIGCOMMERCE_WEBHOOK_TOKEN}:
|
||||
methods:
|
||||
get: true
|
||||
post: true
|
||||
put: true
|
||||
delete: true
|
||||
|
||||
hooks:
|
||||
get:
|
||||
read:
|
||||
type: javascript
|
||||
file: hooks/order/get_read.js
|
||||
|
||||
fields:
|
||||
- name: status
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: Status
|
||||
en: Status
|
||||
widget: select
|
||||
choices:
|
||||
- id: draft
|
||||
name:
|
||||
de: Entwurf
|
||||
en: Draft
|
||||
- id: pending
|
||||
name:
|
||||
de: Ausstehend
|
||||
en: Pending
|
||||
- id: failed
|
||||
name:
|
||||
de: Fehlgeschlagen
|
||||
en: Failed
|
||||
- id: completed
|
||||
name:
|
||||
de: Abgeschlossen
|
||||
en: Completed
|
||||
- id: cancelled
|
||||
name:
|
||||
de: Storniert
|
||||
en: Cancelled
|
||||
- id: inprocess
|
||||
name:
|
||||
de: In Bearbeitung
|
||||
en: In Process
|
||||
- id: onhold
|
||||
name:
|
||||
de: Zurückgehalten
|
||||
en: On Hold
|
||||
- id: partial
|
||||
name:
|
||||
de: Teilweise
|
||||
en: Partial
|
||||
- id: fulfilled
|
||||
name:
|
||||
de: Erfüllt
|
||||
en: Fulfilled
|
||||
|
||||
containerProps:
|
||||
layout:
|
||||
size:
|
||||
default: "col-6"
|
||||
small: "col-12"
|
||||
large: "col-6"
|
||||
- name: statusSetAt
|
||||
type: date
|
||||
meta:
|
||||
label:
|
||||
de: Status gesetzt am
|
||||
en: Status set at
|
||||
containerProps:
|
||||
layout:
|
||||
size:
|
||||
default: "col-6"
|
||||
small: "col-12"
|
||||
large: "col-6"
|
||||
- name: bigCommerceId
|
||||
type: number
|
||||
meta:
|
||||
label:
|
||||
de: BigCommerce ID
|
||||
en: BigCommerce ID
|
||||
helperText:
|
||||
de: Die ID der Bestellung in BigCommerce
|
||||
en: The ID of the Order in BigCommerce
|
||||
containerProps:
|
||||
layout:
|
||||
size:
|
||||
default: "col-6"
|
||||
small: "col-12"
|
||||
large: "col-6"
|
||||
|
||||
- name: customerBigCommerceId
|
||||
type: number
|
||||
meta:
|
||||
label:
|
||||
de: Kunde BigCommerce ID
|
||||
en: Customer BigCommerce ID
|
||||
helperText:
|
||||
de: Die ID des Kunden in BigCommerce
|
||||
en: The ID of the customer in BigCommerce
|
||||
|
||||
containerProps:
|
||||
layout:
|
||||
size:
|
||||
default: "col-6"
|
||||
small: "col-12"
|
||||
large: "col-6"
|
||||
|
||||
- name: customerTibiId
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: Kunde
|
||||
en: Customer
|
||||
filter:
|
||||
type: foreignKey
|
||||
widget: foreignKey
|
||||
foreign:
|
||||
collection: bigCommerceCustomer
|
||||
id: id
|
||||
subNavigation: 0
|
||||
render:
|
||||
defaultCollectionViews: true
|
||||
|
||||
- name: products
|
||||
type: object[]
|
||||
meta:
|
||||
label:
|
||||
de: Produkte
|
||||
en: Producte
|
||||
widget: containerLessObjectArray
|
||||
subFields:
|
||||
- name: bigCommerceId
|
||||
type: number
|
||||
meta:
|
||||
label:
|
||||
de: BigCommerce ID
|
||||
en: BigCommerce ID
|
||||
helperText:
|
||||
de: Die ID des Produkts in BigCommerce
|
||||
en: The ID of the product in BigCommerce
|
||||
containerProps:
|
||||
layout:
|
||||
size:
|
||||
default: "col-6"
|
||||
small: "col-12"
|
||||
large: "col-6"
|
||||
|
||||
- name: tibiId
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: Tibi ID
|
||||
en: Tibi ID
|
||||
widget: foreignKey
|
||||
filter:
|
||||
type: foreignKey
|
||||
foreign:
|
||||
collection: bigCommerceProduct
|
||||
id: id
|
||||
subNavigation: 0
|
||||
render:
|
||||
defaultCollectionViews: true
|
||||
containerProps:
|
||||
layout:
|
||||
size:
|
||||
default: "col-6"
|
||||
small: "col-12"
|
||||
large: "col-6"
|
||||
|
||||
- name: quantity
|
||||
type: number
|
||||
meta:
|
||||
label:
|
||||
de: Menge
|
||||
en: Quantity
|
||||
|
||||
- name: shipments
|
||||
type: object[]
|
||||
meta:
|
||||
label:
|
||||
de: Lieferungen
|
||||
en: Shipments
|
||||
subFields:
|
||||
- name: trackingUrl
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: Tracking URL
|
||||
en: Tracking URL
|
||||
helperText:
|
||||
de: Die URL, um das Paket zu verfolgen
|
||||
en: The URL to track the package
|
||||
containerProps:
|
||||
layout:
|
||||
size:
|
||||
default: "col-6"
|
||||
small: "col-12"
|
||||
large: "col-6"
|
||||
|
||||
- name: trackingNumber
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: Tracking Nummer
|
||||
en: Tracking Number
|
||||
helperText:
|
||||
de: Die Tracking Nummer des Pakets
|
||||
en: The tracking number of the package
|
||||
containerProps:
|
||||
layout:
|
||||
size:
|
||||
default: "col-6"
|
||||
small: "col-12"
|
||||
large: "col-6"
|
||||
- name: carrier
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: Carrier
|
||||
en: Carrier
|
||||
helperText:
|
||||
de: Der Carrier des Pakets
|
||||
en: The carrier of the package
|
||||
containerProps:
|
||||
layout:
|
||||
size:
|
||||
default: "col-6"
|
||||
small: "col-12"
|
||||
large: "col-6"
|
||||
- name: sentAt
|
||||
type: date
|
||||
meta:
|
||||
label:
|
||||
de: Versendet am
|
||||
en: Sent at
|
||||
containerProps:
|
||||
layout:
|
||||
size:
|
||||
default: "col-6"
|
||||
small: "col-12"
|
||||
large: "col-6"
|
||||
@@ -1,273 +0,0 @@
|
||||
name: bigCommerceProduct
|
||||
# This is just so its easier to reference BigCommerce products,
|
||||
# its not intended to be used as a actual reference,
|
||||
# just so its easier to reference products in TibiCMS
|
||||
meta:
|
||||
label:
|
||||
de: Produkt
|
||||
en: Product
|
||||
views: &views
|
||||
- type: cardList
|
||||
mediaQuery: "(min-width: 1200px)"
|
||||
selectionPriority: 1
|
||||
fields:
|
||||
- source: productName
|
||||
filter: true
|
||||
name:
|
||||
de: Produktname
|
||||
en: Product Name
|
||||
|
||||
- source: previewImage
|
||||
filter: true
|
||||
name:
|
||||
de: Vorschaubild
|
||||
en: Preview Image
|
||||
|
||||
- source: printfulProductId
|
||||
filter: true
|
||||
name:
|
||||
de: Printful Produkt ID
|
||||
en: Printful Product ID
|
||||
|
||||
- source: forcedWarning
|
||||
filter: true
|
||||
name:
|
||||
de: Warnungsanzeige
|
||||
en: Warning Display
|
||||
|
||||
- type: table
|
||||
mediaQuery: "(min-width: 600px)"
|
||||
columns:
|
||||
- source: productName
|
||||
- source: previewImage
|
||||
- source: bigCommerceSKU
|
||||
- source: printfulProductId
|
||||
- source: forcedWarning
|
||||
|
||||
- type: simpleList
|
||||
primaryText: previewImage
|
||||
secondaryText: productName
|
||||
tertiaryText: printfulProductId
|
||||
tablist:
|
||||
activeTab: generalDetails
|
||||
tabs:
|
||||
- name: generalDetails
|
||||
label:
|
||||
de: Allgemeine Details
|
||||
en: General Details
|
||||
subFields:
|
||||
- source: productName
|
||||
- source: previewImage
|
||||
- source: bigCommerceSKU
|
||||
- source: bigCommerceId
|
||||
- name: edit
|
||||
label:
|
||||
de: Bearbeitbar
|
||||
en: Editable
|
||||
subFields:
|
||||
- source: forcedWarning
|
||||
- source: printfulProductId
|
||||
- name: sizingChart
|
||||
label:
|
||||
de: Größentabelle
|
||||
en: Sizing Chart
|
||||
subFields:
|
||||
- source: sizingChart
|
||||
|
||||
subNavigation:
|
||||
- name: modal
|
||||
views: *views
|
||||
|
||||
defaultCallback: # Standard-Callback-Funktion, die ausgeführt wird, wenn keine andere spezifiziert ist.
|
||||
eval: | # Der Code wird als JavaScript evaluiert.
|
||||
//js
|
||||
(entry) => { // Diese Funktion nimmt den Eintrag (entry) als Argument.
|
||||
parent.selectEntry(entry) // Die Funktion selectEntry auf dem übergeordneten Objekt wird mit dem Eintrag als Argument aufgerufen.
|
||||
}
|
||||
//!js
|
||||
|
||||
permissions:
|
||||
public:
|
||||
methods:
|
||||
get: true
|
||||
post: false
|
||||
put: false
|
||||
delete: false
|
||||
user:
|
||||
methods:
|
||||
get: true
|
||||
post: true
|
||||
put: false
|
||||
delete: false
|
||||
|
||||
token:${BIGCOMMERCE_WEBHOOK_TOKEN}:
|
||||
methods:
|
||||
get: true
|
||||
post: true
|
||||
put: true
|
||||
delete: true
|
||||
hooks:
|
||||
put:
|
||||
update:
|
||||
type: javascript
|
||||
file: hooks/product/put_update.js
|
||||
post:
|
||||
create:
|
||||
type: javascript
|
||||
file: hooks/product/post_create.js
|
||||
get:
|
||||
read:
|
||||
type: javascript
|
||||
file: hooks/product/get_read.js
|
||||
|
||||
return:
|
||||
type: javascript
|
||||
file: hooks/product/get_return.js
|
||||
fields:
|
||||
- name: bigCommerceSKU
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: BigCommerce SKU
|
||||
en: BigCommerce SKU
|
||||
helperText:
|
||||
de: Die SKU des Produkts in BigCommerce
|
||||
en: The SKU of the product in BigCommerce
|
||||
|
||||
- name: bigCommerceId
|
||||
type: number
|
||||
meta:
|
||||
label:
|
||||
de: BigCommerce ID
|
||||
en: BigCommerce ID
|
||||
helperText:
|
||||
de: Die ID des Produkts in BigCommerce
|
||||
en: The ID of the product in BigCommerce
|
||||
- name: printfulProductId
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: Printful Produkt ID
|
||||
en: Printful Product ID
|
||||
helperText:
|
||||
de: Die ID des Produkts in Printful
|
||||
en: The ID of the product in Printful
|
||||
|
||||
- name: previewImage
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: Vorschaubild
|
||||
en: Preview Image
|
||||
widget: imageURL
|
||||
|
||||
- name: productName
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: Produktname
|
||||
en: Product Name
|
||||
|
||||
- name: forcedWarning
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: Warnungsanzeige
|
||||
en: Warning Display
|
||||
|
||||
- name: sizingChart
|
||||
type: object
|
||||
meta:
|
||||
label:
|
||||
de: Größentabelle
|
||||
en: Sizing Chart
|
||||
subFields:
|
||||
- name: imageURL
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: Bild
|
||||
en: Image
|
||||
widget: imageURL
|
||||
|
||||
- name: imageDescription
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: Bildbeschreibung
|
||||
en: Image Description
|
||||
widget: richtext
|
||||
|
||||
- name: generalDescription
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: Allgemeine Beschreibung
|
||||
en: General Description
|
||||
widget: richtext
|
||||
|
||||
- name: availableSizes
|
||||
type: string[]
|
||||
meta:
|
||||
label:
|
||||
de: Verfügbare Größen
|
||||
en: Available Sizes
|
||||
widget: select
|
||||
choices:
|
||||
- name: XS
|
||||
id: XS
|
||||
- name: S
|
||||
id: S
|
||||
- name: M
|
||||
id: M
|
||||
- name: L
|
||||
id: L
|
||||
- name: XL
|
||||
id: XL
|
||||
- name: 2XL
|
||||
id: 2XL
|
||||
- name: 3XL
|
||||
id: 3XL
|
||||
- name: 4XL
|
||||
id: 4XL
|
||||
- name: 5XL
|
||||
id: 5XL
|
||||
- name: 6XL
|
||||
id: 6XL
|
||||
- name: columns
|
||||
type: object[]
|
||||
meta:
|
||||
label:
|
||||
de: Spalten
|
||||
en: Columns
|
||||
subFields:
|
||||
- name: label
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: Label ID
|
||||
en: Label ID
|
||||
widget: foreignKey
|
||||
foreign:
|
||||
collection: module
|
||||
id: label
|
||||
subNavigation: 0
|
||||
render:
|
||||
defaultCollectionViews: true
|
||||
containerProps:
|
||||
layout:
|
||||
size:
|
||||
default: "col-6"
|
||||
small: "col-12"
|
||||
large: "col-6"
|
||||
- name: sizes
|
||||
type: string[]
|
||||
meta:
|
||||
label:
|
||||
de: Größen
|
||||
en: Sizes
|
||||
useDefaultArray: true
|
||||
widget: string
|
||||
helperText:
|
||||
de: In CM, selbe reihenfolge wie in der availableSizes
|
||||
en: In CM, same order as in availableSizes
|
||||
@@ -1,38 +0,0 @@
|
||||
# just here since bgicommerces graphql api regarding cart and checkout isnt working (only cart create is possible, otherwise it always says cart is not existant)
|
||||
|
||||
########################################################################
|
||||
# cart
|
||||
########################################################################
|
||||
|
||||
name: dummyCartEndpoint
|
||||
meta:
|
||||
hideInNavigation: true
|
||||
openapi:
|
||||
disabled: true
|
||||
|
||||
permissions:
|
||||
public:
|
||||
methods:
|
||||
get: true
|
||||
post: true
|
||||
put: false
|
||||
delete: false
|
||||
|
||||
user:
|
||||
methods:
|
||||
get: false
|
||||
post: false
|
||||
put: false
|
||||
delete: false
|
||||
|
||||
hooks:
|
||||
get:
|
||||
read:
|
||||
type: javascript
|
||||
file: hooks/cart/get_read.js
|
||||
post:
|
||||
validate:
|
||||
type: javascript
|
||||
file: hooks/cart/post_validate.js
|
||||
|
||||
fields:
|
||||
@@ -1,74 +0,0 @@
|
||||
- name: products
|
||||
type: object[]
|
||||
meta:
|
||||
label:
|
||||
de: Produkte
|
||||
en: Products
|
||||
subFields:
|
||||
- name: productImage
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: Produktbild
|
||||
en: Product Image
|
||||
widget: foreignKey
|
||||
foreign:
|
||||
collection: medialib
|
||||
id: id
|
||||
subNavigation: 0
|
||||
render:
|
||||
defaultCollectionViews: true
|
||||
|
||||
- name: productReference
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: Produktreferenz
|
||||
en: Product Reference
|
||||
widget: foreignKey
|
||||
foreign:
|
||||
collection: bigCommerceProduct
|
||||
id: id
|
||||
subNavigation: 0
|
||||
render:
|
||||
defaultCollectionViews: true
|
||||
|
||||
- name: imageWidth
|
||||
type: number
|
||||
meta:
|
||||
label:
|
||||
de: Bildbreite
|
||||
en: Image Width
|
||||
helperText:
|
||||
de: "Höhe des Bildes in Prozent."
|
||||
en: "Height of the image in percent."
|
||||
|
||||
- name: imageHeight
|
||||
type: number
|
||||
meta:
|
||||
label:
|
||||
de: Bildhöhe
|
||||
en: Image Height
|
||||
helperText:
|
||||
de: "Höhe des Bildes in Prozent."
|
||||
en: "Height of the image in percent."
|
||||
|
||||
- name: imageTop
|
||||
type: number
|
||||
meta:
|
||||
label:
|
||||
de: Bildabstand oben
|
||||
en: Image Top
|
||||
helperText:
|
||||
de: "Abstand des Bildes zum oberen Rand in Prozent."
|
||||
en: "Distance of the image to the top edge in percent."
|
||||
|
||||
- name: imageLeft
|
||||
type: number
|
||||
meta:
|
||||
label:
|
||||
de: Bildabstand links
|
||||
en: Image Left
|
||||
helperText:
|
||||
de: "Abstand des Bildes zum linken Rand in Prozent."
|
||||
en: "Distance of the image to the left edge in percent."
|
||||
@@ -1,33 +0,0 @@
|
||||
name: checkboxGroupInput
|
||||
type: object
|
||||
meta:
|
||||
label:
|
||||
de: Checkbox Gruppe
|
||||
en: Checkbox Group
|
||||
dependsOn:
|
||||
eval: $parent?.inputWidgets?.includes('checkboxGroup')
|
||||
helperText:
|
||||
de: Ansammlung von Checkboxen
|
||||
en: Collection of checkboxes
|
||||
subFields:
|
||||
- name: groupTitle
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: Gruppe Titel
|
||||
en: Group title
|
||||
|
||||
- name: checkboxes
|
||||
type: object[]
|
||||
meta:
|
||||
label:
|
||||
|
||||
de: Checkbox Gruppe
|
||||
en: Checkbox Group
|
||||
direction: row
|
||||
widget: containerLessObjectArray
|
||||
subFields:
|
||||
- name: standardInputProperties
|
||||
meta:
|
||||
widget: containerLessObject
|
||||
subFields: !include standardInputProperties.yml
|
||||
@@ -1,17 +0,0 @@
|
||||
name: dateInput
|
||||
type: object
|
||||
meta:
|
||||
label:
|
||||
de: Normaler Kalender
|
||||
en: Normal calendar
|
||||
helperText:
|
||||
de: Datumsfeld
|
||||
en: Date field
|
||||
dependsOn:
|
||||
eval: $parent?.inputWidgets?.includes('defaultCalendar')
|
||||
subFields:
|
||||
- name: standardInputProperties
|
||||
type: object
|
||||
meta:
|
||||
widget: containerLessObject
|
||||
subFields: !include standardInputProperties.yml
|
||||
@@ -1,46 +0,0 @@
|
||||
name: datePickerInput
|
||||
type: object
|
||||
meta:
|
||||
label:
|
||||
de: Custom Kalender
|
||||
en: Custom Calendar
|
||||
dependsOn:
|
||||
eval: $parent?.inputWidgets?.includes('customCalendar')
|
||||
subFields:
|
||||
- name: props
|
||||
type: object
|
||||
meta:
|
||||
label:
|
||||
de: Datumauswahl Eigenschaften
|
||||
en: Date selection properties
|
||||
subFields:
|
||||
- name: allowedDateRanges
|
||||
type: object[]
|
||||
meta:
|
||||
label:
|
||||
de: Erlaubte Datumsbereiche
|
||||
en: Allowed date ranges
|
||||
widget: containerLessObjectArray
|
||||
subFields:
|
||||
- !include ../../fields/from.yml
|
||||
- name: to
|
||||
type: date
|
||||
meta:
|
||||
label:
|
||||
de: Bis
|
||||
en: To
|
||||
widget: date
|
||||
containerProps:
|
||||
layout:
|
||||
size:
|
||||
default: "col-6"
|
||||
small: "col-12"
|
||||
large: "col-6"
|
||||
|
||||
- !include ../../fields/excludedDays.yml
|
||||
|
||||
- name: standardInputProperties
|
||||
type: object
|
||||
meta:
|
||||
widget: containerLessObject
|
||||
subFields: !include standardInputProperties.yml
|
||||
@@ -1,192 +0,0 @@
|
||||
- name: emailSubject
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: Email Betreff
|
||||
en: Email Subject
|
||||
containerProps:
|
||||
layout:
|
||||
size:
|
||||
default: "col-6"
|
||||
small: "col-12"
|
||||
large: "col-6"
|
||||
|
||||
- name: emailReciever
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: Email Empfänger
|
||||
en: Email Reciever
|
||||
helperText:
|
||||
de: "Bsp: xyz@gmail.com"
|
||||
en: "E.g.: xyz@gmail.com"
|
||||
containerProps:
|
||||
layout:
|
||||
size:
|
||||
default: "col-6"
|
||||
small: "col-12"
|
||||
large: "col-6"
|
||||
|
||||
- !include ../../fields/emailCC.yml
|
||||
|
||||
- name: emailIntroduction
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: Email Einleitungssatz
|
||||
en: Email Introduction Sentence
|
||||
helperText:
|
||||
de: "Bsp: Hallo xyz, sie haben eine neue Kaufanfrage erhalten!"
|
||||
en: "E.g.: Hello xyz, you have received a new purchase request!"
|
||||
|
||||
- name: sendFormBtnText
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: Formular Absenden Button Text
|
||||
en: Form Submit Button Text
|
||||
helperText:
|
||||
de: "Bsp: Absenden"
|
||||
en: "E.g.: Submit"
|
||||
|
||||
- name: rows
|
||||
type: object[]
|
||||
meta:
|
||||
label:
|
||||
de: Zeile
|
||||
en: Row
|
||||
widget: grid
|
||||
addElementLabel: Zeile Hinzufügen
|
||||
|
||||
subFields:
|
||||
- name: title
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: Zeilenname
|
||||
en: Row name
|
||||
helperText:
|
||||
de: "Sollte der Titel keinen Wert enthalten, wird kein Zeilenname angezeigt!"
|
||||
en: "If the title does not contain a value, no row name will be displayed!"
|
||||
|
||||
- name: emailTitle
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: Email Abschnitt Titel
|
||||
en: Email section title
|
||||
helperText:
|
||||
de: "Sollte der Titel keinen Wert enthalten, wird kein Abschnitt Titel angezeigt!"
|
||||
en: "If the title does not contain a value, no section title will be displayed!"
|
||||
|
||||
- name: columns
|
||||
type: object[]
|
||||
|
||||
meta:
|
||||
label:
|
||||
de: Spalte
|
||||
en: Column
|
||||
addElementLabel:
|
||||
de: Spalte hinzufügen
|
||||
en: Add column
|
||||
widget: grid
|
||||
direction: horizontal
|
||||
subFields:
|
||||
- name: title
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: Überschrift
|
||||
en: Headline
|
||||
helperText:
|
||||
de: Optional
|
||||
en: Optional
|
||||
|
||||
- name: emailTitle
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: Email Abschnitt Titel
|
||||
en: Email section title
|
||||
|
||||
- name: annotation
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: Zusatzinformation
|
||||
en: Additional information
|
||||
|
||||
- name: inputWidgets
|
||||
type: string[]
|
||||
meta:
|
||||
label:
|
||||
de: Angezeigte Eingabefelder
|
||||
en: Displayed input fields
|
||||
widget: selectArray
|
||||
choices:
|
||||
- name:
|
||||
de: Nummernblock
|
||||
en: Number block
|
||||
id: labelNumber
|
||||
|
||||
- name:
|
||||
de: Zeitenauswahlfeld
|
||||
en: Time selection field
|
||||
id: times
|
||||
|
||||
- name:
|
||||
de: Auswahlfeld
|
||||
en: Selection field
|
||||
id: select
|
||||
|
||||
- name:
|
||||
de: Datumsauswahl - Standard Kalender
|
||||
en: Date selection - Standard calendar
|
||||
id: defaultCalendar
|
||||
|
||||
- name:
|
||||
de: Datumauswahl - Custom Kalender
|
||||
en: Date selection - Custom calendar
|
||||
id: customCalendar
|
||||
|
||||
- name:
|
||||
de: Nummerfeld
|
||||
en: Number field
|
||||
id: number
|
||||
|
||||
- name:
|
||||
de: Checkbox Gruppe
|
||||
en: Checkbox group
|
||||
id: checkboxGroup
|
||||
|
||||
- name:
|
||||
de: Mehrfachauswahl
|
||||
en: Multiple selection
|
||||
id: multiSelect
|
||||
|
||||
- name:
|
||||
de: Textfeld
|
||||
en: Text field
|
||||
id: text
|
||||
|
||||
- name:
|
||||
de: Zeitenauswahl
|
||||
en: Time selection
|
||||
id: timeSelect
|
||||
|
||||
- name:
|
||||
de: Standardauswahl
|
||||
en: Standard selection
|
||||
id: standardSelect
|
||||
|
||||
- !include standardSelect.yml
|
||||
|
||||
- !include labelNumberInput.yml
|
||||
- !include timesInput.yml
|
||||
- !include dateInput.yml
|
||||
- !include numberInput.yml
|
||||
- !include checkboxGroup.yml
|
||||
- !include datePicker.yml
|
||||
- !include multiSelectInput.yml
|
||||
- !include textInputs.yml
|
||||
- !include timeSelect.yml
|
||||
@@ -1,79 +0,0 @@
|
||||
name: labelNumberInput
|
||||
type: object[]
|
||||
meta:
|
||||
label:
|
||||
de: Nummer block
|
||||
en: Number block
|
||||
dependsOn:
|
||||
eval: $parent?.inputWidgets?.includes('labelNumber')
|
||||
helperText:
|
||||
de: Links beschreibender Text, rechts zahleneingabe
|
||||
en: Left descriptive text, right number input
|
||||
|
||||
subFields:
|
||||
- name: group
|
||||
type: number
|
||||
meta:
|
||||
label:
|
||||
de: Gruppe
|
||||
en: Group
|
||||
helpterText:
|
||||
de: Pflichtfeld seperierung. Aus einem Nummernblock muss mindestens eine Gruppe input haben.
|
||||
en: Mandatory field separation. A number block must have at least one group input.
|
||||
|
||||
- name: title
|
||||
type: string
|
||||
meta:
|
||||
label: Titel
|
||||
helperText:
|
||||
de: Block Titel
|
||||
en: Block title
|
||||
containerProps:
|
||||
layout:
|
||||
size:
|
||||
default: "col-6"
|
||||
small: "col-12"
|
||||
large: "col-6"
|
||||
|
||||
- name: emailName
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: Email Name
|
||||
en: Email name
|
||||
containerProps:
|
||||
layout:
|
||||
size:
|
||||
default: "col-6"
|
||||
small: "col-12"
|
||||
large: "col-6"
|
||||
|
||||
- name: block
|
||||
type: object[]
|
||||
meta:
|
||||
label: Block
|
||||
widget: containerLessObjectArray
|
||||
subFields:
|
||||
- name: label
|
||||
type: string
|
||||
meta:
|
||||
label: Label
|
||||
containerProps:
|
||||
layout:
|
||||
size:
|
||||
default: "col-6"
|
||||
small: "col-12"
|
||||
large: "col-6"
|
||||
|
||||
- name: emailName
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: Email Name
|
||||
en: Email name
|
||||
containerProps:
|
||||
layout:
|
||||
size:
|
||||
default: "col-6"
|
||||
small: "col-12"
|
||||
large: "col-6"
|
||||
@@ -1,50 +0,0 @@
|
||||
name: multiSelectInput
|
||||
type: object
|
||||
meta:
|
||||
label:
|
||||
de: Mehrfachauswahl Input
|
||||
en: Multi Select Input
|
||||
dependsOn:
|
||||
eval: $parent?.inputWidgets?.includes('multiSelect')
|
||||
subFields:
|
||||
- name: standardInputProperties
|
||||
type: object
|
||||
meta:
|
||||
widget: containerLessObject
|
||||
subFields: !include standardInputProperties.yml
|
||||
|
||||
- name: options
|
||||
type: object[]
|
||||
meta:
|
||||
label:
|
||||
de: Mehrfachauswahl Optionen
|
||||
en: Multi Select Options
|
||||
direction: row
|
||||
widget: containerLessObjectArray
|
||||
subFields:
|
||||
- name: name
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: Name
|
||||
en: Name
|
||||
|
||||
- name: props
|
||||
type: object
|
||||
meta:
|
||||
label:
|
||||
de: Mehrfachauswahl Eigenschaften
|
||||
en: Multi Select Properties
|
||||
subFields:
|
||||
- name: additionalAddableValues
|
||||
type: boolean
|
||||
meta:
|
||||
label:
|
||||
de: Zusätzliche hinzufügbare Werte
|
||||
en: Additional addable values
|
||||
containerProps:
|
||||
layout:
|
||||
size:
|
||||
default: "col-6"
|
||||
small: "col-12"
|
||||
large: "col-6"
|
||||
@@ -1,14 +0,0 @@
|
||||
name: numberInput
|
||||
type: object
|
||||
meta:
|
||||
label:
|
||||
de: Nummereingabe
|
||||
en: Number Input
|
||||
dependsOn:
|
||||
eval: $parent?.inputWidgets?.includes('number')
|
||||
subFields:
|
||||
- name: standardInputProperties
|
||||
type: object
|
||||
meta:
|
||||
widget: containerLessObject
|
||||
subFields: !include standardInputProperties.yml
|
||||
@@ -1,71 +0,0 @@
|
||||
- name: emailTitle
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: Email Titel
|
||||
en: Email title
|
||||
containerProps:
|
||||
layout:
|
||||
size:
|
||||
default: "col-6"
|
||||
small: "col-12"
|
||||
large: "col-6"
|
||||
|
||||
- name: placeholder
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: Platzhalter
|
||||
en: Placeholder
|
||||
containerProps:
|
||||
layout:
|
||||
size:
|
||||
default: "col-6"
|
||||
small: "col-12"
|
||||
large: "col-6"
|
||||
|
||||
- name: notRequired
|
||||
type: boolean
|
||||
meta:
|
||||
label:
|
||||
de: nicht Notwendig
|
||||
en: not required
|
||||
containerProps:
|
||||
layout:
|
||||
size:
|
||||
default: "col-6"
|
||||
small: "col-12"
|
||||
large: "col-6"
|
||||
|
||||
- name: fieldOrder
|
||||
type: number
|
||||
meta:
|
||||
label:
|
||||
de: Reihenfolge
|
||||
en: Order
|
||||
helperText:
|
||||
de: Die kleinste angegebene Zahl wird am weitesten oben in der Formularspalte stehen
|
||||
en: The smallest specified number will be at the top of the form column.
|
||||
containerProps:
|
||||
layout:
|
||||
size:
|
||||
default: "col-6"
|
||||
small: "col-12"
|
||||
large: "col-6"
|
||||
|
||||
- name: textTitle
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: Text Titel
|
||||
en: Text title
|
||||
helperText:
|
||||
de: Alternative zu textPlaceholder, steht dann über dem Inputfeld
|
||||
en: Alternative to textPlaceholder, then stands above the input field
|
||||
|
||||
- name: groupTitle
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: Gruppe Titel
|
||||
en: Group title
|
||||
@@ -1,58 +0,0 @@
|
||||
name: standardSelect
|
||||
type: object
|
||||
meta:
|
||||
label: Standardauswahl
|
||||
dependsOn:
|
||||
eval: $parent?.inputWidgets?.includes('standardSelect')
|
||||
subFields:
|
||||
- name: standardInputProperties
|
||||
type: object
|
||||
meta:
|
||||
widget: containerLessObject
|
||||
subFields: !include standardInputProperties.yml
|
||||
|
||||
- name: selectEntries
|
||||
type: object[]
|
||||
meta:
|
||||
label:
|
||||
de: Auswahleingabe Möglichkeiten
|
||||
en: Selection input options
|
||||
widget: containerLessObjectArray
|
||||
addElementLabel: Auswahleingabe hinzufügen
|
||||
direction: horizontal
|
||||
subFields:
|
||||
- name: shownValue
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: Angezeigter Wert
|
||||
en: Displayed value
|
||||
containerProps:
|
||||
layout:
|
||||
size:
|
||||
default: "col-6"
|
||||
small: "col-12"
|
||||
large: "col-6"
|
||||
|
||||
- name: value
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: per Email gesendeter Wert
|
||||
en: Value sent by email
|
||||
containerProps:
|
||||
layout:
|
||||
size:
|
||||
default: "col-6"
|
||||
small: "col-12"
|
||||
large: "col-6"
|
||||
|
||||
- name: defaultValue
|
||||
type: boolean
|
||||
meta:
|
||||
label:
|
||||
de: Standardwert
|
||||
en: Default value
|
||||
helperText:
|
||||
de: "Wird dieser Wert ausgewählt, wird er als Standardwert gesetzt"
|
||||
en: "If this value is selected, it will be set as the default value"
|
||||
@@ -1,52 +0,0 @@
|
||||
name: textInput
|
||||
type: object[]
|
||||
meta:
|
||||
label: Textfeld
|
||||
addElementLabel: Textfeld hinzufügen
|
||||
dependsOn:
|
||||
eval: $parent?.inputWidgets?.includes('text')
|
||||
subFields:
|
||||
- name: standardInputProperties
|
||||
type: object
|
||||
meta:
|
||||
widget: containerLessObject
|
||||
subFields: !include standardInputProperties.yml
|
||||
|
||||
- name: textArea
|
||||
type: boolean
|
||||
meta:
|
||||
label:
|
||||
de: Großes Textfeld
|
||||
en: Large text field
|
||||
containerProps:
|
||||
layout:
|
||||
size:
|
||||
default: "col-4"
|
||||
small: "col-6"
|
||||
large: "col-4"
|
||||
|
||||
- name: emailValidation
|
||||
type: boolean
|
||||
meta:
|
||||
label:
|
||||
de: E-Mail-Validierung
|
||||
en: E-mail validation
|
||||
containerProps:
|
||||
layout:
|
||||
size:
|
||||
default: "col-4"
|
||||
small: "col-6"
|
||||
large: "col-4"
|
||||
|
||||
- name: telValidation
|
||||
type: boolean
|
||||
meta:
|
||||
label:
|
||||
de: Telefon-Validierung
|
||||
en: Phone validation
|
||||
containerProps:
|
||||
layout:
|
||||
size:
|
||||
default: "col-4"
|
||||
small: "col-6"
|
||||
large: "col-4"
|
||||
@@ -1,45 +0,0 @@
|
||||
name: timeSelect
|
||||
type: object
|
||||
meta:
|
||||
label: Zeitenwahl
|
||||
dependsOn:
|
||||
eval: $parent?.inputWidgets?.includes('timeSelect')
|
||||
subFields:
|
||||
- name: standardInputProperties
|
||||
type: object
|
||||
meta:
|
||||
widget: containerLessObject
|
||||
subFields: !include standardInputProperties.yml
|
||||
|
||||
- name: selectEntries
|
||||
type: object[]
|
||||
meta:
|
||||
label: Auswahleingabe
|
||||
widget: containerLessObjectArray
|
||||
helperText: "Die Angaben werden in folgendes Übersetzt: Anfangspunkt - Endpunkt"
|
||||
direction: horizontal
|
||||
|
||||
subFields:
|
||||
- name: leftSide
|
||||
type: string
|
||||
meta:
|
||||
label: Anfangspunkt
|
||||
helperText: Bspw. 14:30
|
||||
containerProps:
|
||||
layout:
|
||||
size:
|
||||
default: "col-6"
|
||||
small: "col-12"
|
||||
large: "col-6"
|
||||
|
||||
- name: rightSide
|
||||
type: string
|
||||
meta:
|
||||
label: Endpunkt
|
||||
helperText: Bspw. 15:30
|
||||
containerProps:
|
||||
layout:
|
||||
size:
|
||||
default: "col-6"
|
||||
small: "col-12"
|
||||
large: "col-6"
|
||||
@@ -1,60 +0,0 @@
|
||||
name: timesInput
|
||||
type: object
|
||||
meta:
|
||||
label:
|
||||
de: Zeitenauswahlfeld
|
||||
en: Time input
|
||||
dependsOn:
|
||||
eval: $parent?.inputWidgets?.includes('times')
|
||||
helperText:
|
||||
de: Selectfeld mit von - bis Angabe
|
||||
en: Select field with from - to specification
|
||||
|
||||
subFields:
|
||||
- name: times
|
||||
type: object[]
|
||||
meta:
|
||||
label:
|
||||
de: Zeitenauswahl Möglichkeiten
|
||||
en: Time selection options
|
||||
helperText:
|
||||
de: "Die Angaben werden in folgendes Übersetzt: Anfangspunkt - Endpunkt"
|
||||
en: "The information is translated into the following: starting point - end point"
|
||||
direction: horizontal
|
||||
widget: containerLessObjectArray
|
||||
|
||||
subFields:
|
||||
- name: from
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: Anfangspunkt
|
||||
en: Starting point
|
||||
helperText:
|
||||
de: Bspw. 14:30
|
||||
en: E.g. 14:30
|
||||
containerProps:
|
||||
layout:
|
||||
size:
|
||||
default: "col-6"
|
||||
small: "col-12"
|
||||
large: "col-6"
|
||||
|
||||
- name: to
|
||||
type: string
|
||||
meta:
|
||||
label: Endpunkt
|
||||
helperText:
|
||||
de: Bspw. 15:30
|
||||
en: E.g. 15:30
|
||||
containerProps:
|
||||
layout:
|
||||
size:
|
||||
default: "col-6"
|
||||
small: "col-12"
|
||||
large: "col-6"
|
||||
- name: standardInputProperties
|
||||
type: object
|
||||
meta:
|
||||
widget: containerLessObject
|
||||
subFields: !include standardInputProperties.yml
|
||||
@@ -1,12 +0,0 @@
|
||||
- type: table
|
||||
columns:
|
||||
- source: status
|
||||
filter: true
|
||||
- source: customerTibiId
|
||||
filter:
|
||||
type: foreignKey
|
||||
- source: insertTime
|
||||
filter: true
|
||||
- source: products.tibiId
|
||||
filter:
|
||||
type: foreignKey
|
||||
@@ -1,143 +0,0 @@
|
||||
name: helpCenterChapter
|
||||
|
||||
# Metaangaben zur Kollektion welche in der Admin-UI verwendet werden können
|
||||
meta:
|
||||
openapi:
|
||||
disabled: true
|
||||
# Navigationseintrag in der Admin-UI
|
||||
label:
|
||||
de: Helpcenter Kapitel
|
||||
en: Helpcenter Chapter
|
||||
backup:
|
||||
active: true
|
||||
collectionName: backups
|
||||
# Icon (Material UI) für den Navigationseintrag
|
||||
muiIcon: web
|
||||
|
||||
tablist:
|
||||
activeTab: general
|
||||
tabs:
|
||||
- name: general
|
||||
label:
|
||||
de: Allgemein
|
||||
en: General
|
||||
subFields:
|
||||
- source: title
|
||||
- source: slug
|
||||
- source: brightIcon
|
||||
- source: darkIcon
|
||||
- name: questions
|
||||
label:
|
||||
de: Fragen
|
||||
en: Questions
|
||||
subFields:
|
||||
- source: questions
|
||||
views:
|
||||
# Desktop
|
||||
- type: table
|
||||
mediaQuery: "(min-width:600px)"
|
||||
columns:
|
||||
- source: title
|
||||
name: Titel
|
||||
filter: true
|
||||
- source: slug
|
||||
name: Slug
|
||||
filter: true
|
||||
|
||||
permissions:
|
||||
public:
|
||||
methods:
|
||||
get: true
|
||||
post: false
|
||||
put: false
|
||||
delete: false
|
||||
|
||||
user:
|
||||
methods:
|
||||
get: true
|
||||
post: true
|
||||
put: true
|
||||
delete: true
|
||||
|
||||
# Feldliste der Kollektion
|
||||
fields:
|
||||
- name: title
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: Titel
|
||||
en: Title
|
||||
- name: slug
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: Slug
|
||||
en: Slug
|
||||
- name: brightIcon
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: Icon Hell
|
||||
en: Icon Bright
|
||||
widget: foreignKey
|
||||
foreign:
|
||||
collection: medialib
|
||||
id: id
|
||||
subNavigation: 0
|
||||
render:
|
||||
defaultCollectionViews: true
|
||||
containerProps:
|
||||
layout:
|
||||
size:
|
||||
default: "col-6"
|
||||
small: "col-12"
|
||||
large: "col-6"
|
||||
- name: darkIcon
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: Icon Dunkel
|
||||
en: Icon Dark
|
||||
widget: foreignKey
|
||||
foreign:
|
||||
collection: medialib
|
||||
id: id
|
||||
subNavigation: 0
|
||||
render:
|
||||
defaultCollectionViews: true
|
||||
containerProps:
|
||||
layout:
|
||||
size:
|
||||
default: "col-6"
|
||||
small: "col-12"
|
||||
large: "col-6"
|
||||
- name: questions
|
||||
type: object[]
|
||||
meta:
|
||||
label:
|
||||
de: Fragen
|
||||
en: Questions
|
||||
widget: containerLessObjectArray
|
||||
subFields:
|
||||
- name: priority
|
||||
type: boolean
|
||||
meta:
|
||||
label:
|
||||
de: Priorität
|
||||
en: Priority
|
||||
helperText:
|
||||
de: ob sie direkt ganz oben angezeigt werden soll
|
||||
en: if it should be displayed directly at the top
|
||||
- name: page
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: Seite
|
||||
en: Page
|
||||
widget: foreignKey
|
||||
foreign:
|
||||
collection: content
|
||||
id: path
|
||||
subNavigation: 0
|
||||
render:
|
||||
defaultCollectionViews: true
|
||||
@@ -1,61 +0,0 @@
|
||||
# no data in collection, only for post hook
|
||||
name: login
|
||||
uploadPath: ../media/login
|
||||
|
||||
# Metaangaben zur Kollektion welche in der Admin-UI verwendet werden können
|
||||
meta:
|
||||
openapi:
|
||||
disabled: true
|
||||
hideInNavigation: true
|
||||
# Navigationseintrag in der Admin-UI
|
||||
label: { de: "Login", en: "Login" }
|
||||
# Icon (Material UI) für den Navigationseintrag
|
||||
muiIcon: web
|
||||
# Identifizierung eines Eintrags für z.B. Select-Boxen in der Admin-UI
|
||||
rowIdentTpl: { twig: "{{ id }}" }
|
||||
# Standardsortierung der Liste
|
||||
defaultSort: { field: "id", order: "ASC" }
|
||||
# Admin-Backend Ansichten
|
||||
defaultImageFilter: s
|
||||
views:
|
||||
# Mobile Darstellung
|
||||
- type: simpleList
|
||||
mediaQuery: "(max-width:599px)"
|
||||
primaryText: id
|
||||
|
||||
# Desktop
|
||||
- type: table
|
||||
mediaQuery: "(min-width:600px)"
|
||||
columns:
|
||||
- id
|
||||
|
||||
# Zugriff auf diese Kollektion
|
||||
permissions:
|
||||
# öffentlicher Zugriff
|
||||
public:
|
||||
methods:
|
||||
# Liste und Einzeleinträge lesen
|
||||
get: false
|
||||
# neuen Eintrag anlegen
|
||||
post: true
|
||||
# Eintrag editieren
|
||||
put: false
|
||||
# Eintrag löschen
|
||||
delete: false
|
||||
# zum Projekt zugeordneter Benutzer ohne Zusatzberechtigungen
|
||||
user:
|
||||
methods:
|
||||
get: true
|
||||
post: true
|
||||
put: true
|
||||
delete: true
|
||||
|
||||
hooks:
|
||||
post:
|
||||
# check and return jwt
|
||||
create:
|
||||
type: javascript
|
||||
file: hooks/login/post_create.js
|
||||
|
||||
# Feldliste der Kollektion
|
||||
fields: []
|
||||
@@ -1,24 +0,0 @@
|
||||
name: lookCombination
|
||||
meta:
|
||||
allowExportAll: true
|
||||
label:
|
||||
de: Styling Kombinationen
|
||||
en: Look Combination
|
||||
muiIcon: label
|
||||
views: []
|
||||
|
||||
permissions:
|
||||
public:
|
||||
methods:
|
||||
get: true
|
||||
post: false
|
||||
put: false
|
||||
delete: false
|
||||
user:
|
||||
methods:
|
||||
get: true
|
||||
post: true
|
||||
put: true
|
||||
delete: true
|
||||
|
||||
fields: []
|
||||
@@ -1,75 +0,0 @@
|
||||
name: productBackgroundImage
|
||||
|
||||
meta:
|
||||
allowExportAll: true
|
||||
label:
|
||||
de: Produkt Bg.
|
||||
en: Product Bg.
|
||||
muiIcon: label
|
||||
|
||||
defaultSort:
|
||||
field: name
|
||||
order: ASC
|
||||
|
||||
views: &views
|
||||
- type: table
|
||||
columns:
|
||||
- source: type
|
||||
filter: true
|
||||
- source: image
|
||||
filter:
|
||||
type: foreignKey
|
||||
|
||||
permissions:
|
||||
public:
|
||||
methods:
|
||||
get: true
|
||||
post: false
|
||||
put: false
|
||||
delete: false
|
||||
user:
|
||||
methods:
|
||||
get: true
|
||||
post: true
|
||||
put: true
|
||||
delete: true
|
||||
|
||||
fields:
|
||||
- name: type
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: Typ
|
||||
en: Type
|
||||
widget: select
|
||||
choices:
|
||||
- id: standard
|
||||
label:
|
||||
de: Standard
|
||||
en: Standard
|
||||
|
||||
- id: dark
|
||||
label:
|
||||
de: Dunkel
|
||||
en: Dark
|
||||
- name: image
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: Tibi ID
|
||||
en: Tibi ID
|
||||
widget: foreignKey
|
||||
foreign:
|
||||
collection: medialib
|
||||
id: id
|
||||
subNavigation: 0
|
||||
render:
|
||||
defaultCollectionViews: true
|
||||
filter:
|
||||
type: foreignKey
|
||||
containerProps:
|
||||
layout:
|
||||
size:
|
||||
default: "col-6"
|
||||
small: "col-12"
|
||||
large: "col-6"
|
||||
@@ -1,54 +0,0 @@
|
||||
name: productBenefit
|
||||
|
||||
meta:
|
||||
allowExportAll: true
|
||||
label:
|
||||
de: Product Vorteile
|
||||
en: Product Benefits
|
||||
muiIcon: label
|
||||
|
||||
defaultSort:
|
||||
field: name
|
||||
order: ASC
|
||||
|
||||
views: &views
|
||||
- type: table
|
||||
columns:
|
||||
- source: title
|
||||
label:
|
||||
de: Titel
|
||||
en: Title
|
||||
- source: description
|
||||
label:
|
||||
de: Beschreibung
|
||||
en: Description
|
||||
|
||||
permissions:
|
||||
public:
|
||||
methods:
|
||||
get: true
|
||||
post: false
|
||||
put: false
|
||||
delete: false
|
||||
user:
|
||||
methods:
|
||||
get: true
|
||||
post: true
|
||||
put: true
|
||||
delete: true
|
||||
|
||||
fields:
|
||||
- name: title
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: Titel
|
||||
en: Title
|
||||
|
||||
- name: description
|
||||
type: string
|
||||
meta:
|
||||
widget: richtext
|
||||
label:
|
||||
de: Beschreibung
|
||||
en: Description
|
||||
@@ -1,301 +0,0 @@
|
||||
name: rating
|
||||
|
||||
meta:
|
||||
label: { de: "Bewertungen", en: "Ratings" }
|
||||
muiIcon: reviews
|
||||
views: &views
|
||||
- type: simpleList
|
||||
mediaQuery: "max-width: 600px"
|
||||
- type: table
|
||||
columns:
|
||||
- source: bigCommerceProductId
|
||||
filter:
|
||||
type: foreignKey
|
||||
|
||||
- source: bigcommerceOrderId
|
||||
filter:
|
||||
type: foreignKey
|
||||
|
||||
- source: rating.overall
|
||||
filter: true
|
||||
|
||||
- source: status
|
||||
filter: true
|
||||
|
||||
tablist:
|
||||
activeTab: generalDetails
|
||||
tabs:
|
||||
- name: generalDetails
|
||||
label:
|
||||
de: Allgemeine Details
|
||||
en: General Details
|
||||
subFields:
|
||||
- source: bigCommerceProductId
|
||||
- source: bigcommerceOrderId
|
||||
- source: status
|
||||
- source: review_date
|
||||
- source: bigcommerceReviewId
|
||||
|
||||
- name: rating
|
||||
label:
|
||||
de: Bewertung
|
||||
en: Rating
|
||||
subFields:
|
||||
- source: rating
|
||||
- source: comment
|
||||
- source: title
|
||||
|
||||
subNavigation:
|
||||
- name: modal
|
||||
views: *views
|
||||
|
||||
defaultCallback: # Standard-Callback-Funktion, die ausgeführt wird, wenn keine andere spezifiziert ist.
|
||||
eval: | # Der Code wird als JavaScript evaluiert.
|
||||
//js
|
||||
(entry) => { // Diese Funktion nimmt den Eintrag (entry) als Argument.
|
||||
parent.selectEntry(entry) // Die Funktion selectEntry auf dem übergeordneten Objekt wird mit dem Eintrag als Argument aufgerufen.
|
||||
}
|
||||
//!js
|
||||
permissions:
|
||||
public:
|
||||
methods:
|
||||
get: true
|
||||
post: true
|
||||
put: true
|
||||
delete: false
|
||||
user:
|
||||
methods:
|
||||
get: true
|
||||
post: true
|
||||
put: true
|
||||
delete: false
|
||||
|
||||
hooks:
|
||||
delete:
|
||||
delete:
|
||||
type: javascript
|
||||
file: hooks/rating/delete_delete.js
|
||||
|
||||
return:
|
||||
type: javascript
|
||||
file: hooks/rating/delete_return.js
|
||||
get:
|
||||
read:
|
||||
type: javascript
|
||||
file: hooks/rating/get_read.js
|
||||
|
||||
post:
|
||||
validate:
|
||||
type: javascript
|
||||
file: hooks/rating/post_validate.js
|
||||
create:
|
||||
type: javascript
|
||||
file: hooks/rating/post_create.js
|
||||
|
||||
return:
|
||||
file: hooks/rating/post_return.js
|
||||
type: javascript
|
||||
|
||||
put:
|
||||
validate:
|
||||
type: javascript
|
||||
file: hooks/rating/put_validate.js
|
||||
update:
|
||||
type: javascript
|
||||
file: hooks/rating/put_update.js
|
||||
return:
|
||||
file: hooks/rating/put_return.js
|
||||
type: javascript
|
||||
|
||||
fields:
|
||||
- name: bigcommerceOrderId
|
||||
type: number
|
||||
index: [single]
|
||||
meta:
|
||||
filter:
|
||||
type: foreignKey
|
||||
label: { de: "Interne Bestellnummer", en: "Internal Order Id" }
|
||||
widget: foreignKey
|
||||
foreign:
|
||||
collection: bigCommerceOrder
|
||||
subNavigation: 0
|
||||
id: bigCommerceId
|
||||
render:
|
||||
defaultCollectionViews: true
|
||||
|
||||
validator:
|
||||
required: true
|
||||
|
||||
- name: bigCommerceProductId
|
||||
index: [single]
|
||||
type: number
|
||||
meta:
|
||||
filter:
|
||||
type: foreignKey
|
||||
label: { de: "Produkt", en: "Product" }
|
||||
widget: foreignKey
|
||||
foreign:
|
||||
collection: bigCommerceProduct
|
||||
subNavigation: 0
|
||||
id: bigCommerceId
|
||||
render:
|
||||
defaultCollectionViews: true
|
||||
|
||||
validator:
|
||||
required: true
|
||||
|
||||
- name: rating
|
||||
index: [single]
|
||||
type: object
|
||||
meta:
|
||||
widget: containerLessObject
|
||||
label: { de: "Bewertung", en: "Rating" }
|
||||
|
||||
subFields:
|
||||
- name: quality
|
||||
type: number
|
||||
meta:
|
||||
label: { de: "Qualität", en: "Quality" }
|
||||
helperText: { de: "1 - Schlecht; 5 - Sehr Gut", en: "1 - Bad; 5 - Very Good" }
|
||||
validator:
|
||||
eval: |
|
||||
(function () {
|
||||
return $this >= 0 && $this <= 5;
|
||||
})()
|
||||
containerProps:
|
||||
layout:
|
||||
size:
|
||||
default: "col-6"
|
||||
small: "col-12"
|
||||
large: "col-4"
|
||||
|
||||
- name: priceQualityRatio
|
||||
type: number
|
||||
meta:
|
||||
label: { de: "Preis-Leistungs-Verhältnis", en: "Price-Quality Ratio" }
|
||||
helperText: { de: "1 - Schlecht; 5 - Sehr Gut", en: "1 - Bad; 5 - Very Good" }
|
||||
validator:
|
||||
eval: |
|
||||
(function () {
|
||||
return $this >= 0 && $this <= 5;
|
||||
})()
|
||||
containerProps:
|
||||
layout:
|
||||
size:
|
||||
default: "col-6"
|
||||
small: "col-12"
|
||||
large: "col-4"
|
||||
|
||||
- name: comfort
|
||||
type: number
|
||||
meta:
|
||||
label: { de: "Komfort", en: "Comfort" }
|
||||
helperText: { de: "1 - Unbequem; 5 - Sehr Bequem", en: "1 - Uncomfortable; 5 - Very Comfortable" }
|
||||
validator:
|
||||
eval: |
|
||||
(function () {
|
||||
return $this >= 0 && $this <= 5;
|
||||
})()
|
||||
containerProps:
|
||||
layout:
|
||||
size:
|
||||
default: "col-6"
|
||||
small: "col-12"
|
||||
large: "col-4"
|
||||
|
||||
- name: overall
|
||||
type: number
|
||||
meta:
|
||||
label: { de: "Gesamt", en: "Overall" }
|
||||
helperText: { de: "1 - Schlecht; 5 - Sehr Gut", en: "1 - Bad; 5 - Very Good" }
|
||||
validator:
|
||||
eval: |
|
||||
(function () {
|
||||
return $this >= 0 && $this <= 5;
|
||||
})()
|
||||
containerProps:
|
||||
layout:
|
||||
size:
|
||||
default: "col-6"
|
||||
small: "col-12"
|
||||
large: "col-4"
|
||||
|
||||
- name: comment
|
||||
type: string
|
||||
meta:
|
||||
label: { de: "Kommentar", en: "Comment" }
|
||||
containerProps:
|
||||
layout:
|
||||
size:
|
||||
default: "col-6"
|
||||
small: "col-12"
|
||||
large: "col-6"
|
||||
inputProps:
|
||||
multiline: true
|
||||
|
||||
- name: title
|
||||
type: string
|
||||
meta:
|
||||
label: { de: "Titel", en: "title" }
|
||||
containerProps:
|
||||
layout:
|
||||
size:
|
||||
default: "col-6"
|
||||
small: "col-12"
|
||||
large: "col-6"
|
||||
inputProps:
|
||||
multiline: true
|
||||
|
||||
- name: review_date
|
||||
type: date
|
||||
validator:
|
||||
eval: |
|
||||
(function () {
|
||||
return (new Date($this) !== "Invalid Date") && !isNaN(new Date($this));
|
||||
})()
|
||||
meta:
|
||||
label: { de: "Erstellungsdatum", en: "Creation date" }
|
||||
defaultValue:
|
||||
eval: new Date()
|
||||
containerProps:
|
||||
layout:
|
||||
size:
|
||||
default: "col-6"
|
||||
small: "col-12"
|
||||
large: "col-6"
|
||||
|
||||
- name: status
|
||||
type: string
|
||||
index: [single]
|
||||
validator:
|
||||
eval: (["pending", "approved", "rejected"].includes($this))
|
||||
meta:
|
||||
label: Status
|
||||
widget: "select"
|
||||
defaultValue: pending
|
||||
choices:
|
||||
- name: { de: wartend, en: pending }
|
||||
id: pending
|
||||
- name: { de: bestätigt, en: approved }
|
||||
id: approved
|
||||
- name: { de: abgelehnt, en: rejected }
|
||||
id: rejected
|
||||
containerProps:
|
||||
layout:
|
||||
size:
|
||||
default: "col-6"
|
||||
small: "col-12"
|
||||
large: "col-6"
|
||||
- name: bigcommerceReviewId
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: "BigCommerce Review Id"
|
||||
en: "BigCommerce Review Id"
|
||||
helperText:
|
||||
de: "Die ID der Bewertung in BigCommerce"
|
||||
en: "The ID of the review in BigCommerce"
|
||||
indexes:
|
||||
- name: fulltext # Ein eindeutiger Name für den Index. Es ist optional, wird jedoch empfohlen, um den Index später leicht identifizieren zu können.
|
||||
key: # Bestimmt, auf welche Felder der Index angewendet werden soll. Dies kann ein einfacher String sein, wenn der Index nur ein Feld umfasst, oder ein Array von Strings, wenn der Index mehrere Felder umfasst.
|
||||
- $text:$**
|
||||
@@ -1,196 +0,0 @@
|
||||
name: selfImprovementChapter
|
||||
|
||||
meta:
|
||||
allowExportAll: true
|
||||
label:
|
||||
de: SelfImp. Kapitel
|
||||
en: SelfImp. Chapter
|
||||
muiIcon: label
|
||||
backup:
|
||||
active: true
|
||||
collectionName: backups
|
||||
defaultSort:
|
||||
field: name
|
||||
order: ASC
|
||||
|
||||
views: &views
|
||||
- type: table
|
||||
columns:
|
||||
- source: title
|
||||
label:
|
||||
de: Titel
|
||||
en: Title
|
||||
filter: true
|
||||
- source: shortDescription
|
||||
label:
|
||||
de: Kurzbeschreibung
|
||||
en: Short Description
|
||||
filter: true
|
||||
tabs:
|
||||
activeTab: generalDetails
|
||||
tabs:
|
||||
- name: generalDetails
|
||||
label:
|
||||
de: Allgemeine Details
|
||||
en: General Details
|
||||
subFields:
|
||||
- source: title
|
||||
- source: alias
|
||||
- source: previewVideo
|
||||
- source: previewImage
|
||||
- source: locked
|
||||
|
||||
- name: homepage
|
||||
label:
|
||||
de: Homepage
|
||||
en: Homepage
|
||||
subFields:
|
||||
- source: shortDescription
|
||||
- source: description
|
||||
|
||||
subNavigation:
|
||||
- name: modalForeign
|
||||
defaultSort:
|
||||
field: name
|
||||
order: ASC
|
||||
views: *views
|
||||
|
||||
defaultCallback:
|
||||
eval: |
|
||||
(entry) => {
|
||||
parent.selectEntry(entry)
|
||||
}
|
||||
|
||||
permissions:
|
||||
public:
|
||||
methods:
|
||||
get: true
|
||||
post: false
|
||||
put: false
|
||||
delete: false
|
||||
user:
|
||||
methods:
|
||||
get: true
|
||||
post: true
|
||||
put: true
|
||||
delete: true
|
||||
|
||||
fields:
|
||||
- name: type
|
||||
type: number
|
||||
meta:
|
||||
label:
|
||||
de: Typ
|
||||
en: Type
|
||||
widget: select
|
||||
choices:
|
||||
- name: Krass Kraft
|
||||
id: 1
|
||||
- name: Crazy Calm
|
||||
id: 2
|
||||
- name: Crazy Crave Control
|
||||
id: 3
|
||||
- name: Krass Kreativ
|
||||
id: 4
|
||||
containerProps:
|
||||
layout:
|
||||
size:
|
||||
default: "col-6"
|
||||
small: "col-12"
|
||||
large: "col-6"
|
||||
- name: title
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: Titel
|
||||
en: Title
|
||||
containerProps:
|
||||
layout:
|
||||
size:
|
||||
default: "col-6"
|
||||
small: "col-12"
|
||||
large: "col-6"
|
||||
|
||||
- name: alias
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: Alias
|
||||
en: Alias
|
||||
containerProps:
|
||||
layout:
|
||||
size:
|
||||
default: "col-6"
|
||||
small: "col-12"
|
||||
large: "col-6"
|
||||
|
||||
- name: shortDescription
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: Kurze Beschreibung
|
||||
en: Short Description
|
||||
containerProps:
|
||||
layout:
|
||||
size:
|
||||
default: "col-6"
|
||||
small: "col-12"
|
||||
large: "col-6"
|
||||
|
||||
- name: description
|
||||
type: string
|
||||
meta:
|
||||
widget: richtext
|
||||
label:
|
||||
de: Beschreibung
|
||||
en: Description
|
||||
containerProps:
|
||||
layout:
|
||||
size:
|
||||
default: "col-6"
|
||||
small: "col-12"
|
||||
large: "col-6"
|
||||
|
||||
- name: previewVideo
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: Vorschau Video
|
||||
en: Preview Video
|
||||
widget: foreignKey
|
||||
foreign:
|
||||
collection: medialib
|
||||
id: id
|
||||
subNavigation: 0
|
||||
render:
|
||||
defaultCollectionViews: true
|
||||
|
||||
- name: previewImage
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: Vorschau Bild
|
||||
en: Preview Image
|
||||
widget: foreignKey
|
||||
foreign:
|
||||
collection: medialib
|
||||
id: id
|
||||
subNavigation: 0
|
||||
render:
|
||||
defaultCollectionViews: true
|
||||
|
||||
- name: locked
|
||||
type: boolean
|
||||
meta:
|
||||
label:
|
||||
de: Gesperrt
|
||||
en: Locked
|
||||
helperText:
|
||||
de: Wenn gesperrt, kann das Kapitel nicht geöffnet werden.
|
||||
en: If locked, the chapter cannot be opened.
|
||||
containerProps:
|
||||
layout:
|
||||
size:
|
||||
default: "col-6"
|
||||
small: "col-12"
|
||||
large: "col-6"
|
||||
@@ -1,74 +0,0 @@
|
||||
name: shopStatus
|
||||
|
||||
# Metaangaben zur Kollektion welche in der Admin-UI verwendet werden können
|
||||
meta:
|
||||
openapi:
|
||||
disabled: true
|
||||
# Navigationseintrag in der Admin-UI
|
||||
backup:
|
||||
active: true
|
||||
collectionName: backups
|
||||
label:
|
||||
de: Shop Status
|
||||
en: Shop Status
|
||||
# Icon (Material UI) für den Navigationseintrag
|
||||
muiIcon: web
|
||||
views:
|
||||
# Mobile Darstellung
|
||||
- type: simpleList
|
||||
mediaQuery: "(max-width:599px)"
|
||||
primaryText: status
|
||||
secondaryText: password
|
||||
|
||||
# Desktop
|
||||
- type: table
|
||||
mediaQuery: "(min-width:600px)"
|
||||
columns:
|
||||
- source: status
|
||||
label:
|
||||
de: Status
|
||||
en: Status
|
||||
filter: true
|
||||
- source: password
|
||||
label:
|
||||
de: Passwort
|
||||
en: Password
|
||||
filter: true
|
||||
|
||||
permissions:
|
||||
# öffentlicher Zugriff
|
||||
public:
|
||||
methods:
|
||||
get: true
|
||||
post: false
|
||||
put: false
|
||||
delete: false
|
||||
|
||||
user:
|
||||
methods:
|
||||
get: true
|
||||
post: true
|
||||
put: true
|
||||
delete: true
|
||||
|
||||
# Feldliste der Kollektion
|
||||
fields:
|
||||
- name: status
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: Status
|
||||
en: Status
|
||||
widget: select
|
||||
choices:
|
||||
- name: open
|
||||
id: open
|
||||
|
||||
- name: login
|
||||
id: login
|
||||
- name: password
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: Passwort
|
||||
en: Password
|
||||
@@ -1,24 +0,0 @@
|
||||
name: sizingDetails
|
||||
meta:
|
||||
allowExportAll: true
|
||||
label:
|
||||
de: Größen Details
|
||||
en: Sizing Details
|
||||
muiIcon: label
|
||||
views: []
|
||||
|
||||
permissions:
|
||||
public:
|
||||
methods:
|
||||
get: true
|
||||
post: false
|
||||
put: false
|
||||
delete: false
|
||||
user:
|
||||
methods:
|
||||
get: true
|
||||
post: true
|
||||
put: true
|
||||
delete: true
|
||||
|
||||
fields: []
|
||||
@@ -1,92 +0,0 @@
|
||||
########################################################################
|
||||
# Webhooks
|
||||
########################################################################
|
||||
|
||||
name: webhook
|
||||
meta:
|
||||
openapi:
|
||||
disabled: true
|
||||
label: { de: "Webhooks", en: "webhooks" }
|
||||
muiIcon: http
|
||||
rowIdentTpl: { twig: "{{ insertTime }} - {{ type }}" }
|
||||
views:
|
||||
- type: simpleList
|
||||
mediaQuery: "(max-width: 600px)"
|
||||
primaryText: type
|
||||
secondaryText: scope
|
||||
- type: table
|
||||
columns:
|
||||
- type
|
||||
- scope
|
||||
- active
|
||||
|
||||
permissions:
|
||||
public:
|
||||
methods:
|
||||
get: false
|
||||
post: true
|
||||
put: false
|
||||
delete: false
|
||||
|
||||
user:
|
||||
methods:
|
||||
get: true
|
||||
post: true
|
||||
put: false
|
||||
delete: false
|
||||
|
||||
"token:${BIGCOMMERCE_WEBHOOK_TOKEN}":
|
||||
methods:
|
||||
get: true
|
||||
post: true
|
||||
put: false
|
||||
delete: false
|
||||
|
||||
hooks:
|
||||
post:
|
||||
create:
|
||||
type: javascript
|
||||
file: hooks/webhook/post_create.js
|
||||
put:
|
||||
update:
|
||||
type: javascript
|
||||
file: hooks/webhook/put_update.js
|
||||
delete:
|
||||
delete:
|
||||
type: javascript
|
||||
file: hooks/webhook/delete_delete.js
|
||||
|
||||
fields:
|
||||
- name: type
|
||||
type: string
|
||||
meta:
|
||||
label: { de: "Typ", en: "Type" }
|
||||
widget: select
|
||||
choices:
|
||||
- id: "bigCommerce"
|
||||
name: "BigCommerce"
|
||||
- id: "printful"
|
||||
name: "Printful"
|
||||
- name: scope
|
||||
type: string
|
||||
meta:
|
||||
label: { de: "Scope", en: "Scope" }
|
||||
helperText:
|
||||
de: "The scope of the webhook, for example: store/product/*"
|
||||
en: "The scope of the webhook, for example: store/product/*"
|
||||
- name: active
|
||||
type: boolean
|
||||
meta:
|
||||
label: { de: "Aktiv", en: "Active" }
|
||||
helperText:
|
||||
de: "Aktiviert oder deaktiviert den Webhook"
|
||||
en: "Enables or disables the webhook"
|
||||
- name: webhookId
|
||||
type: number
|
||||
meta:
|
||||
label: { de: "Webhook ID", en: "Webhook ID" }
|
||||
helperText:
|
||||
de: "Die ID des Webhooks"
|
||||
en: "The ID of the webhook"
|
||||
inputProperties:
|
||||
disabled: true
|
||||
@@ -1,36 +0,0 @@
|
||||
########################################################################
|
||||
# cart
|
||||
########################################################################
|
||||
|
||||
name: dummyWishlistEntryEndpoint
|
||||
meta:
|
||||
hideInNavigation: true
|
||||
openapi:
|
||||
disabled: true
|
||||
|
||||
permissions:
|
||||
public:
|
||||
methods:
|
||||
get: true
|
||||
post: true
|
||||
put: false
|
||||
delete: false
|
||||
|
||||
user:
|
||||
methods:
|
||||
get: false
|
||||
post: false
|
||||
put: false
|
||||
delete: false
|
||||
|
||||
hooks:
|
||||
get:
|
||||
read:
|
||||
type: javascript
|
||||
file: hooks/wishlist/get_read.js
|
||||
post:
|
||||
validate:
|
||||
type: javascript
|
||||
file: hooks/wishlist/post_create.js
|
||||
|
||||
fields:
|
||||
@@ -8,48 +8,29 @@ meta:
|
||||
|
||||
dashboard: !include helper/dashboard.yml
|
||||
injectIntoHead:
|
||||
# inject font faces (not possible in shadow dom for preview)
|
||||
- eval: |
|
||||
"<link rel='stylesheet' href='" + $projectBase + "_/assets/variables/variables.css'>"
|
||||
- eval: |
|
||||
"<link rel='stylesheet' href='" + $projectBase + "_/assets/fonts/fonts.css'>"
|
||||
# Liste möglicher Berechtigungen, die Benutzern zugeordnet werden können
|
||||
permissions:
|
||||
- name: pages
|
||||
label:
|
||||
de: Seiten
|
||||
en: Pages
|
||||
|
||||
collections:
|
||||
- !include collections/bigCommerceOrder.yml
|
||||
- !include collections/OrderReturnRequest.yml
|
||||
- !include collections/OrderRevokeRequest.yml
|
||||
- !include collections/contact.yml
|
||||
- !include collections/rating.yml
|
||||
- !include collections/bigCommerceProduct.yml
|
||||
- !include collections/bigCommerceCustomer.yml
|
||||
- !include collections/QRCode.yml
|
||||
- !include collections/productBenefit.yml
|
||||
- !include collections/banner.yml
|
||||
- !include collections/content.yml
|
||||
- !include collections/selfImprovementChapter.yml
|
||||
- !include collections/Challenge.yml
|
||||
- !include collections/medialib.yml
|
||||
- !include collections/navigation.yml
|
||||
- !include collections/sizingDetails.yml
|
||||
- !include collections/lookCombination.yml
|
||||
- !include collections/tag.yml
|
||||
- !include collections/webhook.yml
|
||||
- !include collections/login.yml
|
||||
- !include collections/action.yml
|
||||
- !include collections/wishlistEntry.yml
|
||||
- !include collections/module.yml
|
||||
- !include collections/helpcenterChapter.yml
|
||||
- !include collections/shopStatus.yml
|
||||
- !include collections/productBackgroundImage.yml
|
||||
- !include collections/ssr.yml
|
||||
- !include collections/lighthouseSubpath.yml
|
||||
- !include collections/lighthouse.yml
|
||||
- !include collections/dummyCartEndpoint.yml
|
||||
- !include collections/backups.yml
|
||||
|
||||
jobs:
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
const { getCart, getRedirectUrl } = require("../lib/bigcommerceRestAPI")
|
||||
const { bigcommerceStoreHash, bigcommerceBaseURL, bigCommerceCliendId, bigCommerceClientSecret } = require("../config")
|
||||
const { getCustomerById, getLoginUrl } = require("../lib/bigcommerceRestAPI")
|
||||
const { withAccount } = require("../lib/utils")
|
||||
const { mapRestApiCartToGraphQL } = require("./helper")
|
||||
;(function () {
|
||||
if (context.user.auth()) return {}
|
||||
const checkout = context.request().query("checkout")
|
||||
const loggedInCheckout = context.request().query("loggedInCheckout")
|
||||
if (checkout) {
|
||||
if (loggedInCheckout) {
|
||||
withAccount((loginClaims) => {
|
||||
const cartId = context.request().param("id")
|
||||
const cart = getCart(cartId)
|
||||
const { checkoutURL } = getRedirectUrl(cartId)
|
||||
const loginUrl = getLoginUrl(
|
||||
loginClaims.bigCommerceId,
|
||||
bigcommerceStoreHash,
|
||||
bigcommerceBaseURL,
|
||||
bigCommerceCliendId,
|
||||
bigCommerceClientSecret,
|
||||
checkoutURL.split(bigcommerceBaseURL).pop()
|
||||
)
|
||||
throw {
|
||||
status: 200,
|
||||
data: cart && {
|
||||
cart: mapRestApiCartToGraphQL(cart),
|
||||
checkoutURL: loginUrl,
|
||||
},
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
const cartId = context.request().param("id")
|
||||
const cart = getCart(cartId)
|
||||
const { checkoutURL, embeddedCheckoutURL } = getRedirectUrl(cartId)
|
||||
|
||||
throw {
|
||||
status: 200,
|
||||
data: cart && {
|
||||
cart: mapRestApiCartToGraphQL(cart),
|
||||
checkoutURL: checkoutURL,
|
||||
embeddedCheckoutURL: embeddedCheckoutURL,
|
||||
},
|
||||
}
|
||||
})()
|
||||
@@ -1,53 +0,0 @@
|
||||
/**
|
||||
*
|
||||
* @param {RestApiCart} cart
|
||||
* @returns {BigCommerceCart}
|
||||
*/
|
||||
|
||||
function mapRestApiCartToGraphQL(cart) {
|
||||
return {
|
||||
entityId: cart.id,
|
||||
currencyCode: cart.currency.code,
|
||||
isTaxIncluded: cart.tax_included,
|
||||
baseAmount: { value: cart.base_amount, currencyCode: cart.currency.code },
|
||||
discountedAmount: { value: cart.discount_amount, currencyCode: cart.currency.code },
|
||||
amount: { value: cart.cart_amount, currencyCode: cart.currency.code },
|
||||
discounts: cart.discounts.map((d) => ({
|
||||
entityId: d.id,
|
||||
discountedAmount: { value: d.discounted_amount, currencyCode: cart.currency.code },
|
||||
})),
|
||||
lineItems: {
|
||||
physicalItems: cart.line_items.physical_items.map((item) => ({
|
||||
entityId: item.id,
|
||||
parentEntityId: cart.parent_id,
|
||||
productEntityId: item.product_id,
|
||||
variantEntityId: item.variant_id,
|
||||
sku: item.sku,
|
||||
name: item.name,
|
||||
url: item.url,
|
||||
imageUrl: item.image_url,
|
||||
brand: "BKDF", // REST API does not provide brand directly
|
||||
quantity: item.quantity,
|
||||
isTaxable: item.is_taxable,
|
||||
listPrice: { value: item.list_price, currencyCode: cart.currency.code },
|
||||
extendedListPrice: { value: item.extended_list_price, currencyCode: cart.currency.code },
|
||||
selectedOptions: item?.options?.map((option) => ({
|
||||
// Map each option to the GraphQL format
|
||||
// Assuming a simplified structure here for the example
|
||||
})),
|
||||
isShippingRequired: true, // Example assumption
|
||||
})),
|
||||
digitalItems: [],
|
||||
customItems: [],
|
||||
giftCertificates: [],
|
||||
totalQuantity: cart.line_items.physical_items.reduce((total, item) => total + item.quantity, 0),
|
||||
},
|
||||
createdAt: { utc: new Date(cart.created_time) },
|
||||
updatedAt: { utc: new Date(cart.updated_time) },
|
||||
locale: cart.locale,
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
mapRestApiCartToGraphQL,
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
const { addCartItem, updateCartItem, deleteCartItem, getCart } = require("../lib/bigcommerceRestAPI")
|
||||
const { postAddToCart } = require("../lib/facebookRestAPI")
|
||||
|
||||
;(function () {
|
||||
const operation = context?.data?.operation
|
||||
|
||||
if (operation == "add") {
|
||||
addCartItem(context?.data?.cartId, context?.data?.lineItems)
|
||||
try {
|
||||
postAddToCart()
|
||||
} catch (e) {}
|
||||
} else if (operation == "update") {
|
||||
updateCartItem(context?.data?.cartId, context?.data?.lineItem, context?.data?.entityId)
|
||||
try {
|
||||
postAddToCart()
|
||||
} catch (e) {}
|
||||
} else if (operation == "delete") {
|
||||
deleteCartItem(context?.data?.cartId, context?.data?.entityId)
|
||||
}
|
||||
|
||||
throw {
|
||||
status: 200,
|
||||
message: "success",
|
||||
}
|
||||
})()
|
||||
@@ -1,55 +0,0 @@
|
||||
const { getCustomerAddressById, getCustomerAddresses, getCustomerById } = require("../lib/bigcommerceRestAPI")
|
||||
const { withAccount } = require("../lib/utils")
|
||||
;(function () {
|
||||
console.log("wtf")
|
||||
if (context.user.auth()) return
|
||||
const request = context.request()
|
||||
const queryUsername = request.query("username")
|
||||
if (queryUsername) {
|
||||
const user = context.db.find("bigCommerceCustomer", {
|
||||
filter: { username: queryUsername },
|
||||
})[0]
|
||||
if (!user) {
|
||||
throw { status: 404, error: "customer not found" }
|
||||
}
|
||||
throw {
|
||||
data: {
|
||||
id: user.id,
|
||||
username: user.username,
|
||||
socialMediaAccounts: user.socialMediaAccounts,
|
||||
personalRecords: user.personalRecords,
|
||||
},
|
||||
status: 200,
|
||||
}
|
||||
}
|
||||
const foreign = request.query("foreign")
|
||||
if (foreign) {
|
||||
const id = request.param("id")
|
||||
const user = context.db.find("bigCommerceCustomer", {
|
||||
filter: { _id: id },
|
||||
})[0]
|
||||
if (!user) {
|
||||
throw { status: 404, error: "customer not found" }
|
||||
}
|
||||
throw {
|
||||
data: {
|
||||
id: user.id,
|
||||
username: user.username,
|
||||
socialMediaAccounts: user.socialMediaAccounts,
|
||||
personalRecords: user.personalRecords,
|
||||
},
|
||||
status: 200,
|
||||
}
|
||||
}
|
||||
withAccount((loginClaims) => {
|
||||
const queryAddressId = request.query("address")
|
||||
const queryAddresses = request.query("addresses")
|
||||
|
||||
if (queryAddressId) {
|
||||
throw { data: getCustomerAddressById(loginClaims.bigCommerceId, Number(queryAddressId)), status: 200 }
|
||||
} else if (queryAddresses) {
|
||||
throw { data: getCustomerAddresses(loginClaims.bigCommerceId), status: 200 }
|
||||
} else throw { data: getCustomerById(loginClaims.bigCommerceId), status: 200 }
|
||||
})
|
||||
throw { status: 401, error: "unauthorized", log: false }
|
||||
})()
|
||||
@@ -1,250 +0,0 @@
|
||||
const {
|
||||
updateCustomer,
|
||||
updateCustomerAddressById,
|
||||
deleteCustomerAddressById,
|
||||
addCustomerAddress,
|
||||
validateCredentials,
|
||||
getCustomerById,
|
||||
} = require("../lib/bigcommerceRestAPI")
|
||||
const { withAccount } = require("../lib/utils")
|
||||
const { getJwt } = require("../lib/utils")
|
||||
const emailRegex =
|
||||
/(?:[a-z0-9!#$%&'*+/=?^_`{|}~-]+(?:\.[a-z0-9!#$%&'*+/=?^_`{|}~-]+)*|"(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21\x23-\x5b\x5d-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])*")@(?:(?:[a-z0-9](?:[a-z0-9-]*[a-z0-9])?\.)+[a-z0-9](?:[a-z0-9-]*[a-z0-9])?|\[(?:(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9]))\.){3}(?:(2(5[0-5]|[0-4][0-9])|1[0-9][0-9]|[1-9]?[0-9])|[a-z0-9-]*[a-z0-9]:(?:[\x01-\x08\x0b\x0c\x0e-\x1f\x21-\x5a\x53-\x7f]|\\[\x01-\x09\x0b\x0c\x0e-\x7f])+)\])/
|
||||
;(function () {
|
||||
if (context.user.auth()) return
|
||||
let data = context.data
|
||||
const r = context.request()
|
||||
const customerId = r.param("id")
|
||||
|
||||
const backendAuth = context.user.auth()
|
||||
|
||||
let returnCustomer = undefined
|
||||
if (!backendAuth) {
|
||||
// require authorization header with jwt
|
||||
const token = getJwt(context)
|
||||
/** @type {JWTPwResetClaims} */ // @ts-ignore
|
||||
const pwResetClaims = token.claims
|
||||
/** @type {Customer} */ // @ts-ignore
|
||||
const customer = context.db.find("bigCommerceCustomer", {
|
||||
filter: { _id: pwResetClaims.tibiId },
|
||||
})[0]
|
||||
|
||||
if (!customer) {
|
||||
throw {
|
||||
status: 404,
|
||||
error: "customer not found",
|
||||
log: false,
|
||||
}
|
||||
}
|
||||
|
||||
if (data.operation === "resetPassword") {
|
||||
if (pwResetClaims && pwResetClaims.tibiId && pwResetClaims.check) {
|
||||
// is password reset token since it has .check
|
||||
if (pwResetClaims.tibiId != customerId)
|
||||
throw {
|
||||
status: 403,
|
||||
error: "token not valid for this id",
|
||||
log: false,
|
||||
}
|
||||
|
||||
if (pwResetClaims.check != customer.currentToken)
|
||||
throw {
|
||||
status: 403,
|
||||
error: "password reset token expired",
|
||||
log: false,
|
||||
}
|
||||
updateCustomer({
|
||||
authentication: {
|
||||
force_password_reset: false,
|
||||
new_password: data.password,
|
||||
},
|
||||
id: Number(customer.bigCommerceId),
|
||||
})
|
||||
|
||||
throw {
|
||||
status: 200,
|
||||
log: false,
|
||||
}
|
||||
} else {
|
||||
throw {
|
||||
status: 403,
|
||||
error: "invalid token",
|
||||
log: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
withAccount((loginClaims) => {
|
||||
/** @type {BigCommerceCustomer} */
|
||||
const customer = data.customer
|
||||
if (customer) customer.id = Number(loginClaims.bigCommerceId)
|
||||
if (data.operation === "updateCustomer") {
|
||||
customer.email = loginClaims.email
|
||||
const index = customer.form_fields.findIndex((f) => f.name === "username")
|
||||
const internalCustomer = context.db.find("bigCommerceCustomer", {
|
||||
filter: { email: customer.email },
|
||||
})[0]
|
||||
if (index >= 0) customer.form_fields[index].value = internalCustomer.username
|
||||
const response = updateCustomer(customer)
|
||||
|
||||
throw {
|
||||
status: 200,
|
||||
data: response,
|
||||
message: "customer updated",
|
||||
log: false,
|
||||
}
|
||||
} else if (data.operation == "updateInternalCustomer") {
|
||||
const customer = data.customer
|
||||
returnCustomer = customer
|
||||
} else if (data.operation === "updateCustomerAddress") {
|
||||
/** @type {BigCommerceAddress} */
|
||||
const address = data.address
|
||||
address.customer_id = Number(loginClaims.bigCommerceId)
|
||||
const response = updateCustomerAddressById(address)
|
||||
throw {
|
||||
status: 200,
|
||||
message: "customer addresses updated",
|
||||
data: response,
|
||||
log: false,
|
||||
}
|
||||
} else if (data.operation === "deleteCustomerAddress") {
|
||||
deleteCustomerAddressById(Number(loginClaims.bigCommerceId), data.addressId)
|
||||
throw {
|
||||
status: 200,
|
||||
message: "customer address deleted",
|
||||
data: {
|
||||
success: true,
|
||||
},
|
||||
log: false,
|
||||
}
|
||||
} else if (data.operation === "addCustomerAddress") {
|
||||
/** @type {BigCommerceAddress} */
|
||||
const address = data.address
|
||||
address.customer_id = Number(loginClaims.bigCommerceId)
|
||||
delete address.id
|
||||
const response = addCustomerAddress(address)
|
||||
throw {
|
||||
status: 200,
|
||||
message: "customer addresses updated",
|
||||
data: response,
|
||||
log: false,
|
||||
}
|
||||
} else if (data.operation === "changePassword") {
|
||||
const res = validateCredentials(loginClaims.email, data.currentPassword)
|
||||
if (!res.is_valid) {
|
||||
throw {
|
||||
status: 403,
|
||||
error: "current password incorrect",
|
||||
log: false,
|
||||
}
|
||||
}
|
||||
const resPw = updateCustomer({
|
||||
authentication: {
|
||||
force_password_reset: false,
|
||||
new_password: data.newPassword,
|
||||
},
|
||||
id: Number(loginClaims.bigCommerceId),
|
||||
})
|
||||
throw {
|
||||
status: 200,
|
||||
message: "password reset successful",
|
||||
log: false,
|
||||
}
|
||||
} else if (data.operation === "updateEmail") {
|
||||
const resValidation = validateCredentials(loginClaims.email, data.password)
|
||||
if (!resValidation.is_valid) {
|
||||
throw {
|
||||
status: 403,
|
||||
error: "password incorrect",
|
||||
log: false,
|
||||
}
|
||||
}
|
||||
if (!data.email)
|
||||
throw {
|
||||
status: 400,
|
||||
error: "email is required",
|
||||
log: false,
|
||||
}
|
||||
data.email = data.email.toLowerCase()
|
||||
const customers = context.db.find("bigCommerceCustomer", {
|
||||
filter: { email: data.email },
|
||||
})
|
||||
if (customers.length > 0)
|
||||
throw {
|
||||
status: 409,
|
||||
error: "email already in use",
|
||||
log: false,
|
||||
}
|
||||
if (emailRegex.test(data.email) === false) {
|
||||
throw {
|
||||
status: 400,
|
||||
error: "invalid email",
|
||||
log: false,
|
||||
}
|
||||
}
|
||||
const res = updateCustomer({ id: Number(loginClaims.bigCommerceId), email: data.email })
|
||||
context.db.update("bigCommerceCustomer", loginClaims.tibiId, { email: data.email })
|
||||
throw {
|
||||
status: 200,
|
||||
message: "customer updated",
|
||||
data: res,
|
||||
log: false,
|
||||
}
|
||||
} else if (data.operation === "updateUsername") {
|
||||
const resValidation = validateCredentials(loginClaims.email, data.password)
|
||||
if (!resValidation.is_valid) {
|
||||
throw {
|
||||
status: 403,
|
||||
error: "password incorrect",
|
||||
log: false,
|
||||
}
|
||||
}
|
||||
/**@type {string} */
|
||||
let username = data.username
|
||||
if (!username)
|
||||
throw {
|
||||
status: 400,
|
||||
error: "username is required",
|
||||
log: false,
|
||||
}
|
||||
username = username.toLowerCase()
|
||||
|
||||
const userWithUsername = context.db.find("bigCommerceCustomer", {
|
||||
filter: { username: username },
|
||||
})[0]
|
||||
if (userWithUsername)
|
||||
throw {
|
||||
status: 409,
|
||||
message: "username already in use",
|
||||
}
|
||||
const internalCustomer = context.db.find("bigCommerceCustomer", {
|
||||
filter: { email: loginClaims.email },
|
||||
})[0]
|
||||
internalCustomer.username = username
|
||||
if (internalCustomer) {
|
||||
context.db.update("bigCommerceCustomer", internalCustomer.id, internalCustomer)
|
||||
}
|
||||
|
||||
//update bigcommerce customer too : get it and then update it with username swapped
|
||||
const bCCustomer = getCustomerById(loginClaims.bigCommerceId)
|
||||
if (!bCCustomer.form_fields) bCCustomer.form_fields = []
|
||||
const usernameIndex = bCCustomer.form_fields.findIndex((f) => f.name === "username")
|
||||
if (usernameIndex >= 0) bCCustomer.form_fields[usernameIndex].value = username
|
||||
else bCCustomer.form_fields = [...bCCustomer.form_fields, { name: "username", value: username }]
|
||||
updateCustomer(bCCustomer)
|
||||
|
||||
throw {
|
||||
status: 200,
|
||||
message: "customer updated",
|
||||
data: internalCustomer,
|
||||
log: false,
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
if (returnCustomer) {
|
||||
return {
|
||||
data: returnCustomer,
|
||||
}
|
||||
}
|
||||
throw { status: 401, error: "unauthorized", log: false }
|
||||
})()
|
||||
@@ -1,56 +0,0 @@
|
||||
const { getOrderById, getOrdersForCustomer, createInternalOrderObject } = require("../lib/bigcommerceRestAPI")
|
||||
const { withAccount } = require("../lib/utils")
|
||||
|
||||
;(function () {
|
||||
if (context.user.auth()) return {}
|
||||
withAccount((login) => {
|
||||
const orderId = context.request().param("id")
|
||||
if (orderId) {
|
||||
/** @type {Order} */
|
||||
// @ts-ignore
|
||||
const internalOrder = context.db.find("bigCommerceOrder", {
|
||||
filter: { _id: orderId },
|
||||
})[0]
|
||||
if (internalOrder.customerTibiId !== login.tibiId) {
|
||||
if (String(internalOrder?.customerBigCommerceId) !== String(login?.bigCommerceId)) {
|
||||
throw {
|
||||
message: "You don't have permission to access this order",
|
||||
code: 403,
|
||||
}
|
||||
} else {
|
||||
context.db.update("bigCommerceOrder", internalOrder.id, {
|
||||
customerTibiId: login.tibiId,
|
||||
})
|
||||
}
|
||||
}
|
||||
const order = getOrderById(internalOrder.bigCommerceId)
|
||||
order.tibiId = internalOrder.id
|
||||
order.status = internalOrder.status
|
||||
order.shipments = internalOrder.shipments
|
||||
order.statusSetAt = internalOrder.statusSetAt
|
||||
throw {
|
||||
data: order,
|
||||
status: 200,
|
||||
}
|
||||
} else {
|
||||
const orders = getOrdersForCustomer(login.bigCommerceId)
|
||||
orders.forEach((order) => {
|
||||
let internalOrder = context.db.find("bigCommerceOrder", {
|
||||
filter: { bigCommerceId: order.id },
|
||||
})[0]
|
||||
if (!internalOrder && order.id) {
|
||||
const internalOrderReference = createInternalOrderObject(order.id)
|
||||
internalOrder = context.db.create("bigCommerceOrder", internalOrderReference)
|
||||
} else if (!internalOrder) return
|
||||
order.tibiId = internalOrder?.id
|
||||
order.status = internalOrder?.status
|
||||
order.shipments = internalOrder?.shipments
|
||||
order.statusSetAt = internalOrder?.statusSetAt
|
||||
})
|
||||
throw {
|
||||
data: orders,
|
||||
status: 200,
|
||||
}
|
||||
}
|
||||
})
|
||||
})()
|
||||
@@ -1,5 +0,0 @@
|
||||
import { withAccount } from "../lib/utils"
|
||||
;(function () {
|
||||
if (context.user.auth()) return {}
|
||||
withAccount((login) => {})
|
||||
})
|
||||
@@ -1,47 +0,0 @@
|
||||
const { withAccount } = require("../lib/utils")
|
||||
;(function () {
|
||||
withAccount((login) => {
|
||||
const id = context.request().param("id")
|
||||
if (!id) {
|
||||
throw {
|
||||
message: "id is required",
|
||||
code: 400,
|
||||
}
|
||||
}
|
||||
const existingReturnRequest = context.db.find("orderReturnRequest", {
|
||||
filter: {
|
||||
_id: id,
|
||||
},
|
||||
})[0]
|
||||
if (!existingReturnRequest)
|
||||
throw {
|
||||
message: "Return request not found",
|
||||
code: 404,
|
||||
}
|
||||
const order = context.db.find("bigCommerceOrder", {
|
||||
filter: {
|
||||
bigCommerceId: Number(existingReturnRequest.bigCommerceId),
|
||||
},
|
||||
})[0]
|
||||
if (!order)
|
||||
throw {
|
||||
message: "Order not found",
|
||||
code: 404,
|
||||
}
|
||||
|
||||
if (order.customerBigCommerceId !== login.bigCommerceId)
|
||||
throw {
|
||||
message: "You don't have permission to access this order",
|
||||
code: 403,
|
||||
}
|
||||
if (
|
||||
existingReturnRequest.status !== "pending" &&
|
||||
existingReturnRequest.status !== "approved" &&
|
||||
!!existingReturnRequest.status
|
||||
)
|
||||
throw {
|
||||
message: "Return request is not pending or approved",
|
||||
code: 400,
|
||||
}
|
||||
})
|
||||
})()
|
||||
@@ -1,33 +0,0 @@
|
||||
const { serverBaseURL, logoPath, operatorEmail, noReplyEmail, contactEmail } = require("../config")
|
||||
const { frontendBase } = require("../config-client")
|
||||
const { withAccount } = require("../lib/utils")
|
||||
;(function () {
|
||||
if (context.user.auth()) return
|
||||
withAccount((login) => {
|
||||
const store = {
|
||||
logo: `${serverBaseURL}${logoPath}`,
|
||||
frontendBase,
|
||||
}
|
||||
const html = context.tpl.execute(context.fs.readFile("templates/orderReturnRequestRevoked.html"), {
|
||||
store,
|
||||
})
|
||||
|
||||
context.smtp.sendMail({
|
||||
to: contactEmail,
|
||||
from: noReplyEmail,
|
||||
fromName: "BinKrassDuFass",
|
||||
replyTo: noReplyEmail,
|
||||
subject: "New Order Return Request Cancellation",
|
||||
plain: `Die Bestellung von ${login.email} soll doch nicht zurückgegeben werden.`,
|
||||
})
|
||||
|
||||
context.smtp.sendMail({
|
||||
to: login.email,
|
||||
from: operatorEmail,
|
||||
fromName: "BinKrassDuFass",
|
||||
replyTo: noReplyEmail,
|
||||
subject: "Widerruf deiner Bestellung abgebrochen",
|
||||
html,
|
||||
})
|
||||
})
|
||||
})()
|
||||
@@ -1,41 +0,0 @@
|
||||
const { withAccount } = require("../lib/utils")
|
||||
;(function () {
|
||||
const queryParam = context.request().query("orderId")
|
||||
if (!queryParam) {
|
||||
if (context.user.auth()) return
|
||||
throw {
|
||||
message: "orderId is required",
|
||||
code: 400,
|
||||
}
|
||||
}
|
||||
|
||||
withAccount((login) => {
|
||||
const order = context.db.find("bigCommerceOrder", {
|
||||
filter: {
|
||||
bigCommerceId: Number(queryParam),
|
||||
},
|
||||
})[0]
|
||||
if (!order) {
|
||||
throw {
|
||||
message: "Order not found",
|
||||
code: 404,
|
||||
}
|
||||
}
|
||||
if (order.customerBigCommerceId !== login.bigCommerceId) {
|
||||
console.log(
|
||||
"order.customerBigCommerceId",
|
||||
order.customerBigCommerceId,
|
||||
"login.bigCommerceId",
|
||||
login.bigCommerceId
|
||||
)
|
||||
throw {
|
||||
message: "You don't have permission to access this order",
|
||||
code: 403,
|
||||
}
|
||||
}
|
||||
context.filter = {
|
||||
bigCommerceId: order.bigCommerceId,
|
||||
}
|
||||
})
|
||||
return context
|
||||
})()
|
||||
@@ -1,81 +0,0 @@
|
||||
const { withAccount } = require("../lib/utils")
|
||||
const { getOrderProductsById } = require("../lib/bigcommerceRestAPI")
|
||||
;(function () {
|
||||
if (context.user.auth()) return
|
||||
withAccount((login) => {
|
||||
const order = context.db.find("bigCommerceOrder", {
|
||||
filter: {
|
||||
bigCommerceId: Number(context.data.bigCommerceId),
|
||||
},
|
||||
})[0]
|
||||
if (!order)
|
||||
throw {
|
||||
message: "Order not found",
|
||||
code: 404,
|
||||
}
|
||||
|
||||
if (order.customerBigCommerceId !== login.bigCommerceId)
|
||||
throw {
|
||||
message: "You don't have permission to access this order",
|
||||
code: 403,
|
||||
}
|
||||
|
||||
const bigcommerceProducts = getOrderProductsById(context.data.bigCommerceId)
|
||||
const returnRequestProducts = context.data.products
|
||||
// make sure all ids are also in the orderproducts
|
||||
returnRequestProducts.forEach((/** @type {OrderReturnRequestProduct} */ returnRequestProduct) => {
|
||||
const bigcommerceProduct = bigcommerceProducts.find(
|
||||
(bigcommerceProduct) => Number(bigcommerceProduct.id) === returnRequestProduct.productId
|
||||
)
|
||||
if (!bigcommerceProduct)
|
||||
throw {
|
||||
message: "Product not found",
|
||||
code: 404,
|
||||
}
|
||||
if (bigcommerceProduct.quantity < returnRequestProduct.quantity)
|
||||
throw {
|
||||
message: "Quantity exceeds the available quantity",
|
||||
code: 400,
|
||||
}
|
||||
const existingReturnRequests = context.db.find("orderReturnRequest", {
|
||||
filter: {
|
||||
bigCommerceId: context.data.bigCommerceId,
|
||||
$or: [
|
||||
{
|
||||
status: "pending",
|
||||
},
|
||||
{
|
||||
status: "approved",
|
||||
},
|
||||
{
|
||||
status: "refunded",
|
||||
},
|
||||
],
|
||||
},
|
||||
})
|
||||
returnRequestProduct.baseProductId = bigcommerceProduct.product_id
|
||||
let totalExistingQuantity = 0
|
||||
existingReturnRequests.forEach((existingReturnRequest) => {
|
||||
existingReturnRequest.products.forEach(
|
||||
(/** @type {OrderReturnRequestProduct} */ existingReturnRequestProduct) => {
|
||||
if (existingReturnRequestProduct.productId === returnRequestProduct.productId) {
|
||||
totalExistingQuantity += existingReturnRequestProduct.quantity
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
if (totalExistingQuantity + returnRequestProduct.quantity > bigcommerceProduct.quantity) {
|
||||
throw {
|
||||
message: "Quantity exceeds the available quantity",
|
||||
code: 400,
|
||||
}
|
||||
}
|
||||
})
|
||||
context.data.products = context.data.products.filter(
|
||||
(/** @type {OrderReturnRequestProduct} */ p) => p.quantity > 0
|
||||
)
|
||||
context.data.status = "pending"
|
||||
context.data.email = login.email
|
||||
})
|
||||
return context
|
||||
})()
|
||||
@@ -1,30 +0,0 @@
|
||||
const { serverBaseURL, logoPath, operatorEmail, noReplyEmail, contactEmail } = require("../config")
|
||||
const { frontendBase } = require("../config-client")
|
||||
|
||||
;(function () {
|
||||
const store = {
|
||||
logo: `${serverBaseURL}${logoPath}`,
|
||||
frontendBase,
|
||||
}
|
||||
const html = context.tpl.execute(context.fs.readFile("templates/orderReturnRequestApproval.html"), {
|
||||
store,
|
||||
})
|
||||
|
||||
context.smtp.sendMail({
|
||||
to: contactEmail,
|
||||
from: noReplyEmail,
|
||||
fromName: "BinKrassDuFass",
|
||||
replyTo: noReplyEmail,
|
||||
subject: "New Order Return Request",
|
||||
plain: `Die Bestellung von ${context.data.email} soll zurückgegeben werden.`,
|
||||
})
|
||||
|
||||
context.smtp.sendMail({
|
||||
to: context.data.email,
|
||||
from: operatorEmail,
|
||||
fromName: "BinKrassDuFass",
|
||||
replyTo: noReplyEmail,
|
||||
subject: "Widerruf deiner Bestellung",
|
||||
html,
|
||||
})
|
||||
})()
|
||||
@@ -1,51 +0,0 @@
|
||||
const { logoPath, noReplyEmail, operatorEmail, serverBaseURL } = require("../config")
|
||||
const { frontendBase } = require("../config-client")
|
||||
const { getOrderById, updateOrderById } = require("../lib/bigcommerceRestAPI")
|
||||
;(function () {
|
||||
const oldOrderReturnRequest = context.db.find("orderReturnRequest", {
|
||||
filter: {
|
||||
_id: context.data.id,
|
||||
},
|
||||
})[0]
|
||||
if (!oldOrderReturnRequest)
|
||||
throw {
|
||||
message: "Order return request not found",
|
||||
code: 404,
|
||||
}
|
||||
if (context.data.status == "approved") {
|
||||
const bigCommerceOrder = getOrderById(context.data.bigCommerceId)
|
||||
delete bigCommerceOrder.productObjs
|
||||
delete bigCommerceOrder.shipping_addressObjs
|
||||
bigCommerceOrder.status_id = 5
|
||||
updateOrderById(bigCommerceOrder.id, { status_id: 5 })
|
||||
}
|
||||
if (oldOrderReturnRequest.status !== context.data.status) {
|
||||
const oRR = context.data
|
||||
const customer = context.db.find("bigCommerceCustomer", {
|
||||
filter: { email: oRR.email },
|
||||
})[0]
|
||||
|
||||
if (!customer) {
|
||||
throw {
|
||||
status: 404,
|
||||
message: "Customer not found",
|
||||
}
|
||||
}
|
||||
const store = {
|
||||
logo: `${serverBaseURL}${logoPath}`,
|
||||
frontendBase,
|
||||
}
|
||||
const html = context.tpl.execute(context.fs.readFile("templates/statusUpdateReturnRequest.html"), {
|
||||
store,
|
||||
})
|
||||
|
||||
context.smtp.sendMail({
|
||||
to: oRR.email,
|
||||
from: operatorEmail,
|
||||
fromName: "BinKrassDuFass",
|
||||
replyTo: noReplyEmail,
|
||||
subject: "Stornierung deiner Bestellung",
|
||||
html,
|
||||
})
|
||||
}
|
||||
})()
|
||||
@@ -1,35 +0,0 @@
|
||||
const { withAccount } = require("../lib/utils")
|
||||
;(function () {
|
||||
const queryParam = context.request().query("orderId")
|
||||
if (!queryParam) {
|
||||
if (context.user.auth()) return
|
||||
throw {
|
||||
message: "orderId is required",
|
||||
code: 400,
|
||||
}
|
||||
}
|
||||
|
||||
withAccount((login) => {
|
||||
const order = context.db.find("bigCommerceOrder", {
|
||||
filter: {
|
||||
bigCommerceId: Number(queryParam),
|
||||
},
|
||||
})[0]
|
||||
if (!order) {
|
||||
throw {
|
||||
message: "Order not found",
|
||||
code: 404,
|
||||
}
|
||||
}
|
||||
if (order.customerBigCommerceId !== login.bigCommerceId) {
|
||||
throw {
|
||||
message: "You don't have permission to access this order",
|
||||
code: 403,
|
||||
}
|
||||
}
|
||||
context.filter = {
|
||||
bigCommerceId: order.bigCommerceId,
|
||||
}
|
||||
})
|
||||
return context
|
||||
})()
|
||||
@@ -1,45 +0,0 @@
|
||||
const { withAccount } = require("../lib/utils")
|
||||
const { getPrintfulOrder, cancelPrintfulOrder } = require("../lib/printfulRestAPI")
|
||||
;(function () {
|
||||
withAccount((login) => {
|
||||
const order = context.db.find("bigCommerceOrder", {
|
||||
filter: {
|
||||
bigCommerceId: Number(context.data.bigCommerceId),
|
||||
},
|
||||
})[0]
|
||||
if (!order)
|
||||
throw {
|
||||
message: "Order not found",
|
||||
code: 404,
|
||||
}
|
||||
|
||||
if (order.customerBigCommerceId !== login.bigCommerceId)
|
||||
throw {
|
||||
message: "You don't have permission to access this order",
|
||||
code: 403,
|
||||
}
|
||||
const existingRevokeRequests = context.db.find("orderRevokeRequest", {
|
||||
filter: {
|
||||
bigCommerceId: context.data.bigCommerceId,
|
||||
},
|
||||
})
|
||||
if (existingRevokeRequests.length > 0)
|
||||
throw {
|
||||
message: "Revoke request already exists",
|
||||
code: 400,
|
||||
}
|
||||
if (!!order.status && order.status !== "draft") {
|
||||
throw {
|
||||
message: "Order is already in process",
|
||||
code: 400,
|
||||
}
|
||||
}
|
||||
const printfulOrder = getPrintfulOrder(order.bigCommerceId)
|
||||
|
||||
cancelPrintfulOrder(context.data.bigCommerceId)
|
||||
context.data.status = "pending"
|
||||
context.data.email = login.email
|
||||
context.data.printfulId = printfulOrder.id
|
||||
})
|
||||
return context
|
||||
})()
|
||||
@@ -1,36 +0,0 @@
|
||||
const { serverBaseURL, logoPath, operatorEmail, noReplyEmail, contactEmail } = require("../config")
|
||||
const { frontendBase } = require("../config-client")
|
||||
const { getOrderById, updateOrderById } = require("../lib/bigcommerceRestAPI")
|
||||
;(function () {
|
||||
if (context.user.auth()) return
|
||||
const bigCommerceOrder = getOrderById(context.data.bigCommerceId)
|
||||
delete bigCommerceOrder.productObjs
|
||||
delete bigCommerceOrder.shipping_addressObjs
|
||||
bigCommerceOrder.status_id = 5
|
||||
updateOrderById(bigCommerceOrder.id, { status_id: 5 })
|
||||
const store = {
|
||||
logo: `${serverBaseURL}${logoPath}`,
|
||||
frontendBase,
|
||||
}
|
||||
const html = context.tpl.execute(context.fs.readFile("templates/orderRevokedApproval.html"), {
|
||||
store,
|
||||
})
|
||||
|
||||
context.smtp.sendMail({
|
||||
to: contactEmail,
|
||||
from: noReplyEmail,
|
||||
fromName: "BinKrassDuFass",
|
||||
replyTo: noReplyEmail,
|
||||
subject: "New Order Revoke Request",
|
||||
plain: `Die Bestellung von ${context.data.email} wurde abgebrochen.`,
|
||||
})
|
||||
|
||||
context.smtp.sendMail({
|
||||
to: context.data.email,
|
||||
from: operatorEmail,
|
||||
fromName: "BinKrassDuFass",
|
||||
replyTo: noReplyEmail,
|
||||
subject: "Abbruch deiner Bestellung",
|
||||
html,
|
||||
})
|
||||
})()
|
||||
@@ -1,43 +0,0 @@
|
||||
import { logoPath, noReplyEmail, operatorEmail, serverBaseURL } from "../config"
|
||||
import { frontendBase } from "../config-client"
|
||||
;(function () {
|
||||
const oldOrderReturnRequest = context.db.find("orderReturnRequest", {
|
||||
filter: {
|
||||
_id: context.data.id,
|
||||
},
|
||||
})[0]
|
||||
if (!oldOrderReturnRequest)
|
||||
throw {
|
||||
message: "Order return request not found",
|
||||
code: 404,
|
||||
}
|
||||
if (oldOrderReturnRequest.status !== context.data.status) {
|
||||
const oRR = context.data
|
||||
const customer = context.db.find("bigCommerceCustomer", {
|
||||
filter: { email: oRR.email },
|
||||
})[0]
|
||||
|
||||
if (!customer) {
|
||||
throw {
|
||||
status: 404,
|
||||
message: "Customer not found",
|
||||
}
|
||||
}
|
||||
const store = {
|
||||
logo: `${serverBaseURL}${logoPath}`,
|
||||
frontendBase,
|
||||
}
|
||||
const html = context.tpl.execute(context.fs.readFile("templates/statusUpdateReturnRequest.html"), {
|
||||
store,
|
||||
})
|
||||
|
||||
context.smtp.sendMail({
|
||||
to: oRR.email,
|
||||
from: operatorEmail,
|
||||
fromName: "BinKrassDuFass",
|
||||
replyTo: noReplyEmail,
|
||||
subject: "Stornierung Ihrer Bestellung",
|
||||
html,
|
||||
})
|
||||
}
|
||||
})()
|
||||
@@ -1,23 +0,0 @@
|
||||
const { attachRatingObjsToProduct } = require("./helper")
|
||||
;(function () {
|
||||
if (context.user.auth()) return
|
||||
const bigCommerceProductId = context.request().query("bigCommerceProductId")
|
||||
if (bigCommerceProductId) {
|
||||
/**@type {LocalProduct[]} */
|
||||
//@ts-ignore
|
||||
let products = context.db.find("bigCommerceProduct", {
|
||||
filter: {
|
||||
bigCommerceId: Number(bigCommerceProductId),
|
||||
},
|
||||
})
|
||||
products = attachRatingObjsToProduct(products)
|
||||
|
||||
if (products.length > 0) {
|
||||
throw {
|
||||
status: 200,
|
||||
data: products[0],
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
})()
|
||||
@@ -1,12 +0,0 @@
|
||||
const { attachRatingObjsToProduct } = require("./helper")
|
||||
;(function () {
|
||||
if (context.user.auth()) return
|
||||
/**@type {LocalProduct[]} */
|
||||
//@ts-ignore
|
||||
let products = context.results()
|
||||
products = attachRatingObjsToProduct(products)
|
||||
let hookResponse = {
|
||||
results: products,
|
||||
}
|
||||
return hookResponse
|
||||
})()
|
||||
@@ -1,26 +0,0 @@
|
||||
/**
|
||||
* @param {LocalProduct[]} products
|
||||
* @returns {LocalProduct[]}
|
||||
*/
|
||||
function attachRatingObjsToProduct(products) {
|
||||
let productIds = products.map((product) => product.bigCommerceId)
|
||||
/**@type {ProductRating[]} */
|
||||
//@ts-ignore
|
||||
let allRatings = context.db.find("rating", {
|
||||
filter: {
|
||||
status: "approved",
|
||||
bigCommerceProductId: { $in: productIds },
|
||||
},
|
||||
})
|
||||
|
||||
products.forEach((product, i) => {
|
||||
let ratings = allRatings.filter((rating) => rating.bigCommerceProductId === product.bigCommerceId).reverse()
|
||||
products[i].ratings = ratings
|
||||
})
|
||||
|
||||
return products
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
attachRatingObjsToProduct,
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
const { getAllProducts, getProductImages } = require("../lib/bigcommerceRestAPI")
|
||||
const { extractSizingChart } = require("../lib/printfulRestAPI")
|
||||
;(function () {
|
||||
const products = getAllProducts()
|
||||
const productIds = products.map((p) => p.id)
|
||||
const currentProducts = context.db.find("bigCommerceProduct", { filter: { bigCommerceId: { $in: productIds } } })
|
||||
const notFoundProductIds = productIds.filter((id) => !currentProducts.some((p) => p.bigCommerceId == id))
|
||||
|
||||
const newProducts = products.filter((p) => notFoundProductIds.includes(p.id))
|
||||
newProducts.forEach((p) => {
|
||||
const productImage = getProductImages(String(p.id))
|
||||
context.db.create("bigCommerceProduct", {
|
||||
forcedWarning: "",
|
||||
productName: p.name_customer || p.name,
|
||||
previewImage: productImage?.[0]?.url_thumbnail,
|
||||
bigCommerceId: p?.id,
|
||||
sizingChart: null,
|
||||
})
|
||||
})
|
||||
|
||||
currentProducts.forEach((p) => {
|
||||
context.db.update("bigCommerceProduct", p.id, {
|
||||
...p,
|
||||
productName:
|
||||
products.find((bp) => bp.id == p.bigCommerceId).name_customer ||
|
||||
products.find((bp) => bp.id == p.bigCommerceId).name,
|
||||
bigCommerceSKU: products.find((bp) => bp.id == p.bigCommerceId).sku,
|
||||
bigCommerceId: products.find((bp) => bp.id == p.bigCommerceId).id,
|
||||
sizingChart: extractSizingChart(p.printfulProductId, p.id),
|
||||
})
|
||||
})
|
||||
throw {
|
||||
status: 200,
|
||||
message: "Products created or updated successfully",
|
||||
}
|
||||
})()
|
||||
@@ -1,13 +0,0 @@
|
||||
const { extractSizingChart } = require("../lib/printfulRestAPI")
|
||||
let { clearSSRCache } = require("../lib/utils")
|
||||
|
||||
;(function () {
|
||||
clearSSRCache()
|
||||
if (context.data.printfulProductId) {
|
||||
const sizingChart = extractSizingChart(context.data.printfulProductId, context.data.id)
|
||||
if (sizingChart) {
|
||||
context.data.sizingChart = sizingChart
|
||||
return { data: context.data }
|
||||
}
|
||||
}
|
||||
})()
|
||||
@@ -1,15 +0,0 @@
|
||||
;(function () {
|
||||
const ratingId = context.request().param("id")
|
||||
let rating = context.db.find("rating", {
|
||||
filter: {
|
||||
_id: ratingId,
|
||||
},
|
||||
})[0]
|
||||
if (!rating.id)
|
||||
throw {
|
||||
status: 400,
|
||||
error: "No id specified.",
|
||||
}
|
||||
// @ts-ignore
|
||||
context["rating"] = rating
|
||||
})()
|
||||
@@ -1,7 +0,0 @@
|
||||
let { clearSSRCache } = require("../lib/utils")
|
||||
let { deleteRating } = require("../lib/bigcommerceRestAPI")
|
||||
;(function () {
|
||||
clearSSRCache()
|
||||
// @ts-ignore
|
||||
deleteRating(context["rating"].bigCommerceProductId, context["rating"].bigcommerceReviewId)
|
||||
})()
|
||||
@@ -1,46 +0,0 @@
|
||||
// @ts-check
|
||||
const { withAccount } = require("../lib/utils")
|
||||
;(function () {
|
||||
/** @type {HookResponse} */
|
||||
let hookResponse
|
||||
let request = context.request()
|
||||
if (context.user.auth()) return
|
||||
const orderId = Number(request.query("orderId"))
|
||||
if (orderId) {
|
||||
withAccount((login) => {
|
||||
let order = context.db.find("bigCommerceOrder", {
|
||||
filter: {
|
||||
bigCommerceId: orderId,
|
||||
customerBigCommerceId: login.bigCommerceId,
|
||||
},
|
||||
})[0]
|
||||
if (!order) {
|
||||
throw {
|
||||
status: 404,
|
||||
data: {
|
||||
message: "Order not found",
|
||||
},
|
||||
}
|
||||
}
|
||||
})
|
||||
hookResponse = {
|
||||
filter: {
|
||||
bigcommerceOrderId: orderId,
|
||||
},
|
||||
}
|
||||
|
||||
return hookResponse
|
||||
} else {
|
||||
hookResponse = {
|
||||
filter: context.filter,
|
||||
selector: {
|
||||
bigCommerceProductId: 1,
|
||||
rating: 1,
|
||||
comment: 1,
|
||||
title: 1,
|
||||
review_date: 1,
|
||||
},
|
||||
}
|
||||
return hookResponse
|
||||
}
|
||||
})()
|
||||
@@ -1,17 +0,0 @@
|
||||
const { productInsideOrder } = require("../lib/utils")
|
||||
|
||||
;(function () {
|
||||
if (!context?.user?.auth()?.id) {
|
||||
if (!context.data) throw { status: 400, error: "No data provided" }
|
||||
// @ts-ignore
|
||||
productInsideOrder(context.data)
|
||||
/** @type {ProductRating[]} */ // @ts-ignore
|
||||
let ratings = context.db.find("rating", {
|
||||
filter: {
|
||||
bigcommerceOrderId: context?.data?.bigcommerceOrderId,
|
||||
bigCommerceProductId: context?.data?.bigCommerceProductId,
|
||||
},
|
||||
})
|
||||
if (ratings.length) throw { status: 400, error: "Rating already existing" }
|
||||
}
|
||||
})()
|
||||
@@ -1,41 +0,0 @@
|
||||
const { bigcommerceApiOAuth, serverBaseURL, bigcommerceStoreHash } = require("../config.js")
|
||||
let { sendOperatorRatingMail, clearSSRCache, statusIsValid } = require("../lib/utils")
|
||||
;(function () {
|
||||
const response = context.http.fetch(
|
||||
`https://api.bigcommerce.com/stores/${bigcommerceStoreHash}/v3/catalog/products/${context?.data?.bigCommerceProductId}/reviews`,
|
||||
{
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
"X-Auth-Token": bigcommerceApiOAuth,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
title: context?.data?.title,
|
||||
comment: context?.data?.comment,
|
||||
status:
|
||||
context?.data?.status === "approved"
|
||||
? "approved"
|
||||
: context?.data?.status == "pending"
|
||||
? "pending"
|
||||
: "disapproved",
|
||||
rating: context?.data?.rating?.overall,
|
||||
date_reviewed: context?.data?.review_date,
|
||||
}),
|
||||
}
|
||||
)
|
||||
|
||||
if (!statusIsValid(response.status)) {
|
||||
throw {
|
||||
status: response.status,
|
||||
error: response.statusText,
|
||||
}
|
||||
}
|
||||
const bigcommerceRating = response.body.json()
|
||||
context.db.update("rating", context?.data?.id || "", {
|
||||
bigcommerceReviewId: String(bigcommerceRating?.data?.id),
|
||||
})
|
||||
|
||||
clearSSRCache()
|
||||
sendOperatorRatingMail()
|
||||
})()
|
||||
@@ -1,5 +0,0 @@
|
||||
let { validateAndModifyRating } = require("../lib/utils")
|
||||
;(function () {
|
||||
// @ts-ignore
|
||||
return { data: validateAndModifyRating(context.data) }
|
||||
})()
|
||||
@@ -1,42 +0,0 @@
|
||||
let { sendOperatorRatingMail, clearSSRCache, statusIsValid } = require("../lib/utils")
|
||||
const { bigcommerceApiOAuth, bigcommerceStoreHash } = require("../config.js")
|
||||
;(function () {
|
||||
const response = context.http.fetch(
|
||||
// @ts-ignore
|
||||
`https://api.bigcommerce.com/stores/${bigcommerceStoreHash}/v3/catalog/products/${context?.data?.bigCommerceProductId}/reviews/${context["oldRating"]?.bigcommerceReviewId}`,
|
||||
{
|
||||
method: "PUT",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
Accept: "application/json",
|
||||
"X-Auth-Token": bigcommerceApiOAuth,
|
||||
},
|
||||
body: JSON.stringify({
|
||||
title: context?.data?.title,
|
||||
comment: context?.data?.comment,
|
||||
status:
|
||||
context?.data?.status === "approved"
|
||||
? "approved"
|
||||
: context?.data?.status == "pending"
|
||||
? "pending"
|
||||
: "disapproved",
|
||||
rating: context?.data?.rating?.overall,
|
||||
date_reviewed: context?.data?.review_date,
|
||||
}),
|
||||
}
|
||||
)
|
||||
if (!statusIsValid(response.status)) {
|
||||
throw {
|
||||
status: response.status,
|
||||
error: response.statusText,
|
||||
}
|
||||
}
|
||||
let rating = context.data
|
||||
// @ts-ignore
|
||||
let oldRating = context["oldRating"]
|
||||
// @ts-ignore
|
||||
if (!oldRating || JSON.stringify(rating) != JSON.stringify(oldRating)) {
|
||||
sendOperatorRatingMail()
|
||||
clearSSRCache()
|
||||
}
|
||||
})()
|
||||
@@ -1,16 +0,0 @@
|
||||
const { productInsideOrder } = require("../lib/utils")
|
||||
|
||||
;(function () {
|
||||
if (!context?.user?.auth()?.id) {
|
||||
// @ts-ignore
|
||||
productInsideOrder(context?.data)
|
||||
}
|
||||
/** @type {ProductRating} */ // @ts-ignore
|
||||
let ratingObj = context.db.find("rating", {
|
||||
filter: {
|
||||
_id: context?.data?.id,
|
||||
},
|
||||
})[0]
|
||||
// @ts-ignore
|
||||
context["oldRating"] = ratingObj
|
||||
})()
|
||||
@@ -1,5 +0,0 @@
|
||||
let { validateAndModifyRating } = require("../lib/utils")
|
||||
;(function () {
|
||||
// @ts-ignore
|
||||
return { data: validateAndModifyRating(context.data) }
|
||||
})()
|
||||
@@ -1 +0,0 @@
|
||||
ich arbeite jetzt mit so nhm tool zusammen, was mir das figma als svelte + css ausspuckt, ziemlich redundant. Deine aufgabe ist in diesem chat, die sachen zu nehmen, das css ist less umzuwandeln und redundanzen aus dem less zu entfernen (keine neuen variablen machen!!) und im template mit loops etc arebbeiten sowie im script block halt arrays etc declaren... Schreib unbedingt immer alles aus, niemals iwi ... oder so machen, wenn nötig, auch über mehrere antworten lnag ziehen, ich wills copyn pasten können. Die seite bleibt nicht statisch, ich baue das dann selbst so um, dass die daten dynamisch gefatched werden, baue es so, dass ich da ein leichtes habe. Achte beim HTML vor allem auf ausgezeichnete semantik! Benutze weiterhin die variablen, die auch im bereitgestelltem skript gegeben sind, die sind alle verfügbar:
|
||||
@@ -1,71 +0,0 @@
|
||||
version: "3.9"
|
||||
|
||||
services:
|
||||
maildev:
|
||||
image: maildev/maildev
|
||||
command: [node, bin/maildev, --web, "80", --smtp, "25", -v, --hide-extensions=STARTTLS]
|
||||
expose:
|
||||
- 1080
|
||||
|
||||
mongo:
|
||||
image: mongo:4.2
|
||||
|
||||
tibi-server:
|
||||
image: gitbase.de/cms/tibi-server
|
||||
environment:
|
||||
DB_DIAL: mongodb://mongo
|
||||
API_PORT: 8080
|
||||
MAIL_HOST: maildev:25
|
||||
depends_on:
|
||||
- maildev
|
||||
- mongo
|
||||
volumes:
|
||||
- ./:/workdir
|
||||
|
||||
liveserver:
|
||||
image: node:20
|
||||
command: yarn run -- live-server --no-browser --port=80 --ignore='*' --entry-file=spa.html --no-css-inject --proxy=/api:http://tibi-server:8080/api/v1/_/einfo_test frontend
|
||||
depends_on:
|
||||
- tibi-server
|
||||
volumes:
|
||||
- ./:/workdir
|
||||
working_dir: /workdir
|
||||
|
||||
playwright:
|
||||
image: mcr.microsoft.com/playwright:focal
|
||||
environment:
|
||||
FORCE_COLOR: "true"
|
||||
PLAYWRIGHT_BASE_URL: http://liveserver
|
||||
PLAYWRIGHT_CI: "true"
|
||||
PLAYWRIGHT_mongodbUri: mongodb://mongo
|
||||
PLAYWRIGHT_tibiApiUrl: http://tibi-server:8080/api/v1
|
||||
PLAYWRIGHT_projectApiConfig: /workdir/api/config.yml
|
||||
DISPLAY: display:0
|
||||
PLAYWRIGHT_CACHE_FOLDER: /.cache/Playwright
|
||||
command: sh ./scripts/py-command.docker.sh $PY_MODE $PY_SPECFLAG
|
||||
depends_on:
|
||||
- liveserver
|
||||
volumes:
|
||||
- ./:/workdir
|
||||
- ./tmp/.npm:/.npm
|
||||
- ./tmp/.cache:/.cache
|
||||
- ./tmp/.yarn:/.yarn
|
||||
working_dir: /workdir
|
||||
|
||||
display:
|
||||
image: ghcr.io/dtinth/xtigervnc-docker:main
|
||||
tmpfs: /tmp
|
||||
restart: always
|
||||
environment:
|
||||
VNC_GEOMETRY: 1920x1080
|
||||
|
||||
novnc:
|
||||
image: geek1011/easy-novnc
|
||||
restart: always
|
||||
command: -a :5800 -h display --no-url-password
|
||||
labels:
|
||||
- traefik.enable=true
|
||||
- traefik.http.services.${PROJECT_NAME}-playwright-novnc.loadbalancer.server.port=5800
|
||||
- online.testversion.code.subdomain=${PROJECT_NAME}-playwright-novnc
|
||||
- traefik.http.routers.${PROJECT_NAME}-playwright-novnc.middlewares=${PROJECT_NAME}-playwright-novnc
|
||||
- traefik.http.middlewares.${PROJECT_NAME}-playwright-novnc.basicauth.usersfile=${PWD}/.basic-auth-code
|
||||
@@ -25,11 +25,7 @@
|
||||
import { getWishlist } from "./lib/functions/CommerceAPIs/tibiEndpoints/wishlist"
|
||||
import PublicProfile from "./routes/PublicProfile.svelte"
|
||||
import { api, getDBEntries } from "./api"
|
||||
import NewsletterRow from "./lib/components/pagebuilder/blocks/NewsletterRow.svelte"
|
||||
import HelpCenter from "./routes/HelpCenter.svelte"
|
||||
import { checkIfStoreIsOpen } from "./lib/functions/CommerceAPIs/tibiEndpoints/store"
|
||||
import Input from "./lib/components/pagebuilder/blocks/form/Input.svelte"
|
||||
import { onChange } from "./lib/components/pagebuilder/profile/helper"
|
||||
import RedirectToPublicProfile from "./routes/RedirectToPublicProfile.svelte"
|
||||
import { onMount } from "svelte"
|
||||
import ActionApproval from "./lib/components/widgets/ActionApproval.svelte"
|
||||
@@ -54,7 +50,6 @@
|
||||
if (l.push && oldPath != l.path) window.scrollTo(0, 0)
|
||||
oldPath = l.path
|
||||
})
|
||||
getWishlist().then(wishlist.set)
|
||||
|
||||
api(`module`, {
|
||||
method: "GET",
|
||||
@@ -62,30 +57,6 @@
|
||||
$modules = res.data
|
||||
})
|
||||
|
||||
api("productBackgroundImage", {
|
||||
method: "GET",
|
||||
}).then((res) => {
|
||||
res.data.forEach((element) => {
|
||||
$backgroundImages[element.type] = element.image
|
||||
})
|
||||
})
|
||||
|
||||
checkIfStoreIsOpen().then((status) => {
|
||||
if ($shopStatus.loggedIn) return
|
||||
if (!status || status.status === "open") {
|
||||
shopStatus.set({
|
||||
status: "open",
|
||||
loggedIn: true,
|
||||
password: status?.password,
|
||||
})
|
||||
} else {
|
||||
shopStatus.set({
|
||||
status: "login",
|
||||
loggedIn: false,
|
||||
password: status.password,
|
||||
})
|
||||
}
|
||||
})
|
||||
onMount(() => {
|
||||
const updateModalState = () => {
|
||||
$openModal = document.getElementsByClassName("dialog-open").length > 0
|
||||
@@ -93,21 +64,13 @@
|
||||
const interval = setInterval(updateModalState, 100)
|
||||
return () => clearInterval(interval)
|
||||
})
|
||||
let login = {
|
||||
password: "",
|
||||
}
|
||||
|
||||
let googleCookiesAllowed = false
|
||||
let googleCookieName = "googleAnalytics"
|
||||
let metaPixelCookieName = "metaPixel"
|
||||
let metaPixelCookiesAllowed = false
|
||||
getDBEntries("selfImprovementChapter").then((res) => {
|
||||
$selfImprovementChapters = res
|
||||
})
|
||||
if (typeof window !== "undefined") {
|
||||
window.addEventListener("ccAccept", (e) => {
|
||||
// @ts-ignore
|
||||
if (e.detail.includes(googleCookieName)) googleCookiesAllowed = true
|
||||
if (e.detail.includes(metaPixelCookieName)) metaPixelCookiesAllowed = true
|
||||
})
|
||||
function checkCookie(cookieName: string) {
|
||||
var allCookies = decodeURIComponent(document.cookie)
|
||||
@@ -124,7 +87,6 @@
|
||||
return false
|
||||
}
|
||||
googleCookiesAllowed = checkCookie(googleCookieName)
|
||||
metaPixelCookiesAllowed = checkCookie(metaPixelCookieName)
|
||||
}
|
||||
|
||||
let innerWidth = 0
|
||||
@@ -135,14 +97,7 @@
|
||||
<svelte:head>
|
||||
{#if googleCookiesAllowed}
|
||||
<!-- Google tag (gtag.js) -->
|
||||
<!-- Google tag (gtag.js) -->
|
||||
<!-- Google tag (gtag.js) -->
|
||||
<!-- Google tag (gtag.js) -->
|
||||
<!-- Google tag (gtag.js) -->
|
||||
<!-- Google tag (gtag.js) -->
|
||||
<!-- Google tag (gtag.js) -->
|
||||
<!-- Google tag (gtag.js) -->
|
||||
<!-- Google tag (gtag.js) -->
|
||||
|
||||
<script
|
||||
async
|
||||
src="https://www.googletagmanager.com/gtag/js?id=G-SH85R88QE0"
|
||||
@@ -157,55 +112,12 @@
|
||||
gtag("config", "G-SH85R88QE0")
|
||||
</script>
|
||||
{/if}
|
||||
|
||||
{#if metaPixelCookiesAllowed}
|
||||
<!-- Meta Pixel Code -->
|
||||
<!-- Meta Pixel Code -->
|
||||
<!-- Meta Pixel Code -->
|
||||
<!-- Meta Pixel Code -->
|
||||
<!-- Meta Pixel Code -->
|
||||
<!-- Meta Pixel Code -->
|
||||
<!-- Meta Pixel Code -->
|
||||
<!-- Meta Pixel Code -->
|
||||
<!-- Meta Pixel Code -->
|
||||
<script>
|
||||
console.log("MetaPixelCookiesAllowed ist aktiv.")
|
||||
!(function (f, b, e, v, n, t, s) {
|
||||
if (f.fbq) return
|
||||
n = f.fbq = function () {
|
||||
n.callMethod ? n.callMethod.apply(n, arguments) : n.queue.push(arguments)
|
||||
}
|
||||
if (!f._fbq) f._fbq = n
|
||||
n.push = n
|
||||
n.loaded = !0
|
||||
n.version = "2.0"
|
||||
n.queue = []
|
||||
t = b.createElement(e)
|
||||
t.async = !0
|
||||
t.src = v
|
||||
s = b.getElementsByTagName(e)[0]
|
||||
s.parentNode.insertBefore(t, s)
|
||||
})(window, document, "script", "https://connect.facebook.net/en_US/fbevents.js")
|
||||
fbq("init", "1117933239951751")
|
||||
fbq("track", "PageView")
|
||||
</script>
|
||||
<noscript
|
||||
><img
|
||||
height="1"
|
||||
width="1"
|
||||
style="display: none"
|
||||
src="https://www.facebook.com/tr?id=1117933239951751&ev=PageView&noscript=1"
|
||||
/></noscript
|
||||
>
|
||||
<!-- End Meta Pixel Code -->
|
||||
{/if}
|
||||
</svelte:head>
|
||||
|
||||
<div>
|
||||
<SidebarOverlay />
|
||||
<main>
|
||||
<Header />
|
||||
{#if $shopStatus.status === "open" || $shopStatus.loggedIn}
|
||||
{#if $location?.path?.toLowerCase().startsWith("/product/")}
|
||||
{#key $location?.path}
|
||||
<Product handle="{$location.path.split('/product/')[1]}" />
|
||||
@@ -235,40 +147,7 @@
|
||||
{:else}
|
||||
<Content location="{$location}" />
|
||||
{/if}
|
||||
{:else}
|
||||
<form
|
||||
on:submit|preventDefault|stopPropagation="{() => {
|
||||
if (login.password === $shopStatus.password) {
|
||||
shopStatus.set({
|
||||
status: 'login',
|
||||
loggedIn: true,
|
||||
password: login.password,
|
||||
})
|
||||
newNotification({
|
||||
class: 'success',
|
||||
html: 'Erfolgreich eingeloggt',
|
||||
})
|
||||
} else {
|
||||
newNotification({
|
||||
class: 'error',
|
||||
html: 'Falsches Passwort',
|
||||
})
|
||||
}
|
||||
}}"
|
||||
>
|
||||
<Input
|
||||
id="password"
|
||||
type="password"
|
||||
bind:value="{login.password}"
|
||||
placeholder="Passwort"
|
||||
onChange="{onChange}"
|
||||
/>
|
||||
<button
|
||||
class="cta primary"
|
||||
type="submit">Login</button
|
||||
>
|
||||
</form>
|
||||
{/if}
|
||||
|
||||
<div class="crossGap"></div>
|
||||
<Footer />
|
||||
</main>
|
||||
|
||||
@@ -307,20 +307,6 @@ type EntryTypeSwitch<T> = T extends "medialib"
|
||||
? ContentEntry
|
||||
: T extends "navigation"
|
||||
? NavigationEntry
|
||||
: T extends "tag"
|
||||
? TagEntry
|
||||
: T extends "dummyCartEndpoint"
|
||||
? DummyCartEndpoint
|
||||
: T extends "productBenefit"
|
||||
? ProductBenefit
|
||||
: T extends "selfImprovementChapter"
|
||||
? SelfImprovementChapter
|
||||
: T extends "rating"
|
||||
? ProductRating
|
||||
: T extends "bigCommerceProduct"
|
||||
? LocalProduct
|
||||
: T extends "selfImprovementChallenge"
|
||||
? BKDFChallenge
|
||||
: never
|
||||
|
||||
export async function getDBEntries<T extends CollectionName>(
|
||||
|
||||
@@ -1,122 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { getDBEntry } from "../../../api"
|
||||
import CrinkledSection from "../CrinkledSection.svelte"
|
||||
import ContentBlock from "../pagebuilder/ContentBlock.svelte"
|
||||
import MedialibImage from "../widgets/MedialibImage.svelte"
|
||||
|
||||
export let entryId: string
|
||||
export let thumbnail: string
|
||||
export let sources: Source[]
|
||||
let entry: ContentEntry
|
||||
getDBEntry("content", { _id: entryId }).then((res) => {
|
||||
entry = res
|
||||
console.log("entry", entry)
|
||||
})
|
||||
</script>
|
||||
|
||||
<section class="blog-entry">
|
||||
<section class="thumbnail-image">
|
||||
<img
|
||||
src="../../../../media/topRightCrinkleDarkAndUnedged.svg"
|
||||
class="crinkle"
|
||||
alt="crinkle"
|
||||
/>
|
||||
{#if entry}
|
||||
<MedialibImage id="{thumbnail}" />
|
||||
{/if}
|
||||
</section>
|
||||
<section class="content-wrapper">
|
||||
<CrinkledSection
|
||||
activated="{true}"
|
||||
border="{false}"
|
||||
brightBackground="{false}"
|
||||
bigVersion="{true}"
|
||||
icon="bottomLeftDark"
|
||||
>
|
||||
<section class="content">
|
||||
{#if entry}
|
||||
{#each entry.blocks as block}
|
||||
<ContentBlock block="{block}" />
|
||||
{/each}
|
||||
{/if}
|
||||
</section>
|
||||
<section class="sources">
|
||||
<h4>Quellen:</h4>
|
||||
<ul>
|
||||
{#each sources as { source, url }}
|
||||
<li>
|
||||
<a
|
||||
href="{url}"
|
||||
target="_blank"
|
||||
>
|
||||
{source}
|
||||
</a>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</section>
|
||||
</CrinkledSection>
|
||||
</section>
|
||||
</section>
|
||||
|
||||
<style
|
||||
lang="less"
|
||||
global
|
||||
>
|
||||
.blog-entry {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
max-width: var(--small-max-width);
|
||||
width: 100%;
|
||||
.thumbnail-image {
|
||||
width: 100%;
|
||||
height: 520px;
|
||||
position: relative;
|
||||
margin-botom: -3.6rem;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
.crinkle {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
width: 72px;
|
||||
height: 72px;
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
.content-wrapper {
|
||||
padding: 0px 2.4rem;
|
||||
.content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2.4rem;
|
||||
background: var(--bg-100);
|
||||
width: 100%;
|
||||
* {
|
||||
color: var(--text-100) !important;
|
||||
}
|
||||
}
|
||||
.container {
|
||||
margin-bottom: 0px !important;
|
||||
}
|
||||
.sources {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
color: var(--text-100);
|
||||
padding: 1.2rem 2.4rem;
|
||||
border-top: 1px solid var(--neutral-white);
|
||||
margin-top: 1.2rem;
|
||||
h4,
|
||||
a {
|
||||
padding: 0.6rem 0px;
|
||||
color: var(--text-100);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,133 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { getCachedEntry } from "../../../api"
|
||||
import { getCalendarWeekFromDate } from "../../utils"
|
||||
import Steps from "../pagebuilder/blocks/Steps.svelte"
|
||||
import BlogTabSwitch from "../widgets/BlogTabSwitch.svelte"
|
||||
import MedialibImage from "../widgets/MedialibImage.svelte"
|
||||
import ChallengeBlog from "./Blog.svelte"
|
||||
|
||||
export let slug: string
|
||||
let challenge: BKDFChallenge
|
||||
getCachedEntry("selfImprovementChallenge", {
|
||||
slug: slug,
|
||||
}).then((res) => {
|
||||
challenge = res
|
||||
})
|
||||
let activeTab = 1
|
||||
</script>
|
||||
|
||||
<div class="rows-detailed-challenge">
|
||||
<section class="challenge-detailed-preview">
|
||||
{#if challenge}
|
||||
<h2>Weekly Challenge #{getCalendarWeekFromDate(challenge.activeAt)}</h2>
|
||||
|
||||
<div class="preview-img-wrapper">
|
||||
<MedialibImage id="{challenge.images.preview}" />
|
||||
</div>
|
||||
|
||||
<h1>{challenge.title}</h1>
|
||||
{/if}
|
||||
</section>
|
||||
<BlogTabSwitch
|
||||
tabs="{['Einleitung', 'Informationen']}"
|
||||
bind:selectedTab="{activeTab}"
|
||||
/>
|
||||
{#if activeTab == 0}
|
||||
<section class="introduction">
|
||||
{#if challenge}
|
||||
{#each challenge.introduction as intro}
|
||||
<p>{@html intro}</p>
|
||||
{/each}
|
||||
{/if}
|
||||
</section>
|
||||
{#if challenge}
|
||||
<section class="invitation">
|
||||
{challenge?.howItWorks?.invitation}
|
||||
</section>
|
||||
<Steps block="{challenge?.howItWorks}" />
|
||||
{/if}
|
||||
{:else if challenge}
|
||||
<ChallengeBlog
|
||||
entryId="{challenge.blog.blogId}"
|
||||
thumbnail="{challenge.blog.thumbnail}"
|
||||
sources="{challenge.blog.sources}"
|
||||
/>
|
||||
{/if}
|
||||
<div style="padding: 3.6rem 0px;"></div>
|
||||
</div>
|
||||
|
||||
<style
|
||||
lang="less"
|
||||
global
|
||||
>
|
||||
.rows-detailed-challenge {
|
||||
max-width: var(--normal-max-width);
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 2.4rem;
|
||||
.challenge-detailed-preview {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
flex-direction: column;
|
||||
gap: 1.2rem;
|
||||
max-width: var(--normal-max-width);
|
||||
|
||||
h2 {
|
||||
color: transparent;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
-webkit-text-stroke: 1px var(--krass-kraft-primary);
|
||||
padding: 1.2rem 0px;
|
||||
line-height: 4.8rem;
|
||||
font-size: 4.8rem;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
.preview-img-wrapper {
|
||||
width: 100%;
|
||||
height: 518px;
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
}
|
||||
span {
|
||||
color: var(--text-300);
|
||||
}
|
||||
h1 {
|
||||
font-size: 3.6rem;
|
||||
line-height: 3.6rem;
|
||||
font-weight: 700;
|
||||
color: var(--text-100);
|
||||
}
|
||||
}
|
||||
.introduction {
|
||||
display: flex;
|
||||
|
||||
& > p {
|
||||
padding: 0px 1.2rem;
|
||||
color: var(--text-100);
|
||||
border-right: 1px solid white;
|
||||
* {
|
||||
color: var(--text-100);
|
||||
}
|
||||
&:first-child {
|
||||
padding-left: 0px;
|
||||
}
|
||||
&:last-child {
|
||||
border-right: none;
|
||||
padding-right: 0px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.invitation {
|
||||
padding: 2.4rem;
|
||||
color: white;
|
||||
border: 1px solid white;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,70 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { spaLink } from "../../actions"
|
||||
import MedialibImage from "../widgets/MedialibImage.svelte"
|
||||
|
||||
export let challenge: BKDFChallenge
|
||||
</script>
|
||||
|
||||
<li class="challenge-preview-item">
|
||||
<a
|
||||
class="thumbnail-wrapper"
|
||||
href="/selfimprovement/krasskraft/challenge/{challenge.slug}"
|
||||
use:spaLink
|
||||
>
|
||||
<MedialibImage id="{challenge.images.preview}" />
|
||||
</a>
|
||||
<div class="challenge-footer">
|
||||
<!-- <span>
|
||||
KW {getCalendarWeekFromDate(challenge.activeAt)}
|
||||
</span>-->
|
||||
<h3>
|
||||
{challenge.title}
|
||||
</h3>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<style
|
||||
lang="less"
|
||||
global
|
||||
>
|
||||
@import "../../assets/css/variables.less";
|
||||
li.challenge-preview-item {
|
||||
max-width: 27rem;
|
||||
width: 100%;
|
||||
@media @mobile {
|
||||
width: calc(100vw - 2 * var(--horizontal-default-margin) - 2px);
|
||||
}
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.2rem;
|
||||
a.thumbnail-wrapper {
|
||||
width: 100%;
|
||||
aspect-ratio: 1.2 / 1;
|
||||
display: flex;
|
||||
position: relative;
|
||||
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
}
|
||||
.challenge-footer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.2rem;
|
||||
padding-bottom: 1.2rem;
|
||||
border-bottom: 1px solid var(--krass-kraft-primary);
|
||||
span {
|
||||
color: var(--text-300);
|
||||
}
|
||||
h3 {
|
||||
font-size: 1.6rem;
|
||||
font-weight: 700;
|
||||
color: var(--text-100);
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,67 +0,0 @@
|
||||
<script
|
||||
lang="ts"
|
||||
context="module"
|
||||
>
|
||||
import "simplebar"
|
||||
import "simplebar/dist/simplebar.css"
|
||||
import ResizeObserver from "resize-observer-polyfill"
|
||||
if (typeof window !== "undefined") window.ResizeObserver = ResizeObserver
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import ChallengePreview from "./ChallengePreview.svelte"
|
||||
export let challenges: BKDFChallenge[] = []
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="product-preview-list verticalScrollbar"
|
||||
data-simplebar
|
||||
>
|
||||
<ul class="inner-wrapper">
|
||||
{#each challenges as challenge}
|
||||
<ChallengePreview challenge="{challenge}" />
|
||||
{/each}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<style
|
||||
lang="less"
|
||||
global
|
||||
>
|
||||
.product-preview-list {
|
||||
width: 100%;
|
||||
max-width: var(--normal-max-width);
|
||||
|
||||
.simplebar-wrapper {
|
||||
padding-bottom: 1.2rem;
|
||||
.simplebar-content {
|
||||
max-width: var(--normal-max-width);
|
||||
& > .inner-wrapper {
|
||||
display: flex;
|
||||
gap: 2.2rem;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
.simplebar-track {
|
||||
background-color: rgba(13, 12, 12, 0.25);
|
||||
height: 7px;
|
||||
overflow: visible;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.simplebar-scrollbar {
|
||||
transition-duration: 0ms !important;
|
||||
cursor: pointer;
|
||||
&::before {
|
||||
background-color: var(--bg-100);
|
||||
top: -2px;
|
||||
opacity: 1;
|
||||
border-radius: 0;
|
||||
height: 11px;
|
||||
left: 0px;
|
||||
|
||||
transition-delay: 0s;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,99 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { getDBEntries } from "../../../api"
|
||||
import { spaLink } from "../../actions"
|
||||
import Columns from "../pagebuilder/blocks/Columns.svelte"
|
||||
import ChallengePreviewList from "./ChallengePreviewList.svelte"
|
||||
|
||||
export let chapter: SelfImprovementChapter
|
||||
let challenges: BKDFChallenge[] = []
|
||||
getDBEntries("selfImprovementChallenge", {
|
||||
type: 1,
|
||||
}).then((res) => {
|
||||
challenges = res
|
||||
console.log(challenges)
|
||||
})
|
||||
</script>
|
||||
|
||||
<section class="row hp-row">
|
||||
<Columns
|
||||
block="{{
|
||||
type: 'columns',
|
||||
columns: [
|
||||
{
|
||||
type: 'chapterDescription',
|
||||
verticalAlign: 'middle',
|
||||
chapterDescription: {
|
||||
title: chapter.title,
|
||||
description: chapter.description,
|
||||
type: chapter.type,
|
||||
},
|
||||
},
|
||||
{
|
||||
type: 'image',
|
||||
images: [chapter.previewImage],
|
||||
verticalAlign: 'middle',
|
||||
imageMobileBackground: true,
|
||||
},
|
||||
],
|
||||
}}"
|
||||
/>
|
||||
</section>
|
||||
<section class="challenges-row">
|
||||
<div class="headline">
|
||||
<div class="headline-col">
|
||||
<h2>Challenges</h2>
|
||||
</div>
|
||||
|
||||
<button class="">
|
||||
<a
|
||||
href="/selfimprovement/krasskraft/challenges"
|
||||
use:spaLink
|
||||
>
|
||||
Alle Anzeigen
|
||||
</a>
|
||||
</button>
|
||||
</div>
|
||||
<ChallengePreviewList challenges="{challenges}" />
|
||||
</section>
|
||||
|
||||
<style lang="less">
|
||||
section {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
max-width: var(--normal-max-width);
|
||||
}
|
||||
.row {
|
||||
flex-direction: row;
|
||||
}
|
||||
.hp-row {
|
||||
height: calc(38rem + 145px + 96px + 96px);
|
||||
max-height: 97vh;
|
||||
}
|
||||
.headline {
|
||||
width: 100%;
|
||||
margin-bottom: 1.2rem;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-end;
|
||||
|
||||
h2 {
|
||||
font-size: 4.8rem;
|
||||
font-weight: 500;
|
||||
color: transparent;
|
||||
font-family: sans-serif;
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
-webkit-text-stroke: 2px var(--krass-kraft-primary);
|
||||
}
|
||||
button {
|
||||
a {
|
||||
color: var(--krass-kraft-primary);
|
||||
}
|
||||
}
|
||||
}
|
||||
.challenges-row {
|
||||
margin-bottom: 3.6rem;
|
||||
}
|
||||
</style>
|
||||
@@ -1,105 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { onMount } from "svelte"
|
||||
import Item from "./Item.svelte"
|
||||
import { selfImprovementChapters } from "../../../../store"
|
||||
|
||||
export let block: ContentBlock<"selfImprovementChapterPreview">
|
||||
const chapters = block.selfImprovementChapterPreview
|
||||
let interval: NodeJS.Timeout
|
||||
|
||||
function startInterval() {
|
||||
interval = setInterval(() => {
|
||||
selectedChapter = (selectedChapter + 1) % $selfImprovementChapters.length
|
||||
}, 3000)
|
||||
}
|
||||
|
||||
function stopInterval() {
|
||||
clearInterval(interval)
|
||||
}
|
||||
onMount(() => {
|
||||
startInterval()
|
||||
return () => clearInterval(interval)
|
||||
})
|
||||
let selectedChapter = 0
|
||||
</script>
|
||||
|
||||
<div class="container">
|
||||
<ul>
|
||||
{#each $selfImprovementChapters as chapter, i}
|
||||
<li
|
||||
on:mouseenter="{() => {
|
||||
stopInterval()
|
||||
selectedChapter = i
|
||||
}}"
|
||||
on:mouseleave="{startInterval}"
|
||||
class:active="{selectedChapter === i}"
|
||||
>
|
||||
<Item
|
||||
chapter="{chapter}"
|
||||
bind:selectedChapter="{selectedChapter}"
|
||||
index="{i}"
|
||||
previewImage="{chapters.find((p) => p.chapter === chapter.id)?.previewImage}"
|
||||
/>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<style lang="less">
|
||||
* {
|
||||
transition-duration: 0s;
|
||||
}
|
||||
.container {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
ul {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
width: 100%;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
}
|
||||
|
||||
li {
|
||||
width: 100%;
|
||||
aspect-ratio: 4 / 3;
|
||||
@media (max-width: 1500px) {
|
||||
aspect-ratio: unset;
|
||||
&::before {
|
||||
float: left;
|
||||
padding-top: 125%;
|
||||
content: "";
|
||||
}
|
||||
|
||||
&::after {
|
||||
display: block;
|
||||
content: "";
|
||||
clear: both;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 1500px) {
|
||||
.container {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
ul {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
li {
|
||||
width: 33.33%;
|
||||
}
|
||||
|
||||
li.active {
|
||||
order: -1;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,350 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { onMount, afterUpdate } from "svelte"
|
||||
import MedialibImage from "../../../widgets/MedialibImage.svelte"
|
||||
import { spaLink } from "../../../../actions"
|
||||
import { getVariableNameForChapter } from "../../../../utils"
|
||||
|
||||
export let selectedChapter: number, index: number, chapter: SelfImprovementChapter, previewImage: string
|
||||
const color = chapter.color
|
||||
$: active = selectedChapter === index
|
||||
|
||||
let upperPart: HTMLElement
|
||||
let lowerPart: HTMLElement
|
||||
let topOverlay: HTMLElement
|
||||
let bottomOverlay: HTMLElement
|
||||
|
||||
function updateOverlayHeights() {
|
||||
if (topOverlay && bottomOverlay && upperPart && lowerPart) {
|
||||
topOverlay.style.height = `${upperPart.offsetHeight + 24}px`
|
||||
bottomOverlay.style.height = `${lowerPart.offsetHeight + 24}px`
|
||||
}
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
window.addEventListener("resize", updateOverlayHeights)
|
||||
})
|
||||
|
||||
afterUpdate(() => {
|
||||
updateOverlayHeights()
|
||||
})
|
||||
|
||||
$: if (active) {
|
||||
updateOverlayHeights()
|
||||
} else {
|
||||
if (topOverlay && bottomOverlay) {
|
||||
topOverlay.style.height = "0px"
|
||||
bottomOverlay.style.height = "0px"
|
||||
}
|
||||
}
|
||||
function getSlug(type: number) {
|
||||
if (type == 1) return "krasskraft"
|
||||
return "unknown"
|
||||
}
|
||||
|
||||
let colorName = getVariableNameForChapter(chapter.type)
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="chapter-preview-block"
|
||||
class:active="{active}"
|
||||
>
|
||||
{#if chapter.locked}
|
||||
<div
|
||||
class="locked"
|
||||
class:active="{active}"
|
||||
>
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 72 72"
|
||||
fill="none"
|
||||
>
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
clip-rule="evenodd"
|
||||
d="M15.75 30.165V24C15.75 18.6294 17.8835 13.4787 21.6811 9.68109C25.4787 5.88348 30.6294 3.75 36 3.75C41.3706 3.75 46.5213 5.88348 50.3189 9.68109C54.1165 13.4787 56.25 18.6294 56.25 24V30.165C59.595 30.414 61.77 31.044 63.363 32.637C66 35.271 66 39.516 66 48C66 56.484 66 60.729 63.363 63.363C60.729 66 56.484 66 48 66H24C15.516 66 11.271 66 8.637 63.363C6 60.729 6 56.484 6 48C6 39.516 6 35.271 8.637 32.637C10.227 31.044 12.405 30.414 15.75 30.165ZM20.25 24C20.25 19.8228 21.9094 15.8168 24.8631 12.8631C27.8168 9.90937 31.8228 8.25 36 8.25C40.1772 8.25 44.1832 9.90937 47.1369 12.8631C50.0906 15.8168 51.75 19.8228 51.75 24V30.012C50.601 30 49.353 30 48 30H24C22.644 30 21.399 30 20.25 30.012V24ZM42 48C42 49.5913 41.3679 51.1174 40.2426 52.2426C39.1174 53.3679 37.5913 54 36 54C34.4087 54 32.8826 53.3679 31.7574 52.2426C30.6321 51.1174 30 49.5913 30 48C30 46.4087 30.6321 44.8826 31.7574 43.7574C32.8826 42.6321 34.4087 42 36 42C37.5913 42 39.1174 42.6321 40.2426 43.7574C41.3679 44.8826 42 46.4087 42 48Z"
|
||||
fill="white"></path>
|
||||
</svg>
|
||||
<p>Coming Soon</p>
|
||||
</div>
|
||||
{/if}
|
||||
<div class="background-image">
|
||||
<MedialibImage id="{previewImage}" />
|
||||
|
||||
<div
|
||||
bind:this="{topOverlay}"
|
||||
class="overlay top"
|
||||
style="background-color: var({colorName})"
|
||||
></div>
|
||||
<div
|
||||
bind:this="{bottomOverlay}"
|
||||
class="overlay bottom"
|
||||
style="background-color: var({colorName})"
|
||||
></div>
|
||||
<div
|
||||
class="overlay left"
|
||||
style="background-color: var({colorName})"
|
||||
></div>
|
||||
<div
|
||||
class="overlay right"
|
||||
style="background-color: var({colorName})"
|
||||
></div>
|
||||
</div>
|
||||
<div
|
||||
class="content"
|
||||
class:active="{active}"
|
||||
>
|
||||
<div
|
||||
bind:this="{upperPart}"
|
||||
class:active="{active}"
|
||||
class="upper-part"
|
||||
>
|
||||
<h4
|
||||
class:active="{active}"
|
||||
style="{!active ? 'color: ' + color : 'color: var(--bg-100)'}"
|
||||
>
|
||||
{chapter.alias}
|
||||
</h4>
|
||||
<h3
|
||||
class:active="{active}"
|
||||
style="{!active ? 'color: ' + color : 'color: var(--bg-100)'}"
|
||||
>
|
||||
{chapter.title}
|
||||
</h3>
|
||||
<p
|
||||
class:active="{active}"
|
||||
style="{!active ? 'color: ' + 'transparent' : 'color: var(--bg-100)'}"
|
||||
>
|
||||
{chapter.shortDescription}
|
||||
</p>
|
||||
</div>
|
||||
<div
|
||||
bind:this="{lowerPart}"
|
||||
class="lower-part"
|
||||
class:active="{active}"
|
||||
>
|
||||
<button
|
||||
style="background-color: var({colorName})"
|
||||
disabled="{chapter.locked}"
|
||||
>
|
||||
<a
|
||||
href="/selfimprovement/{getSlug(chapter.type)}"
|
||||
use:spaLink
|
||||
aria-disabled="{chapter.locked ? 'true' : 'false'}"
|
||||
>
|
||||
Weiter
|
||||
</a>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style
|
||||
lang="less"
|
||||
global
|
||||
>
|
||||
@import "../../../../assets/css/variables.less";
|
||||
.chapter-preview-block {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
.locked {
|
||||
position: absolute;
|
||||
z-index: 100;
|
||||
pointer-events: none;
|
||||
background: rgba(13, 12, 12, 0.5);
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
p {
|
||||
color: white;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
svg {
|
||||
width: 72px;
|
||||
height: 72px;
|
||||
}
|
||||
@media (max-width: 1500px) {
|
||||
&:not(.active) {
|
||||
svg {
|
||||
height: 24px;
|
||||
width: 24px;
|
||||
}
|
||||
p {
|
||||
font-size: 0.6rem;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.background-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
z-index: 0;
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
.overlay {
|
||||
position: absolute;
|
||||
transition: all 0.3s ease;
|
||||
&.top {
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 0;
|
||||
}
|
||||
&.bottom {
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 0;
|
||||
}
|
||||
&.left {
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 0;
|
||||
}
|
||||
&.right {
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
width: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
&.active .background-image .overlay.top {
|
||||
height: auto;
|
||||
}
|
||||
&.active .background-image .overlay.bottom {
|
||||
height: auto;
|
||||
}
|
||||
&.active .background-image .overlay.left {
|
||||
width: 3.6rem;
|
||||
@media (max-width: 600px) {
|
||||
width: 0.6rem;
|
||||
}
|
||||
}
|
||||
&.active .background-image .overlay.right {
|
||||
width: 3.6rem;
|
||||
@media (max-width: 600px) {
|
||||
width: 0.6rem;
|
||||
}
|
||||
}
|
||||
.content {
|
||||
transition: border-color 0.3s ease, color 0.3s ease;
|
||||
border-top: 2.4rem solid transparent;
|
||||
border-bottom: 2.4rem solid transparent;
|
||||
border-left: 3.6rem solid transparent;
|
||||
border-right: 3.6rem solid transparent;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-between;
|
||||
position: relative;
|
||||
z-index: 10;
|
||||
.upper-part {
|
||||
transition: border-color 0.3s ease, color 0.3s ease, background-color 0.3s ease;
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.2rem;
|
||||
h4 {
|
||||
margin: 0px;
|
||||
text-transform: uppercase;
|
||||
font-size: 1.2rem;
|
||||
color: var(--bg-100);
|
||||
}
|
||||
h3 {
|
||||
font-size: 3rem;
|
||||
font-weight: 700;
|
||||
margin-bottom: 1.2rem;
|
||||
color: var(--bg-100);
|
||||
}
|
||||
p {
|
||||
font-size: 1.2rem;
|
||||
color: var(--bg-100);
|
||||
}
|
||||
@media (max-width: 1500px) {
|
||||
h3:not(.active) {
|
||||
font-size: 1.6rem;
|
||||
}
|
||||
p:not(.active) {
|
||||
font-size: 1rem;
|
||||
}
|
||||
h4:not(.active) {
|
||||
font-size: 1rem;
|
||||
}
|
||||
}
|
||||
@media (max-width: 600px) {
|
||||
gap: 0.6rem;
|
||||
h3 {
|
||||
font-size: 1.6rem;
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
h3:not(.active) {
|
||||
font-size: 1rem;
|
||||
}
|
||||
p {
|
||||
margin-top: 0px;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
p:not(.active) {
|
||||
font-size: 0.5rem;
|
||||
}
|
||||
h4 {
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
h4:not(.active) {
|
||||
font-size: 0.5rem;
|
||||
}
|
||||
}
|
||||
|
||||
border-bottom: 2.4rem solid transparent;
|
||||
@media (max-width: 600px) {
|
||||
border-width: 0.6rem !important;
|
||||
}
|
||||
}
|
||||
@media (max-width: 600px) {
|
||||
border-width: 0.6rem !important;
|
||||
}
|
||||
.lower-part {
|
||||
border-top: 2.4rem solid transparent;
|
||||
@media (max-width: 600px) {
|
||||
border-width: 0rem !important;
|
||||
}
|
||||
transition: border-color 0.3s ease, color 0.3s ease, background-color 0.3s ease;
|
||||
button {
|
||||
font-weight: 700;
|
||||
font-size: 1.2rem;
|
||||
text-transform: uppercase;
|
||||
padding: 6px 12px;
|
||||
@media (max-width: 1500px) {
|
||||
padding-left: 0px;
|
||||
}
|
||||
color: var(--bg-100);
|
||||
&[disabled] {
|
||||
cursor: not-allowed;
|
||||
pointer-events: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
&:not(.active) {
|
||||
@media (max-width: 1500px) {
|
||||
.lower-part {
|
||||
button {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,261 +0,0 @@
|
||||
<script
|
||||
lang="ts"
|
||||
context="module"
|
||||
>
|
||||
import "simplebar"
|
||||
import "simplebar/dist/simplebar.css"
|
||||
import ResizeObserver from "resize-observer-polyfill"
|
||||
if (typeof window !== "undefined") window.ResizeObserver = ResizeObserver
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { getCachedEntries } from "../../../../../api"
|
||||
import { getBCGraphProductsByIds } from "../../../../functions/CommerceAPIs/bigCommerce/product"
|
||||
import { backgroundImages } from "../../../../store"
|
||||
import MedialibImage from "../../../widgets/MedialibImage.svelte"
|
||||
|
||||
export let block: ContentBlock<"ratingPreview">
|
||||
let ratings: ProductRating[] = []
|
||||
let productsMap: Record<string, BKDFProduct> = {}
|
||||
getCachedEntries("rating", {
|
||||
_id: {
|
||||
$in: block?.ratingsPreview?.ratings?.map((rating) => rating.rating),
|
||||
},
|
||||
}).then((entries) => {
|
||||
getBCGraphProductsByIds(entries.map((entry) => String(entry.bigCommerceProductId))).then((products) => {
|
||||
productsMap = products.reduce((acc, product) => {
|
||||
acc[product.id] = product
|
||||
return acc
|
||||
}, {})
|
||||
ratings = entries
|
||||
})
|
||||
})
|
||||
|
||||
function formatDate(date: string) {
|
||||
return new Date(date).toLocaleDateString("de-DE", {
|
||||
year: "numeric",
|
||||
month: "long",
|
||||
day: "numeric",
|
||||
})
|
||||
}
|
||||
|
||||
function returnWidthForRating(rating: ProductRating) {
|
||||
const ratingP =
|
||||
(rating.rating.quality + rating.rating.priceQualityRatio + rating.rating.comfort + rating.rating.overall) *
|
||||
5
|
||||
return ratingP
|
||||
}
|
||||
|
||||
function returnAvgForRating(rating: ProductRating) {
|
||||
const ratingP =
|
||||
(rating.rating.quality + rating.rating.priceQualityRatio + rating.rating.comfort + rating.rating.overall) /
|
||||
4
|
||||
return ratingP.toFixed(1)
|
||||
}
|
||||
</script>
|
||||
|
||||
{#if ratings.length}
|
||||
<div
|
||||
data-simplebar
|
||||
class="review-list-container horizontalScrollbar"
|
||||
>
|
||||
<ul class="review-list">
|
||||
{#each ratings as rating}
|
||||
<li class="review-item">
|
||||
<div class="upper-box">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="48"
|
||||
height="49"
|
||||
viewBox="0 0 48 49"
|
||||
fill="none"
|
||||
>
|
||||
<path
|
||||
d="M48 0.25V47.5833L0 0.25H48Z"
|
||||
fill="white"></path>
|
||||
</svg>
|
||||
<div class="background-image">
|
||||
<img
|
||||
src="{productsMap[rating.bigCommerceProductId].featuredImage.url}"
|
||||
alt="{rating.title}"
|
||||
/>
|
||||
<MedialibImage
|
||||
id="{$backgroundImages?.standard}"
|
||||
filter="l"
|
||||
/>
|
||||
</div>
|
||||
<div class="overlay">
|
||||
<h3>
|
||||
{rating.title}
|
||||
</h3>
|
||||
<p>{rating.comment}</p>
|
||||
<div class="date">{formatDate(rating.review_date)}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="rating-bar">
|
||||
<div
|
||||
class="rating-filled"
|
||||
style="width: {returnWidthForRating(rating)}%; "
|
||||
>
|
||||
<div class="star-wrapper">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
width="25"
|
||||
height="25"
|
||||
viewBox="0 0 25 25"
|
||||
fill="none"
|
||||
>
|
||||
<path
|
||||
d="M16.7692 23.1373L16.7717 23.1388C17.1926 23.3925 17.6791 23.5165 18.1701 23.4952C18.6612 23.474 19.1351 23.3085 19.5326 23.0193C19.9301 22.7302 20.2335 22.3303 20.405 21.8697C20.5762 21.4098 20.6083 20.9097 20.4972 20.4318C20.497 20.431 20.4968 20.4302 20.4967 20.4294L19.3653 15.5223L22.8594 12.4729H22.8607L23.1422 12.2302C23.515 11.9087 23.7846 11.4842 23.9171 11.0101C24.0496 10.536 24.0392 10.0333 23.8872 9.56506C23.7352 9.09682 23.4483 8.68389 23.0626 8.37804C22.6772 8.0725 22.2103 7.88743 21.7202 7.84595C21.7197 7.84591 21.7192 7.84587 21.7187 7.84583L16.7503 7.41534L14.8029 2.78442C14.8027 2.78387 14.8024 2.78331 14.8022 2.78276C14.6125 2.32902 14.2929 1.94145 13.8836 1.66874C13.4738 1.39569 12.9924 1.25 12.5 1.25C12.0076 1.25 11.5262 1.3957 11.1164 1.66874C10.7069 1.94157 10.3872 2.32937 10.1976 2.78337C10.1974 2.78372 10.1973 2.78407 10.1971 2.78442L8.25556 7.41539L3.28596 7.84583C3.28549 7.84586 3.28502 7.8459 3.28455 7.84594C2.7945 7.8874 2.32754 8.07248 1.94214 8.37804C1.55638 8.68388 1.2695 9.09682 1.11748 9.56506C0.965459 10.0333 0.955069 10.536 1.08761 11.0101L2.05069 10.7409L1.08761 11.0101C1.21983 11.4831 1.48842 11.9066 1.85981 12.2279L5.63577 15.5275L4.50617 20.4294C4.50605 20.43 4.50592 20.4305 4.50579 20.4311C4.39454 20.9092 4.42654 21.4096 4.59781 21.8697C4.76928 22.3303 5.07273 22.7302 5.47023 23.0193C5.86773 23.3085 6.34163 23.474 6.8327 23.4952C7.32376 23.5165 7.81019 23.3925 8.23118 23.1388L8.23442 23.1368L12.4968 20.546L16.7692 23.1373Z"
|
||||
fill="#2F4858"
|
||||
stroke="white"
|
||||
stroke-width="2"></path>
|
||||
</svg>
|
||||
<span>{returnAvgForRating(rating)}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style
|
||||
lang="less"
|
||||
global
|
||||
>
|
||||
.review-list-container {
|
||||
width: 100%;
|
||||
max-width: var(--normal-max-width);
|
||||
.simplebar-wrapper {
|
||||
padding-bottom: 1.2rem;
|
||||
.simplebar-content {
|
||||
max-width: var(--normal-max-width);
|
||||
& > .inner-wrapper {
|
||||
display: flex;
|
||||
gap: 2.2rem;
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
.simplebar-track {
|
||||
background-color: rgba(13, 12, 12, 0.25);
|
||||
height: 7px;
|
||||
overflow: visible;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
.simplebar-scrollbar {
|
||||
transition-duration: 0ms !important;
|
||||
cursor: pointer;
|
||||
&::before {
|
||||
background-color: var(--bg-100);
|
||||
top: -2px;
|
||||
opacity: 1;
|
||||
border-radius: 0;
|
||||
height: 11px;
|
||||
left: 0px;
|
||||
|
||||
transition-delay: 0s;
|
||||
}
|
||||
}
|
||||
.review-list {
|
||||
display: flex;
|
||||
|
||||
gap: 1.2rem;
|
||||
height: 400px;
|
||||
align-items: flex-start;
|
||||
.review-item {
|
||||
overflow: visible;
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1px;
|
||||
.upper-box {
|
||||
width: 320px;
|
||||
height: 320px;
|
||||
position: relative;
|
||||
background: linear-gradient(180deg, rgba(0, 0, 0, 0) 0%, #000100);
|
||||
svg {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
right: 0px;
|
||||
}
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
position: absolute;
|
||||
&:first-of-type {
|
||||
z-index: -1;
|
||||
}
|
||||
&:last-of-type {
|
||||
z-index: -2;
|
||||
}
|
||||
}
|
||||
.overlay {
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
padding: 1.2rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 0.6rem;
|
||||
h3 {
|
||||
color: var(--neutral-white);
|
||||
font-family: Outfit;
|
||||
font-size: 1rem;
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
line-height: normal;
|
||||
}
|
||||
p {
|
||||
color: var(--neutral-white);
|
||||
font-family: Poly;
|
||||
font-size: 1rem;
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
line-height: normal;
|
||||
}
|
||||
.date {
|
||||
color: var(--neutral-white);
|
||||
border-top: 1px solid var(--neutral-white);
|
||||
font-size: 0.7rem;
|
||||
padding-top: 6px;
|
||||
width: fit-content;
|
||||
}
|
||||
}
|
||||
}
|
||||
.rating-bar {
|
||||
width: 100%;
|
||||
height: 9px;
|
||||
background: rgba(47, 72, 88, 0.2);
|
||||
.rating-filled {
|
||||
background: var(--text-invers-100);
|
||||
height: 100%;
|
||||
left: 0px;
|
||||
position: relative;
|
||||
.star-wrapper {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-direction: column;
|
||||
gap: 0.6rem;
|
||||
padding: 0spx;
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
right: 0%;
|
||||
transform: translate(50%, -35%);
|
||||
span {
|
||||
font-family: Outfit;
|
||||
font-size: 20px;
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
line-height: 14px;
|
||||
margin-top: -4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,70 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from "svelte"
|
||||
import { deleteCartItem, updateCartItem } from "../../../functions/CommerceAPIs/bigCommerce/cart"
|
||||
import { deleteCookie } from "../../../functions/utils"
|
||||
import ProductInCartPreview from "../product/ProductInCartPreview.svelte"
|
||||
import LoadingWrapper from "../../widgets/LoadingWrapper.svelte"
|
||||
const dispatcher = createEventDispatcher()
|
||||
async function removeItem(id: string) {
|
||||
if (cart.lines.length == 1) {
|
||||
deleteCookie("cartId")
|
||||
await deleteCartItem(cart.id, id, true)
|
||||
dispatcher("removeCart")
|
||||
} else cart = await deleteCartItem(cart.id, id)
|
||||
}
|
||||
interface UpdateItemQuantity {
|
||||
merchandiseId: string
|
||||
productId: string
|
||||
quantity: number
|
||||
entityId: string
|
||||
}
|
||||
async function updateItemQuantity(line: UpdateItemQuantity) {
|
||||
cart = await updateCartItem(cart.id, line)
|
||||
}
|
||||
export let cart: BKDFCart,
|
||||
hideActions = false,
|
||||
showQuantity: boolean = false
|
||||
let loading = -1
|
||||
</script>
|
||||
|
||||
<ul class="products">
|
||||
{#each cart.lines as item, i (item?.id)}
|
||||
<li class="product">
|
||||
<LoadingWrapper
|
||||
active="{loading == i}"
|
||||
styles=" display: flex; width: 100%;flex-direction: column; gap: 24px;"
|
||||
>
|
||||
<ProductInCartPreview
|
||||
item="{item}"
|
||||
hideActions="{hideActions}"
|
||||
showQuantity="{showQuantity}"
|
||||
on:updateQuantity="{(e) => {
|
||||
loading = i
|
||||
updateItemQuantity({
|
||||
merchandiseId: item.merchandise.id,
|
||||
quantity: e.detail.quantity,
|
||||
productId: item.merchandise.product.id,
|
||||
entityId: item.id,
|
||||
}).finally(() => (loading = -1))
|
||||
}}"
|
||||
on:remove="{() => {
|
||||
loading = i
|
||||
removeItem(item.id).finally(() => (loading = -1))
|
||||
}}"
|
||||
/></LoadingWrapper
|
||||
>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>
|
||||
|
||||
<style lang="less">
|
||||
.products {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
flex-direction: column;
|
||||
gap: 1.2rem;
|
||||
.product {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,200 +0,0 @@
|
||||
<script
|
||||
lang="ts"
|
||||
context="module"
|
||||
>
|
||||
import "simplebar"
|
||||
import "simplebar/dist/simplebar.css"
|
||||
import ResizeObserver from "resize-observer-polyfill"
|
||||
if (typeof window !== "undefined") window.ResizeObserver = ResizeObserver
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import { mdiCartCheck } from "@mdi/js"
|
||||
import { getCart } from "../../../functions/CommerceAPIs/bigCommerce/cart"
|
||||
import { deleteCookie, getCookie } from "../../../functions/utils"
|
||||
import Icon from "../../widgets/Icon.svelte"
|
||||
import CartProducts from "./CartProducts.svelte"
|
||||
import Loader from "../Loader.svelte"
|
||||
import { newNotification } from "../../../store"
|
||||
import { minimumForFreeShipping } from "../../../../config"
|
||||
|
||||
const cartId = getCookie("cartId")
|
||||
export let cart: BKDFCart | null = null,
|
||||
hideActions = false,
|
||||
showQuantity: boolean = false,
|
||||
shippingIncludedInTotal = false
|
||||
|
||||
let loading = false
|
||||
async function setCart(id: string) {
|
||||
loading = true
|
||||
cart = await getCart(id).catch(() => {
|
||||
loading = false
|
||||
newNotification({
|
||||
class: "error",
|
||||
html: `Fehler beim Laden des Warenkorbs. Bitte laden Sie die Seite neu.`,
|
||||
})
|
||||
deleteCookie("cartId")
|
||||
})
|
||||
loading = false
|
||||
}
|
||||
|
||||
async function refetchCartAndGoToCheckout() {
|
||||
loading = true
|
||||
cart = await getCart(cartId).catch(() => {
|
||||
loading = false
|
||||
newNotification({
|
||||
class: "error",
|
||||
html: `Fehler beim Laden des Warenkorbs. Bitte laden Sie die Seite neu.`,
|
||||
})
|
||||
deleteCookie("cartId")
|
||||
})
|
||||
loading = false
|
||||
if (cart) {
|
||||
window.location.href = cart.checkoutUrl
|
||||
}
|
||||
}
|
||||
if (cartId && !cart) setCart(cartId)
|
||||
</script>
|
||||
|
||||
{#if cart}
|
||||
<div
|
||||
class="product-listing-wrapper"
|
||||
data-simplebar
|
||||
>
|
||||
<section class="product-listing-inner">
|
||||
<CartProducts
|
||||
bind:cart="{cart}"
|
||||
on:removeCart="{() => (cart = null)}"
|
||||
hideActions="{hideActions}"
|
||||
showQuantity="{showQuantity}"
|
||||
/>
|
||||
</section>
|
||||
</div>
|
||||
<section
|
||||
class="cart-summary"
|
||||
style="{cart.checkoutUrl ? '' : 'height: unset'} "
|
||||
>
|
||||
{#if Number(cart.cost.discountedAmount.amount) > 0}
|
||||
<div class="discount">
|
||||
<div>Rabatt</div>
|
||||
<div>-{cart.cost.discountedAmount.amount} €</div>
|
||||
</div>
|
||||
{/if}
|
||||
{#if Number(cart?.cost?.couponDiscount?.amount || 0) > 0}
|
||||
<div class="discount">
|
||||
<div>Rabatt Codes</div>
|
||||
<div>-{cart.cost.couponDiscount.amount} €</div>
|
||||
</div>
|
||||
{/if}
|
||||
<div class="discount">
|
||||
<div>Versand</div>
|
||||
<div>
|
||||
{#if Number(cart.cost.amount.amount) >= minimumForFreeShipping}
|
||||
<span>Kostenfrei</span>
|
||||
{:else}
|
||||
<span>5,00 €</span>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
<div class="total">
|
||||
<em>Gesamt</em>
|
||||
<div>
|
||||
{(
|
||||
Number(cart.cost.amount.amount) +
|
||||
(Number(cart.cost.amount.amount) >= minimumForFreeShipping || shippingIncludedInTotal ? 0 : 5)
|
||||
).toFixed(2)} €
|
||||
</div>
|
||||
</div>
|
||||
{#if cart.checkoutUrl}
|
||||
<button
|
||||
class="checkout"
|
||||
on:click="{() => refetchCartAndGoToCheckout()}"
|
||||
><Icon path="{mdiCartCheck}" /> <span>Zur Kasse</span></button
|
||||
>
|
||||
{/if}
|
||||
</section>
|
||||
{:else if loading}
|
||||
<Loader size="3" />
|
||||
{:else}
|
||||
<p class="no-products">Keine Produkte im Warenkorb</p>
|
||||
{/if}
|
||||
|
||||
<style lang="less">
|
||||
@import "../../../assets/css/variables.less";
|
||||
.no-products {
|
||||
padding-left: 90px;
|
||||
@media @mobile {
|
||||
padding-left: 38px;
|
||||
}
|
||||
}
|
||||
.product-listing-wrapper {
|
||||
width: 100%;
|
||||
max-height: calc(100% - 255px);
|
||||
@media @mobile {
|
||||
max-height: calc(100% - 200px);
|
||||
}
|
||||
flex-grow: 1;
|
||||
.product-listing-inner {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
padding: 24px 1rem 24px 90px;
|
||||
@media @mobile {
|
||||
padding: 24px 2.8rem 24px 38px;
|
||||
}
|
||||
}
|
||||
}
|
||||
.cart-summary {
|
||||
display: flex;
|
||||
|
||||
flex-direction: column;
|
||||
padding: 24px 1rem 24px 90px;
|
||||
@media @mobile {
|
||||
padding: 24px 2rem 24px 38px;
|
||||
}
|
||||
border-top: 1px solid var(--text-300);
|
||||
height: 255px;
|
||||
@media @mobile {
|
||||
height: 200px;
|
||||
padding-top: 1.2rem;
|
||||
padding-bottom: 1.2rem;
|
||||
}
|
||||
.discount,
|
||||
.total {
|
||||
padding: 0.6rem 1.2rem;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.discount {
|
||||
border-bottom: 1px solid var(--text-300);
|
||||
}
|
||||
.total {
|
||||
background-color: var(--bg-100);
|
||||
color: var(--neutral-white);
|
||||
em {
|
||||
font-weight: 700;
|
||||
font-size: 1rem;
|
||||
line-height: 0.7rem;
|
||||
}
|
||||
}
|
||||
.checkout {
|
||||
width: 100%;
|
||||
padding: 0.6rem 1.2rem;
|
||||
font-weight: 700;
|
||||
color: var(--neutral-white);
|
||||
font-size: 1rem;
|
||||
line-height: 0.7rem;
|
||||
background-color: var(--primary-100);
|
||||
box-shadow: 0px -2px 0px 0px rgba(0, 0, 0, 0.25);
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
margin: 35px 0px;
|
||||
border-radius: 5px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,86 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { createEventDispatcher } from "svelte"
|
||||
import { getCart } from "../../../functions/CommerceAPIs/bigCommerce/cart"
|
||||
import { getBCGraphProductsByCategory } from "../../../functions/CommerceAPIs/bigCommerce/product"
|
||||
import { getCookie } from "../../../functions/utils"
|
||||
import ProductPreview from "../product/ProductPreview.svelte"
|
||||
const dispatch = createEventDispatcher()
|
||||
const cartId = getCookie("cartId")
|
||||
let products: BKDFProduct[] = []
|
||||
async function loadProductRecommendations() {
|
||||
const cart = await getCart(cartId).catch((error) => {
|
||||
console.log(error)
|
||||
})
|
||||
if (cart) {
|
||||
const categories = cart.lines.map((l) => {
|
||||
return l.merchandise.product.categories[0].id
|
||||
})
|
||||
const productsOfCategories: BKDFProduct[] = []
|
||||
const promises = categories.map(async (category) => {
|
||||
const products = await getBCGraphProductsByCategory(category)
|
||||
if (products.length > 0) {
|
||||
products.forEach((p) => {
|
||||
if (!productsOfCategories.find((product) => product.id === p.id)) {
|
||||
productsOfCategories.push(p)
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
await Promise.all(promises)
|
||||
if (productsOfCategories.length == 0) {
|
||||
dispatch("removeOverlay")
|
||||
return
|
||||
}
|
||||
// take 5 random indices of products not already in cart and every index only once and make sure at
|
||||
let i = 0
|
||||
const randomIndizes: number[] = []
|
||||
while (randomIndizes.length < 5) {
|
||||
i++
|
||||
if (i > 100) {
|
||||
break
|
||||
}
|
||||
const randomIndex = Math.floor(Math.random() * productsOfCategories.length)
|
||||
if (
|
||||
!randomIndizes.includes(randomIndex) &&
|
||||
!cart.lines.find((line) => line.merchandise.product.id === productsOfCategories[randomIndex].id)
|
||||
) {
|
||||
randomIndizes.push(randomIndex)
|
||||
}
|
||||
}
|
||||
|
||||
products = randomIndizes.map((index) => productsOfCategories[index])
|
||||
dispatch("showOverlay")
|
||||
} else dispatch("removeOverlay")
|
||||
}
|
||||
loadProductRecommendations()
|
||||
</script>
|
||||
|
||||
{#if products.length > 0}
|
||||
<div class="product-overlay-listing-wrapper">
|
||||
<ul>
|
||||
{#each products as product}
|
||||
<ProductPreview
|
||||
product="{product}"
|
||||
brightVersion="{true}"
|
||||
/>
|
||||
{/each}
|
||||
</ul>
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style
|
||||
lang="less"
|
||||
global
|
||||
>
|
||||
.product-overlay-listing-wrapper {
|
||||
li.product-preview {
|
||||
width: 100% !important;
|
||||
}
|
||||
& > ul {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2.4rem;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,60 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { getBCGraphProductsByIds } from "../../../functions/CommerceAPIs/bigCommerce/product"
|
||||
import { wishlist } from "../../../store"
|
||||
import Loader from "../Loader.svelte"
|
||||
import ProductInCartPreview from "../product/ProductInCartPreview.svelte"
|
||||
async function loadProducts(): Promise<Partial<BKDFCartItem[]>> {
|
||||
const products = await getBCGraphProductsByIds($wishlist.items.map((p) => String(p.product_id)))
|
||||
const productsFormated: Partial<BKDFCartItem[]> = products.map((p) => {
|
||||
const variant = p.variants.find(
|
||||
(v) =>
|
||||
Number(v.id) ==
|
||||
Number($wishlist.items.find((w) => String(w.product_id) === String(p.id))?.variant_id)
|
||||
)
|
||||
return {
|
||||
previewImage: p.featuredImage,
|
||||
merchandise: {
|
||||
id: variant.id,
|
||||
selectedOptions: variant?.selectedOptions,
|
||||
product: p,
|
||||
},
|
||||
}
|
||||
})
|
||||
return productsFormated
|
||||
}
|
||||
let reload = false
|
||||
</script>
|
||||
|
||||
{#key $wishlist?.items?.length}
|
||||
{#if !$wishlist?.items?.length}
|
||||
<p>Keine Produkte in der Wunschliste</p>
|
||||
{:else}
|
||||
{#await loadProducts()}
|
||||
<Loader size="3" />
|
||||
{:then products}
|
||||
{#if products.length == 0}
|
||||
<p>Keine Produkte in der Wunschliste</p>
|
||||
{:else}
|
||||
<ul>
|
||||
{#each products as product}
|
||||
<li>
|
||||
<ProductInCartPreview
|
||||
item="{product}"
|
||||
on:removedFavorite="{() => (reload = !reload)}"
|
||||
hideActions="{true}"
|
||||
showFavoriteActionButtons="{true}"
|
||||
/>
|
||||
</li>
|
||||
{/each}
|
||||
</ul>{/if}
|
||||
{/await}
|
||||
{/if}
|
||||
{/key}
|
||||
|
||||
<style lang="less">
|
||||
ul {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.2rem;
|
||||
}
|
||||
</style>
|
||||
@@ -1,44 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { login } from "../../../store"
|
||||
import Button from "../../widgets/Button.svelte"
|
||||
import FavoriteListItems from "./FavoriteListItems.svelte"
|
||||
</script>
|
||||
|
||||
<section class="wishlistWrapper">
|
||||
{#if $login}
|
||||
<FavoriteListItems />
|
||||
{:else}
|
||||
<p>Um deine Favoriten zu sehen, musst du eingeloggt sein.</p>
|
||||
<div>
|
||||
<Button
|
||||
button="{{
|
||||
ctaType: 0,
|
||||
page: '/profile/login',
|
||||
buttonTarget: '_self',
|
||||
buttonText: 'Einloggen',
|
||||
}}"
|
||||
/>
|
||||
<Button
|
||||
button="{{
|
||||
ctaType: 1,
|
||||
page: '/profile/register',
|
||||
buttonTarget: '_self',
|
||||
buttonText: 'Registrieren',
|
||||
}}"
|
||||
/>
|
||||
</div>
|
||||
{/if}
|
||||
</section>
|
||||
|
||||
<style lang="less">
|
||||
@import "../../../assets/css/variables.less";
|
||||
.wishlistWrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2.4rem;
|
||||
padding: 24px 1rem 24px 90px;
|
||||
@media @mobile {
|
||||
padding: 24px 2rem 24px 38px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,134 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { getCachedEntries } from "../../../../api"
|
||||
import { spaLink } from "../../../actions"
|
||||
import MedialibImage from "../../widgets/MedialibImage.svelte"
|
||||
import Loader from "../Loader.svelte"
|
||||
import CardWrapper from "../profile/CardWrapper.svelte"
|
||||
export let chapter: HelpCenterChapter
|
||||
let questions: ContentEntry[]
|
||||
let loading = true
|
||||
getCachedEntries("content", { type: "helpcenterQuestion", path: { $in: chapter.questions.map((q) => q.page) } })
|
||||
.then((res) => {
|
||||
questions = res
|
||||
})
|
||||
.finally(() => (loading = false))
|
||||
</script>
|
||||
|
||||
<CardWrapper>
|
||||
<div
|
||||
class="iconCardWrapper"
|
||||
slot="header"
|
||||
>
|
||||
<div class="inner-wrapper">
|
||||
<div class="blackBox"></div>
|
||||
<MedialibImage id="{chapter.brightIcon}" />
|
||||
</div>
|
||||
</div>
|
||||
<div class="innerWrapper">
|
||||
<h3>{chapter.title}</h3>
|
||||
<ul class="questions">
|
||||
{#if loading}
|
||||
<Loader size="3" />
|
||||
{:else if questions}
|
||||
{#each questions as question, i}
|
||||
{#if i < 3}
|
||||
<li class="question">
|
||||
<a
|
||||
href="/helpCenter/{chapter.slug}{question.path}"
|
||||
use:spaLink
|
||||
>
|
||||
{question.question}
|
||||
</a>
|
||||
</li>
|
||||
{/if}
|
||||
{/each}
|
||||
{:else}
|
||||
<p>Es wurden keine Fragen gefunden.</p>
|
||||
{/if}
|
||||
</ul>
|
||||
<div class="action-row">
|
||||
<button class="cta secondary">
|
||||
<a
|
||||
href="/helpCenter/{chapter.slug}"
|
||||
use:spaLink>weitere Fragen</a
|
||||
></button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
</CardWrapper>
|
||||
|
||||
<style
|
||||
lang="less"
|
||||
global
|
||||
>
|
||||
.iconCardWrapper {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 50%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
transform: translateX(-50%) translateY(-50%);
|
||||
width: 4.8rem;
|
||||
height: 4.8rem;
|
||||
.inner-wrapper {
|
||||
position: relative;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
.blackBox {
|
||||
position: absolute;
|
||||
width: 2.4rem;
|
||||
height: 2.4rem;
|
||||
background-color: var(--bg-100);
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
img {
|
||||
width: 1.6rem;
|
||||
height: 1.6rem;
|
||||
position: relative;
|
||||
object-fit: contain;
|
||||
z-index: 100;
|
||||
}
|
||||
}
|
||||
}
|
||||
.innerWrapper {
|
||||
padding: 0px 2.4rem 2.4rem 2.4rem;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
gap: 1.8rem;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
h3 {
|
||||
font-size: 1.6rem;
|
||||
line-height: 1.6rem;
|
||||
font-weight: 700;
|
||||
border-bottom: 2px solid var(--bg-100);
|
||||
padding-bottom: 0.6rem;
|
||||
}
|
||||
ul {
|
||||
display: flex !important;
|
||||
flex-direction: column !important;
|
||||
flex-grow: 1;
|
||||
gap: 0.6rem !important;
|
||||
width: 100%;
|
||||
li {
|
||||
padding: 0.6rem 1.2rem;
|
||||
border-radius: 2px;
|
||||
width: 100%;
|
||||
border: 1px solid var(--bg-100);
|
||||
a {
|
||||
font-size: 1rem;
|
||||
font-weight: 400;
|
||||
font-family: Outfit;
|
||||
}
|
||||
}
|
||||
}
|
||||
.action-row {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,182 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { mdiArrowLeft } from "@mdi/js"
|
||||
import { spaLink } from "../../../actions"
|
||||
import Icon from "../../widgets/Icon.svelte"
|
||||
import { getHelpCenterChapters } from "../../../functions/CommerceAPIs/tibiEndpoints/helpCenter"
|
||||
import Loader from "../Loader.svelte"
|
||||
import MedialibImage from "../../widgets/MedialibImage.svelte"
|
||||
import ChapterQuestionPreview from "./ChapterQuestionPreview.svelte"
|
||||
import ChapterQuestionDetailed from "./ChapterQuestionDetailed.svelte"
|
||||
|
||||
export let location: LocationStore
|
||||
$: pathBefore = location.path.split("/").slice(0, -1).join("/")
|
||||
let chapters: HelpCenterChapter[]
|
||||
getHelpCenterChapters().then((res) => {
|
||||
chapters = res
|
||||
})
|
||||
</script>
|
||||
|
||||
<div class="chapterDetailed">
|
||||
<div class="return">
|
||||
<a
|
||||
use:spaLink
|
||||
href="{pathBefore}"><Icon path="{mdiArrowLeft}" /> <span>Zurück</span></a
|
||||
>
|
||||
</div>
|
||||
<div class="main">
|
||||
<ul class="nav">
|
||||
{#if !Array.isArray(chapters)}
|
||||
<Loader size="3" />
|
||||
{:else}
|
||||
{#each chapters as chapter}
|
||||
<li class:active="{location.path.includes(`/helpCenter/${chapter.slug}`)}">
|
||||
<a
|
||||
href="/helpCenter/{chapter.slug}"
|
||||
use:spaLink
|
||||
>
|
||||
<span class="icon">
|
||||
<MedialibImage
|
||||
id="{location.path.includes(`/helpCenter/${chapter.slug}`)
|
||||
? chapter.brightIcon
|
||||
: chapter.darkIcon}"
|
||||
/>
|
||||
</span>
|
||||
{chapter.title}
|
||||
</a>
|
||||
</li>
|
||||
{/each}
|
||||
{/if}
|
||||
</ul>
|
||||
{#if pathBefore == "/helpCenter" || pathBefore == "/helpCenter/"}
|
||||
<ul class="questions">
|
||||
{#if !Array.isArray(chapters)}
|
||||
<Loader size="3" />
|
||||
{:else}
|
||||
{#each chapters as chapter}
|
||||
<ChapterQuestionPreview
|
||||
chapter="{chapter}"
|
||||
location="{location}"
|
||||
/>
|
||||
{/each}
|
||||
{/if}
|
||||
</ul>
|
||||
{:else}
|
||||
<ChapterQuestionDetailed location="{location}" />
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style
|
||||
lang="less"
|
||||
global
|
||||
>
|
||||
@import "../../../../lib/assets/css/variables.less";
|
||||
.chapterDetailed {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2.4rem;
|
||||
width: 100%;
|
||||
max-width: 100%;
|
||||
|
||||
.return {
|
||||
a {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
align-items: center;
|
||||
font-weight: 400;
|
||||
font-family: Outfit;
|
||||
}
|
||||
}
|
||||
|
||||
.main {
|
||||
display: flex;
|
||||
gap: 2.4rem;
|
||||
@media @mobile {
|
||||
flex-direction: column;
|
||||
}
|
||||
.nav {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
min-width: 360px;
|
||||
|
||||
@media @mobile {
|
||||
min-width: 100%;
|
||||
}
|
||||
li {
|
||||
border-radius: 2px;
|
||||
border: 1px solid var(--bg-100);
|
||||
display: flex;
|
||||
|
||||
align-items: center;
|
||||
gap: 1.2rem;
|
||||
background-color: var(--neutral-white);
|
||||
a {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 0.6rem 1.2rem;
|
||||
align-items: center;
|
||||
gap: 1.2rem;
|
||||
.icon {
|
||||
min-height: 1.2rem;
|
||||
min-width: 1.2rem;
|
||||
height: 1.2rem;
|
||||
width: 1.2rem;
|
||||
img {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
color: var(--bg-100);
|
||||
}
|
||||
&.active {
|
||||
background: var(--bg-100);
|
||||
a {
|
||||
color: var(--neutral-white);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.questions {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.2rem;
|
||||
flex-grow: 1;
|
||||
.question {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
align-items: center;
|
||||
gap: 1.2rem;
|
||||
padding: 0.6rem 0px;
|
||||
border-bottom: 1px solid var(--bg-100);
|
||||
cursor: pointer;
|
||||
a {
|
||||
color: var(--bg-100);
|
||||
font-weight: 400;
|
||||
font-family: Outfit;
|
||||
}
|
||||
button {
|
||||
width: 1.6rem;
|
||||
height: 1.6rem;
|
||||
min-width: 1.6rem;
|
||||
min-height: 1.6rem;
|
||||
border: 2px solid var(--bg-100);
|
||||
background-color: var(--neutral-white);
|
||||
transform: rotate(45deg);
|
||||
color: var(--bg-100);
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
&:hover {
|
||||
button {
|
||||
transform: rotate(0);
|
||||
background-color: var(--bg-100);
|
||||
color: var(--neutral-white);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,51 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { getCachedEntry } from "../../../../api"
|
||||
import NotFound from "../../../../routes/NotFound.svelte"
|
||||
import ContentBlock from "../ContentBlock.svelte"
|
||||
import Loader from "../Loader.svelte"
|
||||
import Index from "../SEO/Index.svelte"
|
||||
export let location: LocationStore
|
||||
let question: ContentEntry
|
||||
let loading = true
|
||||
getCachedEntry("content", {
|
||||
type: "helpcenterQuestion",
|
||||
path: `/${location.path.split("/").filter(Boolean).pop()}`,
|
||||
})
|
||||
.then((res) => {
|
||||
question = res
|
||||
})
|
||||
.finally(() => {
|
||||
loading = false
|
||||
})
|
||||
</script>
|
||||
|
||||
{#if loading}
|
||||
<Loader size="3" />
|
||||
{:else if question}
|
||||
<Index
|
||||
title="{question.question} - BinKrassDuFass"
|
||||
keywords="Hilfe, FAQ, Fragen, Antworten, BinKrassDuFass"
|
||||
metaDescription="Antwort auf die Frage {question.question}."
|
||||
article="{true}"
|
||||
/>
|
||||
<div class="blocks">
|
||||
{#each question.blocks || [] as block, idx}
|
||||
<ContentBlock
|
||||
block="{block}"
|
||||
noHorizontalMargin="{true}"
|
||||
verticalPadding="{idx !== 0}"
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
{:else}
|
||||
<NotFound />
|
||||
{/if}
|
||||
|
||||
<style lang="less">
|
||||
.blocks {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 1.2rem;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
||||
@@ -1,47 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { mdiLinkVariant } from "@mdi/js"
|
||||
import { getCachedEntries } from "../../../../api"
|
||||
import { spaLink, spaNavigate } from "../../../actions"
|
||||
import Icon from "../../widgets/Icon.svelte"
|
||||
import Loader from "../Loader.svelte"
|
||||
import Index from "../SEO/Index.svelte"
|
||||
|
||||
export let chapter: HelpCenterChapter, location: LocationStore
|
||||
getCachedEntries("content", {
|
||||
type: "helpcenterQuestion",
|
||||
path: { $in: chapter.questions.map((q) => q.page) },
|
||||
}).then((res) => {
|
||||
questions = res
|
||||
})
|
||||
let questions: ContentEntry[]
|
||||
</script>
|
||||
|
||||
{#if location.path.includes(`/helpCenter/${chapter.slug}`)}
|
||||
<Index
|
||||
title="{chapter.title} - BinKrassDuFass"
|
||||
keywords="Hilfe, FAQ, Fragen, Antworten, BinKrassDuFass"
|
||||
metaDescription="Häufig gestellte Fragen und Antworten zu BinKrassDuFass im Themengebiet {chapter.title}."
|
||||
/>
|
||||
{#if !Array.isArray(questions)}
|
||||
<Loader size="3" />
|
||||
{:else}
|
||||
{#each questions as question}
|
||||
<li class="question">
|
||||
<a
|
||||
href="/helpCenter/{chapter.slug}{question.path}"
|
||||
use:spaLink
|
||||
>
|
||||
{question.question}
|
||||
</a>
|
||||
<button
|
||||
aria-label="Link zur Frage"
|
||||
on:click="{() => {
|
||||
spaNavigate(`/helpCenter/${chapter.slug}${question.path}`)
|
||||
}}"
|
||||
>
|
||||
<Icon path="{mdiLinkVariant}" />
|
||||
</button>
|
||||
</li>
|
||||
{/each}
|
||||
{/if}
|
||||
{/if}
|
||||
@@ -1,89 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { icons } from "../../../../config"
|
||||
import { addProductToCart } from "../../../functions/helper/product"
|
||||
import { newNotification } from "../../../store"
|
||||
import Icon from "../../widgets/Icon.svelte"
|
||||
import ToggleFavorite from "./ToggleFavorite.svelte"
|
||||
import ProductQuantity from "./widgets/ProductQuantity.svelte"
|
||||
export let variant: BKDFProductVariant,
|
||||
possibleVariants: BKDFProductVariant[],
|
||||
mobileFormat = false
|
||||
let quantity = 1
|
||||
|
||||
function noVariantError() {
|
||||
newNotification({
|
||||
class: "error",
|
||||
html: `Bitte wähle eine eindeutige Variante aus. Du musst sowohl eine Farbe als auch eine Größe auswählen.`,
|
||||
})
|
||||
}
|
||||
|
||||
function addToCart(variant: BKDFProductVariant, quantity: number) {
|
||||
loading = true
|
||||
addProductToCart(variant, quantity)
|
||||
.then(() => {
|
||||
loading = false
|
||||
})
|
||||
.catch(() => {
|
||||
loading = false
|
||||
})
|
||||
}
|
||||
|
||||
let loading = false
|
||||
</script>
|
||||
|
||||
<div
|
||||
id="actionRow"
|
||||
class="actionRow"
|
||||
class:mobileFormat="{mobileFormat}"
|
||||
>
|
||||
<ProductQuantity bind:quantity="{quantity}" />
|
||||
<button
|
||||
disabled="{loading}"
|
||||
id="addToCart"
|
||||
class="cta primary"
|
||||
on:click="{() => {
|
||||
if (!variant) return noVariantError()
|
||||
addToCart(variant, quantity)
|
||||
}}"
|
||||
>
|
||||
<Icon
|
||||
path="{icons.shoppingBag}"
|
||||
color="#F3EED9"
|
||||
width="{24}px"
|
||||
height="{24}px"
|
||||
props="{{ 'fill-rule': 'evenodd', 'clip-rule': 'evenodd' }}"
|
||||
/>
|
||||
IN DIE TASCHE</button
|
||||
>
|
||||
<ToggleFavorite
|
||||
productId="{Number(!variant ? possibleVariants[0]?.parentId : variant?.parentId)}"
|
||||
variantid="{Number(!variant ? possibleVariants[0]?.id : variant?.id)}"
|
||||
bigFormat="{mobileFormat}"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<style lang="less">
|
||||
.actionRow {
|
||||
display: grid;
|
||||
gap: 12px;
|
||||
margin-top: 1.2rem;
|
||||
grid-template-columns: 1fr 8fr 1fr;
|
||||
&.mobileFormat {
|
||||
grid-template-columns: 1fr 3fr;
|
||||
gap: 0;
|
||||
// give thrid item full width not just 1 fr
|
||||
}
|
||||
#addToCart {
|
||||
border-radius: 2px;
|
||||
padding: 0.6rem 1.2rem 0.6rem 1.2rem;
|
||||
flex-grow: 1;
|
||||
font-weight: 700;
|
||||
font-family: Outfit-Bold, sans-serif;
|
||||
font-size: 1rem;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,37 +0,0 @@
|
||||
<script lang="ts">
|
||||
import Icon from "../../widgets/Icon.svelte"
|
||||
|
||||
import { icons } from "../../../../config"
|
||||
import { addProductToCart } from "../../../functions/helper/product"
|
||||
|
||||
export let productId, variantId: number
|
||||
</script>
|
||||
|
||||
<button
|
||||
aria-label="In den Warenkorb legen"
|
||||
on:click="{() => {
|
||||
addProductToCart(
|
||||
{
|
||||
id: variantId,
|
||||
parentId: productId,
|
||||
},
|
||||
1
|
||||
)
|
||||
}}"
|
||||
>
|
||||
<Icon
|
||||
path="{icons.shoppingBag}"
|
||||
color="#2f4858"
|
||||
width="{24}px"
|
||||
height="{24}px"
|
||||
props="{{ 'fill-rule': 'evenodd', 'clip-rule': 'evenodd' }}"
|
||||
/>
|
||||
</button>
|
||||
|
||||
<style lang="less">
|
||||
button {
|
||||
position: relative;
|
||||
z-index: 100;
|
||||
color: var(--text-invers-100);
|
||||
}
|
||||
</style>
|
||||
@@ -1,3 +0,0 @@
|
||||
<script lang="ts">
|
||||
export let CL: CompleteYourLook
|
||||
</script>
|
||||
@@ -1,31 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { getCachedEntries } from "../../../../api"
|
||||
import ContentBlock from "../ContentBlock.svelte"
|
||||
export let bigCommerceProductId: number
|
||||
getCachedEntries("content", {
|
||||
products: bigCommerceProductId,
|
||||
type: "product",
|
||||
}).then((res) => {
|
||||
contentEntries = res
|
||||
})
|
||||
let contentEntries: ContentEntry
|
||||
</script>
|
||||
|
||||
<div class="dRows">
|
||||
{#each contentEntries || [] as contentEntry}
|
||||
{#each contentEntry.blocks || [] as block, idx}
|
||||
<ContentBlock
|
||||
block="{block}"
|
||||
noHorizontalMargin="{true}"
|
||||
/>
|
||||
{/each}
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<style
|
||||
lang="less"
|
||||
global
|
||||
>
|
||||
.dRows {
|
||||
}
|
||||
</style>
|
||||
@@ -1,71 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { spaNavigate } from "../../../actions"
|
||||
import { categories } from "../../../store"
|
||||
import FilterBlock from "./widgets/FilterBlock.svelte"
|
||||
function findCategoryByPath(categories: Category[], path: string): Category {
|
||||
for (const category of categories) {
|
||||
if (category.path === path) {
|
||||
return category
|
||||
}
|
||||
const foundInChildren = findCategoryByPath(category.children, path)
|
||||
if (foundInChildren) {
|
||||
return foundInChildren
|
||||
}
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
function getDepthOfCategory(categories: Category[], path: string): number {
|
||||
for (const category of categories) {
|
||||
if (category.path === path) {
|
||||
return 0
|
||||
}
|
||||
const foundInChildren = getDepthOfCategory(category.children, path)
|
||||
if (foundInChildren !== undefined) {
|
||||
return foundInChildren + 1
|
||||
}
|
||||
}
|
||||
return undefined
|
||||
}
|
||||
export let path: string
|
||||
const category = findCategoryByPath($categories, path)
|
||||
</script>
|
||||
|
||||
{#if category}
|
||||
<div class="filter-wrapper">
|
||||
<FilterBlock
|
||||
title="{category.name}"
|
||||
deletable="{getDepthOfCategory($categories, path) > 0}"
|
||||
active="{true}"
|
||||
on:click="{() => {
|
||||
if (getDepthOfCategory($categories, path) > 0) {
|
||||
const newPath = path.endsWith('/') ? path.slice(0, -1) : path
|
||||
spaNavigate(`/collections/${newPath.split('/').slice(0, -1).join('/')}`)
|
||||
}
|
||||
}}"
|
||||
/>
|
||||
{#each category.children as subcategory}
|
||||
<FilterBlock
|
||||
title="{subcategory.name}"
|
||||
active="{false}"
|
||||
on:click="{() => {
|
||||
spaNavigate(`/collections${subcategory.path}`)
|
||||
}}"
|
||||
/>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<style lang="less">
|
||||
.filter-wrapper {
|
||||
display: flex;
|
||||
max-width: 100%;
|
||||
overflow-x: auto;
|
||||
gap: 12px;
|
||||
-ms-overflow-style: none; /* Internet Explorer 10+ */
|
||||
scrollbar-width: none; /* Firefox */
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: none; /* Safari and Chrome */
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,141 +0,0 @@
|
||||
<script lang="ts">
|
||||
import { backgroundImages } from "../../../store"
|
||||
import MedialibImage from "../../widgets/MedialibImage.svelte"
|
||||
|
||||
export let src: string, alt: string
|
||||
|
||||
let shouldZoom = false,
|
||||
imagePosition: HTMLButtonElement,
|
||||
zoomFactor = 3, // Example zoom factor, adjust for stronger or weaker zoom
|
||||
initialClickPosition = { x: 0, y: 0 },
|
||||
currentPosition = { x: 0, y: 0 },
|
||||
coordX = 0,
|
||||
coordY = 0,
|
||||
lastTouchMoveEvent: number = 0
|
||||
|
||||
function handleMousemove(event: MouseEvent) {
|
||||
if (!shouldZoom) return
|
||||
updatePosition(event.clientX, event.clientY)
|
||||
}
|
||||
|
||||
function handleTouchMove(event: TouchEvent) {
|
||||
if (!shouldZoom) return
|
||||
event.preventDefault()
|
||||
const now = Date.now()
|
||||
if (now - lastTouchMoveEvent < 10) return
|
||||
lastTouchMoveEvent = now
|
||||
updatePosition(event.touches[0].clientX, event.touches[0].clientY)
|
||||
}
|
||||
|
||||
function handleClick(event: MouseEvent | TouchEvent) {
|
||||
shouldZoom = !shouldZoom
|
||||
if (shouldZoom) {
|
||||
if (event instanceof MouseEvent) handleMousemove(event)
|
||||
else if (event instanceof TouchEvent) handleTouchMove(event)
|
||||
} else resetPosition()
|
||||
}
|
||||
|
||||
function updatePosition(clientX: number, clientY: number) {
|
||||
var rect = imagePosition.getBoundingClientRect()
|
||||
currentPosition.x = clientX - rect.left
|
||||
currentPosition.y = clientY - rect.top
|
||||
coordX = currentPosition.x * -1 * zoomFactor
|
||||
coordY = currentPosition.y * -1 * zoomFactor
|
||||
}
|
||||
|
||||
function resetPosition() {
|
||||
initialClickPosition.x = 0
|
||||
initialClickPosition.y = 0
|
||||
coordX = 0
|
||||
coordY = 0
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="imageholder">
|
||||
<button
|
||||
class="image"
|
||||
aria-label="zoom in image"
|
||||
class:allowZoom="{shouldZoom}"
|
||||
on:click="{handleClick}"
|
||||
on:mousemove="{handleMousemove}"
|
||||
on:touchmove="{handleTouchMove}"
|
||||
bind:this="{imagePosition}"
|
||||
>
|
||||
<img
|
||||
src="{src.replace('2000w', '800w')}"
|
||||
alt="{alt}"
|
||||
/>
|
||||
<div class="zoomimg">
|
||||
<img
|
||||
src="{src}"
|
||||
alt="{alt}"
|
||||
style="left: {coordX}px; top: {coordY}px;"
|
||||
/>
|
||||
<div class="background-img-product">
|
||||
<MedialibImage
|
||||
id="{$backgroundImages['standard']}"
|
||||
filter="l"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="background-img-product">
|
||||
<MedialibImage
|
||||
id="{$backgroundImages['standard']}"
|
||||
filter="l"
|
||||
/>
|
||||
</div>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<style lang="less">
|
||||
@import "../../../assets/css/variables.less";
|
||||
.imageholder {
|
||||
height: 100%;
|
||||
width: fit-content;
|
||||
flex-shrink: 0;
|
||||
.image {
|
||||
background: var(--bg-300);
|
||||
height: 100%;
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
img {
|
||||
height: 100%;
|
||||
width: auto;
|
||||
object-fit: contain;
|
||||
position: relative;
|
||||
z-index: 2;
|
||||
}
|
||||
.zoomimg {
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
background: var(--bg-300);
|
||||
z-index: 2;
|
||||
position: absolute;
|
||||
|
||||
top: 0;
|
||||
left: 0px;
|
||||
bottom: 0;
|
||||
right: 0px;
|
||||
img {
|
||||
position: absolute;
|
||||
width: 400%;
|
||||
height: auto;
|
||||
top: 0;
|
||||
left: 0;
|
||||
transition: top 0s ease, left 0s ease;
|
||||
}
|
||||
}
|
||||
&:hover {
|
||||
cursor: cell;
|
||||
}
|
||||
&.allowZoom:hover {
|
||||
cursor: crosshair;
|
||||
> .zoomimg {
|
||||
opacity: 1;
|
||||
visibility: visible;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user