general setup
This commit is contained in:
@@ -15,6 +15,7 @@
|
||||
de: Modul Import
|
||||
en: Module Import
|
||||
id: moduleImport
|
||||
|
||||
- name:
|
||||
de: Formular
|
||||
en: form
|
||||
@@ -24,10 +25,17 @@
|
||||
de: Text
|
||||
en: Text
|
||||
id: text
|
||||
|
||||
- name:
|
||||
de: Google Maps
|
||||
en: Google Maps
|
||||
id: googleMaps
|
||||
|
||||
- name:
|
||||
de: Produktslider
|
||||
en: Product Slider
|
||||
id: productSlider
|
||||
|
||||
- !include ../fields/images.yml
|
||||
|
||||
- name: moduleImport
|
||||
@@ -47,3 +55,5 @@
|
||||
defaultCollectionViews: true
|
||||
|
||||
- !include ../fields/text.yml
|
||||
|
||||
- !include ../fields/productSlider.yml
|
||||
|
||||
74
api/collections/fieldLists/completeYourLook.yml
Normal file
74
api/collections/fieldLists/completeYourLook.yml
Normal file
@@ -0,0 +1,74 @@
|
||||
- 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: bigCommerceProducts
|
||||
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."
|
||||
@@ -19,6 +19,27 @@
|
||||
render:
|
||||
defaultCollectionViews: true
|
||||
|
||||
- name: contentWidth
|
||||
type: number
|
||||
meta:
|
||||
label:
|
||||
de: Inhaltsbreite
|
||||
en: Content Width
|
||||
widget: select
|
||||
choices:
|
||||
- id: 0
|
||||
name:
|
||||
de: Seitenbreite
|
||||
en: Page Width
|
||||
- id: 1
|
||||
name:
|
||||
de: Normale Breite
|
||||
en: Normal Width
|
||||
- id: 2
|
||||
name:
|
||||
de: Schmale Breite
|
||||
en: Narrow Width
|
||||
|
||||
- name: columns
|
||||
type: object[]
|
||||
meta:
|
||||
|
||||
65
api/collections/fields/productSlider.yml
Normal file
65
api/collections/fields/productSlider.yml
Normal file
@@ -0,0 +1,65 @@
|
||||
name: productSlider
|
||||
type: object
|
||||
meta:
|
||||
label:
|
||||
de: Produktslider
|
||||
en: Product Slider
|
||||
widget: containerLessObject
|
||||
subFields:
|
||||
- name: productSource
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: Produktdatenquelle
|
||||
en: Product Data Source
|
||||
widget: select
|
||||
choices:
|
||||
- name:
|
||||
de: Manuelle Auswahl
|
||||
en: Manual Selection
|
||||
id: manual
|
||||
|
||||
- name:
|
||||
de: Kategorie
|
||||
en: Category
|
||||
id: category
|
||||
|
||||
- name:
|
||||
de: Bestseller
|
||||
en: Bestseller
|
||||
id: bestseller
|
||||
|
||||
- name:
|
||||
de: Neue Produkte
|
||||
en: New Products
|
||||
id: newProducts
|
||||
|
||||
- name:
|
||||
de: Angebote
|
||||
en: Offers
|
||||
id: offers
|
||||
|
||||
- name: productSKUs
|
||||
type: string[]
|
||||
meta:
|
||||
label:
|
||||
de: Produkt SKUs
|
||||
en: Product SKUs
|
||||
dependsOn:
|
||||
eval: $.productSource === 'manual'
|
||||
widget: foreignKey
|
||||
foreign:
|
||||
collection: bigCommerceProducts
|
||||
id: id
|
||||
subNavigation: 0
|
||||
render:
|
||||
defaultCollectionViews: true
|
||||
|
||||
- name: category
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: Kategorie
|
||||
en: Category
|
||||
dependsOn:
|
||||
eval: $.productSource === 'category'
|
||||
@@ -88,17 +88,54 @@ fields:
|
||||
type: string
|
||||
meta:
|
||||
label:
|
||||
de: Modultyp
|
||||
en: Module Type
|
||||
de: Modultyp
|
||||
en: Module Type
|
||||
helperText:
|
||||
de: "Wählen Sie den Typ des Moduls aus."
|
||||
en: "Choose the type of the module."
|
||||
de: "Wählen Sie den Typ des Moduls aus."
|
||||
en: "Choose the type of the module."
|
||||
widget: select
|
||||
choices:
|
||||
- name: { de: "Formular", en: "Form" }
|
||||
id: form
|
||||
|
||||
- name:
|
||||
de: Verfollständige deinen Look
|
||||
en: Complete your look
|
||||
id: completeYourLook
|
||||
- name:
|
||||
de: Größengraph
|
||||
en: Size Chart
|
||||
id: sizeChart
|
||||
|
||||
- name: form
|
||||
type: object
|
||||
meta:
|
||||
widget: containerLessObject
|
||||
dependsOn:
|
||||
eval: $.type === 'form'
|
||||
subFields: !include fieldLists/formular/form.yml
|
||||
|
||||
- name: completeYourLook
|
||||
type: object
|
||||
meta:
|
||||
widget: containerLessObject
|
||||
dependsOn:
|
||||
eval: $.type === 'completeYourLook'
|
||||
subFields: !include fieldLists/completeYourLook.yml
|
||||
|
||||
- name: sizeChart
|
||||
type: object
|
||||
meta:
|
||||
widget: containerLessObject
|
||||
dependsOn:
|
||||
eval: $.type === 'sizeChart'
|
||||
subFields:
|
||||
- name: rows
|
||||
type: object[]
|
||||
meta:
|
||||
label:
|
||||
de: Zeilen
|
||||
en: Rows
|
||||
direction: row
|
||||
widget: grid
|
||||
subFields: !include fieldLists/row.yml
|
||||
|
||||
@@ -83,7 +83,7 @@ x-page: &seite
|
||||
name: path
|
||||
|
||||
fields:
|
||||
- name: tree
|
||||
- name: type
|
||||
type: number
|
||||
meta:
|
||||
label: Baum
|
||||
@@ -107,9 +107,18 @@ fields:
|
||||
- *name
|
||||
- *seite
|
||||
- name: image
|
||||
type: file
|
||||
type: string
|
||||
meta:
|
||||
label: Bild
|
||||
label:
|
||||
de: Hintergrundbild
|
||||
en: Background Image
|
||||
widget: foreignKey
|
||||
foreign:
|
||||
collection: medialib
|
||||
id: id
|
||||
subNavigation: 0
|
||||
render:
|
||||
defaultCollectionViews: true
|
||||
dependsOn:
|
||||
eval: |
|
||||
(function(){
|
||||
|
||||
140
api/collections/products.yml
Normal file
140
api/collections/products.yml
Normal file
@@ -0,0 +1,140 @@
|
||||
name: bigCommerceProducts
|
||||
# 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:
|
||||
views:
|
||||
- type: cardList
|
||||
mediaQuery: "(min-width: 1200px)"
|
||||
selectionPriority: 1
|
||||
fields:
|
||||
- source: previewImage
|
||||
name:
|
||||
de: Vorschaubild
|
||||
en: Preview Image
|
||||
- source: bigCommerceSKU
|
||||
name:
|
||||
de: BigCommerce SKU
|
||||
en: BigCommerce SKU
|
||||
|
||||
- type: table
|
||||
mediaQuery: "(min-width: 600px)"
|
||||
columns:
|
||||
- source: previewImage
|
||||
- source: bigCommerceSKU
|
||||
|
||||
- type: simpleList
|
||||
primaryText: previewImage
|
||||
secondaryText: bigCommerceSKU
|
||||
|
||||
subNavigation:
|
||||
- name: modal
|
||||
views:
|
||||
- type: table
|
||||
columns:
|
||||
- source: type
|
||||
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:
|
||||
post:
|
||||
return:
|
||||
#webhook
|
||||
type: javascript
|
||||
file: hooks/products/post_validate.js
|
||||
|
||||
permissions:
|
||||
public:
|
||||
methods:
|
||||
get: false
|
||||
post: false
|
||||
put: false
|
||||
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: 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: previewImage
|
||||
type: file
|
||||
meta:
|
||||
label:
|
||||
de: Vorschaubild
|
||||
en: Preview Image
|
||||
widget: image
|
||||
|
||||
- name: isBestseller
|
||||
type: boolean
|
||||
meta:
|
||||
label:
|
||||
de: Bestseller
|
||||
en: Bestseller
|
||||
containerProps:
|
||||
layout:
|
||||
size:
|
||||
default: "col-6"
|
||||
small: "col-12"
|
||||
large: "col-6"
|
||||
|
||||
- name: isFeatured
|
||||
type: boolean
|
||||
meta:
|
||||
label:
|
||||
de: Empfohlen
|
||||
en: Featured
|
||||
containerProps:
|
||||
layout:
|
||||
size:
|
||||
default: "col-6"
|
||||
small: "col-12"
|
||||
large: "col-6"
|
||||
|
||||
- name: isOnSale
|
||||
type: boolean
|
||||
meta:
|
||||
label:
|
||||
de: Im Angebot
|
||||
en: On Sale
|
||||
containerProps:
|
||||
layout:
|
||||
size:
|
||||
default: "col-6"
|
||||
small: "col-12"
|
||||
large: "col-6"
|
||||
|
||||
- name: isNew
|
||||
type: boolean
|
||||
meta:
|
||||
label:
|
||||
de: Neu
|
||||
en: New
|
||||
containerProps:
|
||||
layout:
|
||||
size:
|
||||
default: "col-6"
|
||||
small: "col-12"
|
||||
large: "col-6"
|
||||
300
api/collections/rating.yml
Normal file
300
api/collections/rating.yml
Normal file
@@ -0,0 +1,300 @@
|
||||
name: rating
|
||||
|
||||
meta:
|
||||
label: { de: "Bewertungen", en: "Ratings" }
|
||||
muiIcon: reviews
|
||||
views:
|
||||
- type: simpleList
|
||||
mediaQuery: "max-width: 600px"
|
||||
primaryText: order
|
||||
- type: table
|
||||
columns:
|
||||
- source: orderId
|
||||
filter: true
|
||||
- source: productId
|
||||
filter: true
|
||||
- source: rating
|
||||
filter: true
|
||||
- source: status
|
||||
filter: true
|
||||
|
||||
permissions:
|
||||
public:
|
||||
methods:
|
||||
get: true
|
||||
post: true
|
||||
put: true
|
||||
delete: false
|
||||
user:
|
||||
methods:
|
||||
get: true
|
||||
post: true
|
||||
put: true
|
||||
delete: false
|
||||
|
||||
rating:
|
||||
methods:
|
||||
get: true
|
||||
post: true
|
||||
put: true
|
||||
delete: false
|
||||
|
||||
"token:${IMPORT_TOKEN}":
|
||||
methods:
|
||||
get: true
|
||||
post: true
|
||||
put: true
|
||||
delete: true
|
||||
|
||||
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
|
||||
index: [single]
|
||||
type: string
|
||||
meta:
|
||||
label: { de: Bestellung, en: "Order" }
|
||||
containerProps:
|
||||
layout:
|
||||
size:
|
||||
default: "col-6"
|
||||
small: "col-12"
|
||||
large: "col-4"
|
||||
|
||||
- name: bigCommerceProductRatingId
|
||||
index: [single]
|
||||
type: string
|
||||
meta:
|
||||
label: { de: "Produktbewertung - BigCommerce ID", en: "Product Rating - BigCommerce ID" }
|
||||
containerProps:
|
||||
layout:
|
||||
size:
|
||||
default: "col-6"
|
||||
small: "col-12"
|
||||
large: "col-4"
|
||||
|
||||
- name: productId
|
||||
index: [single]
|
||||
type: string
|
||||
meta:
|
||||
label: { de: "Produkt", en: "Product" }
|
||||
widget: foreignKey
|
||||
foreign:
|
||||
collection: bigCommerceProducts
|
||||
subNavigation: 0
|
||||
id: id
|
||||
render:
|
||||
defaultCollectionViews: true
|
||||
containerProps:
|
||||
layout:
|
||||
size:
|
||||
default: "col-6"
|
||||
small: "col-12"
|
||||
large: "col-4"
|
||||
validator:
|
||||
required: true
|
||||
|
||||
- name: author
|
||||
type: string
|
||||
validator:
|
||||
required: true
|
||||
meta:
|
||||
label: { de: "Autor", en: "Author" }
|
||||
defaultValue: Anonym
|
||||
|
||||
- name: rating
|
||||
index: [single]
|
||||
type: object
|
||||
meta:
|
||||
label: { de: "Bewertung", en: "Rating" }
|
||||
subFields:
|
||||
- name: length
|
||||
type: number
|
||||
meta:
|
||||
label: { de: "Länge", en: "Length" }
|
||||
helperText: { de: "1 - Besonders Kurz; 5 - Besonders Lang", en: "1 - Very Short; 5 - Very Long" }
|
||||
defaultValue: 5
|
||||
validator:
|
||||
eval: |
|
||||
(function () {
|
||||
return $this >= 0 && $this <= 5;
|
||||
})()
|
||||
containerProps:
|
||||
layout:
|
||||
size:
|
||||
default: "col-6"
|
||||
small: "col-12"
|
||||
large: "col-4"
|
||||
- name: fit
|
||||
type: number
|
||||
meta:
|
||||
label: { de: "Passform", en: "Fit" }
|
||||
helperText: { de: "1 - Sehr Eng; 5 - Sehr Weit", en: "1 - Very Tight; 5 - Very Wide" }
|
||||
defaultValue: 5
|
||||
validator:
|
||||
eval: |
|
||||
(function () {
|
||||
return $this >= 0 && $this <= 5;
|
||||
})()
|
||||
containerProps:
|
||||
layout:
|
||||
size:
|
||||
default: "col-6"
|
||||
small: "col-12"
|
||||
large: "col-4"
|
||||
|
||||
- name: quality
|
||||
type: number
|
||||
meta:
|
||||
label: { de: "Qualität", en: "Quality" }
|
||||
helperText: { de: "1 - Schlecht; 5 - Sehr Gut", en: "1 - Bad; 5 - Very Good" }
|
||||
defaultValue: 5
|
||||
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" }
|
||||
defaultValue: 5
|
||||
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" }
|
||||
defaultValue: 5
|
||||
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" }
|
||||
defaultValue: 5
|
||||
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:
|
||||
widget: richtext
|
||||
label: { de: "Kommentar", en: "Comment" }
|
||||
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"
|
||||
@@ -1,4 +1,4 @@
|
||||
namespace: tibi_starter
|
||||
namespace: bkdf
|
||||
|
||||
meta:
|
||||
openapi:
|
||||
@@ -6,7 +6,7 @@ meta:
|
||||
- url: https://tibi-admin-server.code.testversion.online/api/v1/_/demo
|
||||
description: code-server
|
||||
|
||||
dashboard: helper/dashboard.yml
|
||||
dashboard: !include helper/dashboard.yml
|
||||
|
||||
# Liste möglicher Berechtigungen, die Benutzern zugeordnet werden können
|
||||
permissions:
|
||||
@@ -23,6 +23,7 @@ collections:
|
||||
- !include collections/medialib.yml
|
||||
- !include collections/module.yml
|
||||
- !include collections/navigation.yml
|
||||
- !include collections/products.yml
|
||||
- !include collections/ssr.yml
|
||||
|
||||
jobs:
|
||||
|
||||
@@ -1,2 +1,3 @@
|
||||
TOKEN=geheim
|
||||
SSR_TOKEN=owshwerNwoa
|
||||
SSR_TOKEN=owshwerNwoa
|
||||
BIGCOMMERCE_WEBHOOK_TOKEN=super_secure_and_extremely_secret_big_commerce_webhook_token_123
|
||||
|
||||
@@ -101,8 +101,8 @@
|
||||
else if (oldVal !== newVal) {
|
||||
updateLogs.push({
|
||||
field: newPath,
|
||||
previous: oldVal.toString(),
|
||||
current: newVal.toString(),
|
||||
previous: oldVal?.toString(),
|
||||
current: newVal?.toString(),
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
@@ -8,4 +8,7 @@ if (release && typeof context !== "undefined") {
|
||||
module.exports = {
|
||||
release,
|
||||
apiClientBaseURL,
|
||||
frontendBase: "https://bkdf.code.testversion.online/",
|
||||
tibiUrl: "https://bkdf-tibiadmin.code.testversion.online/",
|
||||
bkdfApiUrl: "https://bkdf.code.testversion.online/api/",
|
||||
}
|
||||
|
||||
@@ -1,32 +1,92 @@
|
||||
const apiSsrBaseURL = "http://localhost:8080/api/v1/_/allkids_erfurt"
|
||||
|
||||
const apiSsrBaseURL = "http://localhost:8080/api/v1/_/tibi_starter"
|
||||
const { frontendBase, tibiUrl, bkdfApiUrl } = require("./config-client")
|
||||
module.exports = {
|
||||
operatorEmail: "binkrassdufass.clothing@gmail.com",
|
||||
operatorName: "BinKrassDuFass",
|
||||
contactEmail: "binkrassdufass.clothing@gmail.com",
|
||||
|
||||
frontendBase,
|
||||
apiBase: frontendBase + "/api/",
|
||||
tibiUrl,
|
||||
apiSsrBaseURL,
|
||||
ssrValidatePath: function (path) {
|
||||
// validate if path ssr rendering is ok, -1 = NOTFOUND, 0 = NO SSR, 1 = SSR
|
||||
// pe. use context.readCollection("product", {filter: {path: path}}) ... to validate dynamic urls
|
||||
|
||||
// // / is de home
|
||||
// if (path == "/") return 1
|
||||
|
||||
// // all other sites are in db
|
||||
//path = path?.replace(/^\//, "")
|
||||
console.log("PATH:", path)
|
||||
const resp = context.db.find("content", {
|
||||
filter: {
|
||||
$and: [{ path }],
|
||||
},
|
||||
|
||||
selector: { _id: 1 },
|
||||
})
|
||||
console.log("RESP:", resp?.length)
|
||||
if (resp && resp.length) {
|
||||
ssrValidatePath: function (/** @type {string} */ url) {
|
||||
//TODO: ANPASSEN!
|
||||
// -1 = NOTFOUND, 0 = NOSSR, 1 = SSR, "string" = PATH_FOR_CACHE
|
||||
if (url == "/") {
|
||||
return 1
|
||||
}
|
||||
|
||||
// not found
|
||||
return -1
|
||||
if (url.match(/^\/(checkout|order|search)/)) {
|
||||
// no ssr
|
||||
return 0
|
||||
}
|
||||
|
||||
if (url.match(/^\/service(\/.*)?$/)) {
|
||||
// static content
|
||||
return 1
|
||||
}
|
||||
|
||||
const categoryPath = url.replace(/\/[^_\/]+_[^\/]+$/, "") // also in app query
|
||||
const cat = context.db.find("category", {
|
||||
filter: {
|
||||
$or: [
|
||||
{ "path.de": categoryPath },
|
||||
{ "path.en": categoryPath },
|
||||
{ "path.fr": categoryPath },
|
||||
{ "path.se": categoryPath },
|
||||
{ "path.dk": categoryPath },
|
||||
],
|
||||
},
|
||||
selector: {
|
||||
insertTime: 1,
|
||||
},
|
||||
})
|
||||
|
||||
if (cat && cat.length) {
|
||||
// in category
|
||||
const matches = url.match(/^\/([^\/]+\/)*([^_\/]+)_/)
|
||||
if (matches && matches.length > 2) {
|
||||
// product url
|
||||
const prod = context.db.find("product", {
|
||||
filter: {
|
||||
code: matches[2],
|
||||
},
|
||||
selector: {
|
||||
insertTime: 1,
|
||||
},
|
||||
})
|
||||
|
||||
if (prod && prod.length) {
|
||||
// force one url for product code in cache
|
||||
return categoryPath + "/" + matches[2] + "_ssr-product"
|
||||
}
|
||||
|
||||
// product not found
|
||||
return -1
|
||||
}
|
||||
|
||||
// try to render category
|
||||
return 1
|
||||
}
|
||||
|
||||
// search for other content sites
|
||||
const c = context.db.find("content", {
|
||||
filter: {
|
||||
path: url,
|
||||
},
|
||||
selector: {
|
||||
insertTime: 1,
|
||||
},
|
||||
})
|
||||
if (c && c.length) {
|
||||
// found
|
||||
return 1
|
||||
} else {
|
||||
// not found
|
||||
return -1
|
||||
}
|
||||
},
|
||||
|
||||
ssrPublishCheckCollections: ["content"],
|
||||
LIGHTHOUSE_TOKEN: "AIzaSyC0UxHp3-MpJiDL3ws7pEV6lj57bfIc7GQ",
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ function log(str) {
|
||||
/**
|
||||
* convert object to string
|
||||
* @param {any} obj object
|
||||
* @returns {Object | undefined}
|
||||
*/
|
||||
function obj2str(obj) {
|
||||
if (Array.isArray(obj)) {
|
||||
@@ -47,15 +48,22 @@ function clearSSRCache() {
|
||||
context.response.header("X-SSR-Cleared", info.removed)
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {{ [x: string]: any; }[]} dbObjs
|
||||
*/
|
||||
function calculateAverageDynamically(dbObjs) {
|
||||
const sumObj = {}
|
||||
let count = 0
|
||||
|
||||
dbObjs.forEach((obj) => {
|
||||
dbObjs.forEach((/** @type {{ [x: string]: any; }} */ obj) => {
|
||||
accumulate(obj, sumObj)
|
||||
count++
|
||||
})
|
||||
|
||||
/**
|
||||
* @param {{ [x: string]: any; }} sourceObj
|
||||
* @param {{ [x: string]: any; }} targetObj
|
||||
*/
|
||||
function accumulate(sourceObj, targetObj) {
|
||||
for (const key in sourceObj) {
|
||||
if (typeof sourceObj[key] === "number") {
|
||||
@@ -67,6 +75,9 @@ function calculateAverageDynamically(dbObjs) {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {{ [x: string]: any; }} targetObj
|
||||
*/
|
||||
function average(targetObj) {
|
||||
for (const key in targetObj) {
|
||||
if (typeof targetObj[key] === "number") {
|
||||
@@ -81,6 +92,9 @@ function calculateAverageDynamically(dbObjs) {
|
||||
return sumObj
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} url
|
||||
*/
|
||||
function run(url) {
|
||||
const response = context.http
|
||||
.fetch(url, {
|
||||
@@ -133,12 +147,103 @@ function setUpQuery(subPath = "/") {
|
||||
|
||||
let query = `${api}?`
|
||||
for (let key in parameters) {
|
||||
// @ts-ignore
|
||||
query += `${key}=${parameters[key]}&`
|
||||
}
|
||||
query += params // Append other parameters without URL encoding
|
||||
return query
|
||||
}
|
||||
|
||||
/**@param {LocalProduct} product */
|
||||
function recalcRatingToProduct(product) {
|
||||
let ratings = context.db.find("rating", {
|
||||
filter: {
|
||||
productId: product.id,
|
||||
status: "approved",
|
||||
},
|
||||
})
|
||||
|
||||
let ratingSum = ratings.map((e) => e.rating).reduce((accumulator, currentValue) => accumulator + currentValue, 0)
|
||||
let ratingAverage = (ratingSum || 0) / (ratings.length || 1)
|
||||
if (isNaN(ratingAverage)) return
|
||||
context.db.update("bigCommerceProducts", product.id, {
|
||||
amountOfRatings: ratings.length,
|
||||
averageRating: ratingAverage,
|
||||
})
|
||||
|
||||
// clear ssr cache because of product update
|
||||
clearSSRCache()
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param {HookContext} c
|
||||
* @param {string} filename
|
||||
* @param {string} locale
|
||||
* @returns {string}
|
||||
*/
|
||||
function tpl(c, filename, locale) {
|
||||
return c.tpl.execute(c.fs.readFile(filename), {
|
||||
context: c,
|
||||
config: config,
|
||||
})
|
||||
}
|
||||
var config = require("../config")
|
||||
function sendOperatorRatingMail() {
|
||||
if (!context.data) context.data = {}
|
||||
let locale = context.request().query("locale")
|
||||
locale = locale ? locale : "de-DE"
|
||||
let order = context.db.find("order", {
|
||||
filter: {
|
||||
_id: context.data.order,
|
||||
},
|
||||
})[0]
|
||||
context.data.order = order
|
||||
context.data.tibiLink = `project/${context.project().id}/collection/rating/edit/${context.data.id}` //projekt ID
|
||||
context.smtp.sendMail({
|
||||
to: config.operatorEmail,
|
||||
from: config.operatorEmail,
|
||||
fromName: config.operatorName,
|
||||
subject: tpl(context, "templates/operator_rating_subject.de-DE.txt", locale),
|
||||
html: tpl(context, "templates/operator_rating_body.de-DE.html", locale),
|
||||
})
|
||||
}
|
||||
|
||||
/**@param {ProductRating} currentRating */
|
||||
function validateAndModifyRating(currentRating) {
|
||||
// Fetch the rating object from the database
|
||||
|
||||
/** @type {ProductRating} */ // @ts-ignore
|
||||
let oldRating = context.db.find("rating", {
|
||||
filter: { orderId: currentRating.orderId, productId: currentRating.productId },
|
||||
})[0]
|
||||
|
||||
if (oldRating && (oldRating.status != "pending" || currentRating.status != "pending")) {
|
||||
//@ts-ignore
|
||||
oldRating.review_date = convertDateToUTCISO(oldRating.review_date)
|
||||
if (oldRating.status == "pending") {
|
||||
oldRating.status = currentRating.status
|
||||
if (currentRating.status == "approved") return currentRating
|
||||
}
|
||||
|
||||
return context?.user?.auth()?.id &&
|
||||
(currentRating?.status == "pending" || currentRating?.status == "rejected") &&
|
||||
(oldRating?.status == "approved" || oldRating?.status == "rejected")
|
||||
? currentRating
|
||||
: oldRating
|
||||
}
|
||||
|
||||
// If the status of the current rating is 'pending', or not provided
|
||||
if (currentRating.status == "pending" || !currentRating.status) {
|
||||
currentRating.status = "pending"
|
||||
if (!currentRating.review_date) {
|
||||
//@ts-ignore
|
||||
currentRating.review_date = new Date().toISOString()
|
||||
}
|
||||
}
|
||||
|
||||
return currentRating
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
log,
|
||||
clearSSRCache,
|
||||
@@ -146,4 +251,7 @@ module.exports = {
|
||||
run,
|
||||
setUpQuery,
|
||||
calculateAverageDynamically,
|
||||
recalcRatingToProduct,
|
||||
sendOperatorRatingMail,
|
||||
validateAndModifyRating,
|
||||
}
|
||||
|
||||
5
api/hooks/products/post_validate.js
Normal file
5
api/hooks/products/post_validate.js
Normal file
@@ -0,0 +1,5 @@
|
||||
;(function () {
|
||||
// fetch all products from bigCommerce and save/update them in the database
|
||||
// pay attention to the avg. product rating, should also be inserted into BigCommerce with every update,
|
||||
// so the product requests dont have to be repeated -> Perfomance!
|
||||
})()
|
||||
15
api/hooks/rating/delete_delete.js
Normal file
15
api/hooks/rating/delete_delete.js
Normal file
@@ -0,0 +1,15 @@
|
||||
;(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["product"] = rating.productId
|
||||
})()
|
||||
17
api/hooks/rating/delete_return.js
Normal file
17
api/hooks/rating/delete_return.js
Normal file
@@ -0,0 +1,17 @@
|
||||
let { recalcRatingToProduct } = require("../lib/utils")
|
||||
;(function () {
|
||||
let product = context.db.find("bigCommerceProducts", {
|
||||
filter: {
|
||||
// @ts-ignore
|
||||
_id: context["product"],
|
||||
},
|
||||
})[0]
|
||||
if (!product)
|
||||
throw {
|
||||
status: 400,
|
||||
error: "Could not resolve rating product",
|
||||
}
|
||||
//@ts-ignore
|
||||
recalcRatingToProduct(product)
|
||||
//TODO: delete rating from bigCommerce
|
||||
})()
|
||||
44
api/hooks/rating/get_read.js
Normal file
44
api/hooks/rating/get_read.js
Normal file
@@ -0,0 +1,44 @@
|
||||
// @ts-check
|
||||
;(function () {
|
||||
/** @type {HookResponse} */
|
||||
let hookResponse
|
||||
let request = context.request()
|
||||
if (request.query("rateIt")) {
|
||||
let orderNumber
|
||||
orderNumber = Number(request.query("orderNumber"))
|
||||
|
||||
if (isNaN(orderNumber))
|
||||
throw {
|
||||
status: 400,
|
||||
message: "Invalid order number.",
|
||||
}
|
||||
|
||||
/*
|
||||
TODO: reprogram to bigcommerce
|
||||
let order = context.db.find("order", {
|
||||
filter: {
|
||||
sequence: orderNumber,
|
||||
},
|
||||
})[0]
|
||||
|
||||
if (!order)
|
||||
throw {
|
||||
status: 400,
|
||||
message: "No entry with this order number.",
|
||||
}
|
||||
|
||||
if (order.deliveryAddress.postcode != request.query("postalcode"))
|
||||
throw {
|
||||
status: 403,
|
||||
message: "Error",
|
||||
}
|
||||
|
||||
hookResponse = {
|
||||
filter: {
|
||||
orderId: order.id,
|
||||
},
|
||||
}*/
|
||||
|
||||
return hookResponse
|
||||
}
|
||||
})()
|
||||
35
api/hooks/rating/post_create.js
Normal file
35
api/hooks/rating/post_create.js
Normal file
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
TODO: reprogram to bigcommerce
|
||||
function productInsideOrder(orderRating) {
|
||||
|
||||
let order = context.db.find("order", {
|
||||
filter: { _id: orderRating.orderId },
|
||||
})[0]
|
||||
|
||||
if (!order) throw { error: "No Order object with given ID.", status: 400 }
|
||||
|
||||
let productsInOrder = order.cart.map((entry) => entry.product.id)
|
||||
if (productsInOrder.length == 0) throw { error: "No products inside the Order.", status: 400 }
|
||||
|
||||
let productInRating = orderRating.productId
|
||||
let productInsideOrder = productsInOrder.includes(productInRating)
|
||||
|
||||
if (!productInsideOrder) throw { error: "Rated products are not inside the Order.", status: 400 }
|
||||
}
|
||||
|
||||
*/
|
||||
;(function () {
|
||||
if (!context?.user?.auth()?.id) {
|
||||
console.log(context?.user?.auth()?.id, "=IDD")
|
||||
//productInsideOrder(context.data)
|
||||
/** @type {ProductRating[]} */ // @ts-ignore
|
||||
let ratings = context.db.find("rating", {
|
||||
filter: {
|
||||
bigCommerceOrderId: context?.data?.orderId,
|
||||
productId: context?.data?.productId,
|
||||
},
|
||||
})
|
||||
|
||||
if (ratings.length) throw { status: 400, error: "Rating already existing" }
|
||||
}
|
||||
})()
|
||||
6
api/hooks/rating/post_return.js
Normal file
6
api/hooks/rating/post_return.js
Normal file
@@ -0,0 +1,6 @@
|
||||
// @ts-check
|
||||
let { sendOperatorRatingMail } = require("../lib/utils")
|
||||
;(function () {
|
||||
//TODO: has to be installed for this project,j ust copied
|
||||
sendOperatorRatingMail()
|
||||
})()
|
||||
4
api/hooks/rating/post_validate.js
Normal file
4
api/hooks/rating/post_validate.js
Normal file
@@ -0,0 +1,4 @@
|
||||
let { validateAndModifyRating } = require("../lib/utils")
|
||||
;(function () {
|
||||
return { data: validateAndModifyRating(context.data) }
|
||||
})()
|
||||
25
api/hooks/rating/put_return.js
Normal file
25
api/hooks/rating/put_return.js
Normal file
@@ -0,0 +1,25 @@
|
||||
let { sendOperatorRatingMail, recalcRatingToProduct } = require("../lib/utils")
|
||||
|
||||
;(function () {
|
||||
/** @type {ProductRating} */
|
||||
let rating = context.data
|
||||
/** @type {LocalProduct} */ // @ts-ignore
|
||||
let product = context.db.find("bigCommerceProducts", {
|
||||
filter: {
|
||||
_id: rating.productId,
|
||||
},
|
||||
})[0]
|
||||
|
||||
if (!product)
|
||||
throw {
|
||||
status: 400,
|
||||
error: "Product not found.",
|
||||
}
|
||||
recalcRatingToProduct(product)
|
||||
|
||||
/**@type {any} */
|
||||
let oldRating = context["oldRating"]
|
||||
if (!oldRating || JSON.stringify(rating.rating) != JSON.stringify(oldRating)) {
|
||||
sendOperatorRatingMail()
|
||||
}
|
||||
})()
|
||||
14
api/hooks/rating/put_update.js
Normal file
14
api/hooks/rating/put_update.js
Normal file
@@ -0,0 +1,14 @@
|
||||
;(function () {
|
||||
if (!context?.user?.auth()?.id) {
|
||||
console.log(context?.user?.auth()?.id)
|
||||
//TODO: productInsideOrder(context.data) look in post
|
||||
}
|
||||
/** @type {ProductRating} */ // @ts-ignore
|
||||
let ratingObj = context.db.find("rating", {
|
||||
filter: {
|
||||
_id: context.data.id,
|
||||
},
|
||||
})[0]
|
||||
|
||||
context["oldRating"] = ratingObj.rating
|
||||
})()
|
||||
4
api/hooks/rating/put_validate.js
Normal file
4
api/hooks/rating/put_validate.js
Normal file
@@ -0,0 +1,4 @@
|
||||
let { validateAndModifyRating } = require("../lib/utils")
|
||||
;(function () {
|
||||
return { data: validateAndModifyRating(context.data) }
|
||||
})()
|
||||
@@ -21,7 +21,7 @@ const { ssrRequest } = require("../lib/ssr-server.js")
|
||||
|
||||
if (url) {
|
||||
// comment will be printed to html later
|
||||
let comment = ""
|
||||
let comment = "yml"
|
||||
/** @type {Date} */ // @ts-ignore
|
||||
context.ssrCacheValidUntil = null
|
||||
|
||||
|
||||
9
api/templates/operator_rating_body.de-DE.html
Normal file
9
api/templates/operator_rating_body.de-DE.html
Normal file
@@ -0,0 +1,9 @@
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
|
||||
<html>
|
||||
<body>
|
||||
<h1>Neue Bewertung</h1>
|
||||
<label>Sequenz Nr.:</label> {{context.data.order.sequence}}<br />
|
||||
<label>Tibi Admin Link:</label>
|
||||
<a href="{{config.tibiUrl}}{{context.data.tibiLink}}" target="_blank"> hier Klicken </a><br />
|
||||
</body>
|
||||
</html>
|
||||
1
api/templates/operator_rating_subject.de-DE.txt
Normal file
1
api/templates/operator_rating_subject.de-DE.txt
Normal file
@@ -0,0 +1 @@
|
||||
BKDF: Neue Bewertung eingetroffen.
|
||||
Reference in New Issue
Block a user