general setup

This commit is contained in:
2024-03-11 17:14:31 +00:00
parent 5af89ab258
commit b82ab35bbe
342 changed files with 3672 additions and 10809 deletions

View File

@@ -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

View 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."

View File

@@ -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:

View 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'

View File

@@ -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

View File

@@ -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(){

View 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
View 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"

View File

@@ -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:

View File

@@ -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

View File

@@ -101,8 +101,8 @@
else if (oldVal !== newVal) {
updateLogs.push({
field: newPath,
previous: oldVal.toString(),
current: newVal.toString(),
previous: oldVal?.toString(),
current: newVal?.toString(),
})
}
})

View File

@@ -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/",
}

View File

@@ -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",
}

View File

@@ -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,
}

View 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!
})()

View 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
})()

View 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
})()

View 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
}
})()

View 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" }
}
})()

View 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()
})()

View File

@@ -0,0 +1,4 @@
let { validateAndModifyRating } = require("../lib/utils")
;(function () {
return { data: validateAndModifyRating(context.data) }
})()

View 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()
}
})()

View 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
})()

View File

@@ -0,0 +1,4 @@
let { validateAndModifyRating } = require("../lib/utils")
;(function () {
return { data: validateAndModifyRating(context.data) }
})()

View File

@@ -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

View 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>

View File

@@ -0,0 +1 @@
BKDF: Neue Bewertung eingetroffen.