diff --git a/.changeset/six-buses-sit.md b/.changeset/six-buses-sit.md new file mode 100644 index 00000000..231a9826 --- /dev/null +++ b/.changeset/six-buses-sit.md @@ -0,0 +1,6 @@ +--- +'@o2s/integrations.contentful-cms': minor +'@o2s/api-harmonization': minor +--- + +feat: added contentful integration diff --git a/apps/api-harmonization/.env.local b/apps/api-harmonization/.env.local index 91a14cd1..34c88aa3 100644 --- a/apps/api-harmonization/.env.local +++ b/apps/api-harmonization/.env.local @@ -29,3 +29,8 @@ CACHE_REDIS_PASS={REDIS_PASS} ALGOLIA_APP_ID= ALGOLIA_API_KEY= + +CF_TOKEN= +CF_SPACE_ID= +CF_ENV= +CF_MANAGEMENT_TOKEN= \ No newline at end of file diff --git a/apps/api-harmonization/turbo.json b/apps/api-harmonization/turbo.json index 63ec7a85..359a9d57 100644 --- a/apps/api-harmonization/turbo.json +++ b/apps/api-harmonization/turbo.json @@ -1,8 +1,12 @@ { - "extends": ["//"], + "extends": [ + "//" + ], "tasks": { "dev": { - "dependsOn": ["build"] + "dependsOn": [ + "build" + ] }, "start": { "env": [ @@ -25,8 +29,12 @@ "CACHE_REDIS_PORT", "CACHE_REDIS_PASS", "ALGOLIA_APP_ID", - "ALGOLIA_API_KEY" + "ALGOLIA_API_KEY", + "CF_TOKEN", + "CF_SPACE_ID", + "CF_ENV", + "CF_MANAGEMENT_TOKEN" ] } } -} +} \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index 9870fbb1..d8715730 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28,7 +28,7 @@ }, "apps/api-harmonization": { "name": "@o2s/api-harmonization", - "version": "0.17.0", + "version": "0.18.0", "license": "MIT", "dependencies": { "@nestjs/axios": "^3.1.2", @@ -36,8 +36,8 @@ "@nestjs/config": "^3.3.0", "@nestjs/core": "^10.0.0", "@nestjs/platform-express": "^10.0.0", - "@o2s/framework": "^0.19.0", - "@o2s/integrations.mocked": "^0.18.0", + "@o2s/framework": "^0.20.0", + "@o2s/integrations.mocked": "^0.19.0", "@o2s/utils.logger": "^0.9.3", "compression": "^1.8.0", "cookie": "^1.0.2", @@ -349,7 +349,7 @@ }, "apps/docs": { "name": "@o2s/docs", - "version": "0.10.3", + "version": "0.10.4", "dependencies": { "@docusaurus/core": "3.7.0", "@docusaurus/preset-classic": "3.7.0", @@ -448,7 +448,7 @@ }, "apps/frontend": { "name": "@o2s/frontend", - "version": "0.17.0", + "version": "0.18.0", "dependencies": { "@auth/prisma-adapter": "^2.8.0", "@o2s/api-harmonization": "*", @@ -3756,6 +3756,14 @@ "node": ">=0.1.90" } }, + "node_modules/@contentful/rich-text-types": { + "version": "16.8.5", + "resolved": "https://registry.npmjs.org/@contentful/rich-text-types/-/rich-text-types-16.8.5.tgz", + "integrity": "sha512-q18RJuJCOuYveGiCIjE5xLCQc5lZ3L2Qgxrlg/H2YEobDFqdtmklazRi1XwEWaK3tMg6yVXBzKKkQfLB4qW14A==", + "engines": { + "node": ">=6.0.0" + } + }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", @@ -11174,6 +11182,10 @@ "resolved": "packages/integrations/algolia", "link": true }, + "node_modules/@o2s/integrations.contentful-cms": { + "resolved": "packages/integrations/contentful-cms", + "link": true + }, "node_modules/@o2s/integrations.mocked": { "resolved": "packages/integrations/mocked", "link": true @@ -13914,6 +13926,11 @@ "integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==", "dev": true }, + "node_modules/@types/json-patch": { + "version": "0.0.30", + "resolved": "https://registry.npmjs.org/@types/json-patch/-/json-patch-0.0.30.tgz", + "integrity": "sha512-MhCUjojzDhVLnZnxwPwa+rETFRDQ0ffjxYdrqOP6TBO2O0/Z64PV5tNeYApo4bc4y4frbWOrRwv/eEkXlI13Rw==" + }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -13964,6 +13981,11 @@ "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==" }, + "node_modules/@types/minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==" + }, "node_modules/@types/ms": { "version": "0.7.34", "resolved": "https://registry.npmjs.org/@types/ms/-/ms-0.7.34.tgz", @@ -14233,6 +14255,11 @@ "@types/node": "*" } }, + "node_modules/@types/normalize-package-data": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", + "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==" + }, "node_modules/@types/parse-json": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", @@ -15379,6 +15406,14 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/arrify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", + "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/asap": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", @@ -15510,9 +15545,9 @@ } }, "node_modules/axios": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.3.tgz", - "integrity": "sha512-iP4DebzoNlP/YN2dpwCgb8zoCmhtkajzS48JvwmkSkXvPI3DHc7m+XYL5tGnSlJtR6nImXZmdCuN5aP8dh1d8A==", + "version": "1.8.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.8.4.tgz", + "integrity": "sha512-eBSYY4Y68NNlHbHBMdeDmKNtDgXWhQsJcGqzO3iLUM0GraQFSS9cVgPX5I9b3lbdFKyYoAEGAZF1DwhTaljNAw==", "peer": true, "dependencies": { "follow-redirects": "^1.15.6", @@ -16417,7 +16452,6 @@ "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", - "dev": true, "engines": { "node": ">=6" } @@ -16430,6 +16464,30 @@ "node": ">= 6" } }, + "node_modules/camelcase-keys": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/camelcase-keys/-/camelcase-keys-6.2.2.tgz", + "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==", + "dependencies": { + "camelcase": "^5.3.1", + "map-obj": "^4.0.0", + "quick-lru": "^4.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/camelcase-keys/node_modules/quick-lru": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-4.0.1.tgz", + "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==", + "engines": { + "node": ">=8" + } + }, "node_modules/caniuse-api": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/caniuse-api/-/caniuse-api-3.0.0.tgz", @@ -17824,6 +17882,97 @@ "node": ">= 0.6" } }, + "node_modules/contentful": { + "version": "9.3.5", + "resolved": "https://registry.npmjs.org/contentful/-/contentful-9.3.5.tgz", + "integrity": "sha512-QVXHwD9nxREBpcemC6Po2LUYStmBBHPyVbN3SKzkR+WmIZhflF6x+TDmmz2jcCg/RSN+INDZbhe8FQ1S/zTE8w==", + "hasInstallScript": true, + "dependencies": { + "@contentful/rich-text-types": "^16.0.2", + "axios": "^0.27.0", + "contentful-resolve-response": "^1.3.12", + "contentful-sdk-core": "^7.0.5", + "fast-copy": "^2.1.7", + "json-stringify-safe": "^5.0.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/contentful-resolve-response": { + "version": "1.9.2", + "resolved": "https://registry.npmjs.org/contentful-resolve-response/-/contentful-resolve-response-1.9.2.tgz", + "integrity": "sha512-VTY1hZGh29yspBeveJ62cuDf+cw9Iq/NcKWBdNHjTq8hvgxz+E19+Pej3LW/02o98+D0XcKfcPYXIpm4lHY+eg==", + "dependencies": { + "fast-copy": "^2.1.7" + }, + "engines": { + "node": ">=4.7.2" + } + }, + "node_modules/contentful-sdk-core": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/contentful-sdk-core/-/contentful-sdk-core-7.1.0.tgz", + "integrity": "sha512-RzTPnRsbCdVAhyka3wa9sDsAu9YsxoerNgaMqd63Ljb7qpG2zkdHcP7NTfyIbuHDJNJdAQdifyafxfEEwP+q/w==", + "dependencies": { + "fast-copy": "^2.1.7", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "p-throttle": "^4.1.1", + "qs": "^6.9.4" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/contentful-typescript-codegen": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/contentful-typescript-codegen/-/contentful-typescript-codegen-3.4.0.tgz", + "integrity": "sha512-SLNd7UxD/EfH0GA/R6mB51kLyntEySWTl5og7bSKNsvQeYfDbHq74V5PDDZyb0smquG7uv98aAIOreTa8bSuhA==", + "dependencies": { + "fs-extra": "^9.1.0", + "lodash": "^4.17.21", + "meow": "^9.0.0" + }, + "bin": { + "contentful-typescript-codegen": "dist/contentful-typescript-codegen.js" + }, + "engines": { + "node": ">=6.0.0" + }, + "peerDependencies": { + "prettier": ">= 1", + "ts-node": ">= 9.0.0" + }, + "peerDependenciesMeta": { + "ts-node": { + "optional": true + } + } + }, + "node_modules/contentful-typescript-codegen/node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/contentful/node_modules/axios": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz", + "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==", + "dependencies": { + "follow-redirects": "^1.14.9", + "form-data": "^4.0.0" + } + }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", @@ -19238,7 +19387,29 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", "integrity": "sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", - "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/decamelize-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/decamelize-keys/-/decamelize-keys-1.1.1.tgz", + "integrity": "sha512-WiPxgEirIV0/eIOMcnFBA3/IJZAZqKnwAwWyvvdi4lsr1WCN22nhdf/3db3DoZcUjTV2SqfzIwNyp6y2xs3nmg==", + "dependencies": { + "decamelize": "^1.1.0", + "map-obj": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/decamelize-keys/node_modules/map-obj": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-1.0.1.tgz", + "integrity": "sha512-7N/q3lyZ+LVCp7PzuxrJr4KMbBE2hW7BT7YNia330OFxIf4d3r5zVpicP2650l7CPN6RM9zOJRl3NGpqSiw3Eg==", "engines": { "node": ">=0.10.0" } @@ -21337,6 +21508,11 @@ "url": "https://github.com/sponsors/jaydenseric" } }, + "node_modules/fast-copy": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/fast-copy/-/fast-copy-2.1.7.tgz", + "integrity": "sha512-ozrGwyuCTAy7YgFCua8rmqmytECYk/JYAMXcswOcm0qvGoE3tPb7ivBeIHTOK2DiapBhDZgacIhzhQIKU5TCfA==" + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -22709,6 +22885,14 @@ "uglify-js": "^3.1.4" } }, + "node_modules/hard-rejection": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/hard-rejection/-/hard-rejection-2.1.0.tgz", + "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==", + "engines": { + "node": ">=6" + } + }, "node_modules/has-bigints": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", @@ -23014,6 +23198,33 @@ "react-is": "^16.7.0" } }, + "node_modules/hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/hosted-git-info/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/hosted-git-info/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + }, "node_modules/hpack.js": { "version": "2.1.6", "resolved": "https://registry.npmjs.org/hpack.js/-/hpack.js-2.1.6.tgz", @@ -26443,6 +26654,11 @@ "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "devOptional": true }, + "node_modules/json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==" + }, "node_modules/json-to-pretty-yaml": { "version": "1.2.2", "resolved": "https://registry.npmjs.org/json-to-pretty-yaml/-/json-to-pretty-yaml-1.2.2.tgz", @@ -27490,6 +27706,17 @@ "node": ">=0.10.0" } }, + "node_modules/map-obj": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/map-obj/-/map-obj-4.3.0.tgz", + "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/mark.js": { "version": "8.11.1", "resolved": "https://registry.npmjs.org/mark.js/-/mark.js-8.11.1.tgz", @@ -27952,6 +28179,50 @@ "node": ">= 4.0.0" } }, + "node_modules/meow": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/meow/-/meow-9.0.0.tgz", + "integrity": "sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ==", + "dependencies": { + "@types/minimist": "^1.2.0", + "camelcase-keys": "^6.2.2", + "decamelize": "^1.2.0", + "decamelize-keys": "^1.1.0", + "hard-rejection": "^2.1.0", + "minimist-options": "4.1.0", + "normalize-package-data": "^3.0.0", + "read-pkg-up": "^7.0.1", + "redent": "^3.0.0", + "trim-newlines": "^3.0.0", + "type-fest": "^0.18.0", + "yargs-parser": "^20.2.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/meow/node_modules/type-fest": { + "version": "0.18.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.18.1.tgz", + "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/meow/node_modules/yargs-parser": { + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "engines": { + "node": ">=10" + } + }, "node_modules/merge-descriptors": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", @@ -29802,6 +30073,14 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/min-indent": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "engines": { + "node": ">=4" + } + }, "node_modules/mini-css-extract-plugin": { "version": "2.9.2", "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-2.9.2.tgz", @@ -29897,6 +30176,27 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/minimist-options": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/minimist-options/-/minimist-options-4.1.0.tgz", + "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==", + "dependencies": { + "arrify": "^1.0.1", + "is-plain-obj": "^1.1.0", + "kind-of": "^6.0.3" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/minimist-options/node_modules/is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha512-yvkRyxmFKEOQ4pNXCmJG5AEQNlXJS5LaONXo5/cLdTZdWvsZ1ioJEonLGAosKlMWE8lwUy/bJzMjcw8az73+Fg==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/minipass": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", @@ -30458,6 +30758,20 @@ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==" }, + "node_modules/normalize-package-data": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", + "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==", + "dependencies": { + "hosted-git-info": "^4.0.1", + "is-core-module": "^2.5.0", + "semver": "^7.3.4", + "validate-npm-package-license": "^3.0.1" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", @@ -30946,6 +31260,17 @@ "node": ">=8" } }, + "node_modules/p-throttle": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/p-throttle/-/p-throttle-4.1.1.tgz", + "integrity": "sha512-TuU8Ato+pRTPJoDzYD4s7ocJYcNSEZRvlxoq3hcPI2kZDZ49IQ1Wkj7/gDJc3X7XiEAAvRGtDzdXJI0tC3IL1g==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", @@ -32933,7 +33258,6 @@ "version": "3.5.3", "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.5.3.tgz", "integrity": "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw==", - "dev": true, "bin": { "prettier": "bin/prettier.cjs" }, @@ -33847,6 +34171,124 @@ "pify": "^2.3.0" } }, + "node_modules/read-pkg": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz", + "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==", + "dependencies": { + "@types/normalize-package-data": "^2.4.0", + "normalize-package-data": "^2.5.0", + "parse-json": "^5.0.0", + "type-fest": "^0.6.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz", + "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==", + "dependencies": { + "find-up": "^4.1.0", + "read-pkg": "^5.2.0", + "type-fest": "^0.8.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/find-up": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", + "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", + "dependencies": { + "locate-path": "^5.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/locate-path": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", + "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", + "dependencies": { + "p-locate": "^4.1.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/p-limit": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", + "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", + "dependencies": { + "p-try": "^2.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg-up/node_modules/p-locate": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", + "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", + "dependencies": { + "p-limit": "^2.2.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg-up/node_modules/type-fest": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.8.1.tgz", + "integrity": "sha512-4dbzIzqvjtgiM5rw1k5rEHtBANKmdudhGyBEajN01fEyhaAIhsoKNy6y7+IN93IfpFtwY9iqi7kD+xwKhQsNJA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/read-pkg/node_modules/hosted-git-info": { + "version": "2.8.9", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", + "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==" + }, + "node_modules/read-pkg/node_modules/normalize-package-data": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz", + "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==", + "dependencies": { + "hosted-git-info": "^2.1.4", + "resolve": "^1.10.0", + "semver": "2 || 3 || 4 || 5", + "validate-npm-package-license": "^3.0.1" + } + }, + "node_modules/read-pkg/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/read-pkg/node_modules/type-fest": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz", + "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==", + "engines": { + "node": ">=8" + } + }, "node_modules/read-yaml-file": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/read-yaml-file/-/read-yaml-file-1.1.0.tgz", @@ -34065,6 +34507,18 @@ "node": "*" } }, + "node_modules/redent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", + "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dependencies": { + "indent-string": "^4.0.0", + "strip-indent": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/redis": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/redis/-/redis-4.7.0.tgz", @@ -35809,6 +36263,34 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==" + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.21", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.21.tgz", + "integrity": "sha512-Bvg/8F5XephndSK3JffaRqdT+gyhfqIPwDHpX80tJrF8QQRYMo8sNMeaZ2Dp5+jhwKnUmIOyFFQfHRkjJm5nXg==" + }, "node_modules/spdy": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/spdy/-/spdy-4.0.2.tgz", @@ -36167,6 +36649,17 @@ "node": ">=6" } }, + "node_modules/strip-indent": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", + "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dependencies": { + "min-indent": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -36931,6 +37424,14 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/trim-newlines": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/trim-newlines/-/trim-newlines-3.0.1.tgz", + "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", + "engines": { + "node": ">=8" + } + }, "node_modules/triple-beam": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", @@ -38215,6 +38716,15 @@ "node": ">=10.12.0" } }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, "node_modules/validate-npm-package-name": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/validate-npm-package-name/-/validate-npm-package-name-5.0.1.tgz", @@ -39831,7 +40341,7 @@ }, "packages/framework": { "name": "@o2s/framework", - "version": "0.19.0", + "version": "0.20.0", "license": "MIT", "dependencies": { "@nestjs/axios": "^3.1.2", @@ -39894,9 +40404,78 @@ "typescript": "^5.8.2" } }, + "packages/integrations/contentful-cms": { + "name": "@o2s/integrations.contentful-cms", + "version": "0.1.0", + "license": "MIT", + "dependencies": { + "@nestjs/axios": "^3.1.2", + "@nestjs/common": "^10.0.0", + "@nestjs/config": "^3.3.0", + "@nestjs/core": "^10.0.0", + "@o2s/framework": "*", + "@o2s/utils.logger": "*", + "contentful": "^9.3.5", + "contentful-management": "^10.36.0", + "contentful-typescript-codegen": "^3.4.0", + "rxjs": "^7.8.1" + }, + "devDependencies": { + "@o2s/eslint-config": "*", + "@o2s/prettier-config": "*", + "@o2s/typescript-config": "*", + "concurrently": "^9.1.2", + "eslint": "^8.57.0", + "prettier": "^3.5.3", + "tsc-alias": "^1.8.13", + "typescript": "^5.8.2" + } + }, + "packages/integrations/contentful-cms/node_modules/contentful-management": { + "version": "10.36.0", + "resolved": "https://registry.npmjs.org/contentful-management/-/contentful-management-10.36.0.tgz", + "integrity": "sha512-YcTkIpDpk4Z5mduFTdcVCH8cfwsm1yWv5sKkS1i9NXVCU1AnEU1BwyvEVwsp5cDuknaXXadkLVYFqovPHT96Qg==", + "dependencies": { + "@contentful/rich-text-types": "^16.0.3", + "@types/json-patch": "0.0.30", + "axios": "^0.27.1", + "contentful-sdk-core": "^7.0.1", + "fast-copy": "^3.0.0", + "lodash.isplainobject": "^4.0.6", + "type-fest": "^3.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "packages/integrations/contentful-cms/node_modules/contentful-management/node_modules/axios": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.27.2.tgz", + "integrity": "sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==", + "dependencies": { + "follow-redirects": "^1.14.9", + "form-data": "^4.0.0" + } + }, + "packages/integrations/contentful-cms/node_modules/contentful-management/node_modules/fast-copy": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/fast-copy/-/fast-copy-3.0.2.tgz", + "integrity": "sha512-dl0O9Vhju8IrcLndv2eU4ldt1ftXMqqfgN4H1cpmGV7P6jeB9FwpN9a2c8DPGE1Ys88rNUJVYDHq73CGAGOPfQ==" + }, + "packages/integrations/contentful-cms/node_modules/contentful-management/node_modules/type-fest": { + "version": "3.13.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-3.13.1.tgz", + "integrity": "sha512-tLq3bSNx+xSpwvAJnzrK0Ep5CLNWjvFTOp71URMaAEWBfRb9nnJiBoUe0tF8bI4ZFO3omgBR6NvnbzVUT3Ly4g==", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "packages/integrations/mocked": { "name": "@o2s/integrations.mocked", - "version": "0.18.0", + "version": "0.19.0", "license": "MIT", "dependencies": { "@nestjs/axios": "^3.1.2", @@ -39944,7 +40523,7 @@ }, "packages/integrations/strapi-cms": { "name": "@o2s/integrations.strapi-cms", - "version": "0.17.0", + "version": "0.18.0", "license": "MIT", "dependencies": { "@nestjs/axios": "^3.1.2", @@ -39989,7 +40568,7 @@ }, "packages/ui": { "name": "@o2s/ui", - "version": "0.11.0", + "version": "0.12.0", "license": "MIT", "dependencies": { "@radix-ui/react-accordion": "^1.2.3", diff --git a/packages/integrations/contentful-cms/.eslintrc.js b/packages/integrations/contentful-cms/.eslintrc.js new file mode 100644 index 00000000..ce6729bc --- /dev/null +++ b/packages/integrations/contentful-cms/.eslintrc.js @@ -0,0 +1,11 @@ +/** @type {import("eslint").Linter.Config} */ +module.exports = { + root: true, + extends: ['@o2s/eslint-config/api.js'], + parser: '@typescript-eslint/parser', + parserOptions: { + project: 'tsconfig.json', + tsconfigRootDir: __dirname, + sourceType: 'module', + }, +}; diff --git a/packages/integrations/contentful-cms/.gitignore b/packages/integrations/contentful-cms/.gitignore new file mode 100644 index 00000000..11eb8de0 --- /dev/null +++ b/packages/integrations/contentful-cms/.gitignore @@ -0,0 +1,56 @@ + +# compiled output +/dist +/node_modules +/build + +# Logs +logs +*.log +npm-debug.log* +pnpm-debug.log* +yarn-debug.log* +yarn-error.log* +lerna-debug.log* + +# OS +.DS_Store + +# Tests +/coverage +/.nyc_output + +# IDEs and editors +/.idea +.project +.classpath +.c9/ +*.launch +.settings/ +*.sublime-workspace + +# IDE - VSCode +.vscode/* +!.vscode/settings.json +!.vscode/tasks.json +!.vscode/launch.json +!.vscode/extensions.json + +# dotenv environment variable files +.env +.env.development.local +.env.test.local +.env.production.local + +# temp directory +.temp +.tmp + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Diagnostic reports (https://nodejs.org/api/report.html) +report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json diff --git a/packages/integrations/contentful-cms/.prettierrc.mjs b/packages/integrations/contentful-cms/.prettierrc.mjs new file mode 100644 index 00000000..7c192fbb --- /dev/null +++ b/packages/integrations/contentful-cms/.prettierrc.mjs @@ -0,0 +1,11 @@ +import apiConfig from "@o2s/prettier-config/api.mjs"; + +/** + * @see https://prettier.io/docs/en/configuration.html + * @type {import("prettier").Config} + */ +const config = { + ...apiConfig, +}; + +export default config; diff --git a/packages/integrations/contentful-cms/CHANGELOG.md b/packages/integrations/contentful-cms/CHANGELOG.md new file mode 100644 index 00000000..e69de29b diff --git a/packages/integrations/contentful-cms/generated/contentful.d.ts b/packages/integrations/contentful-cms/generated/contentful.d.ts new file mode 100644 index 00000000..83c4c604 --- /dev/null +++ b/packages/integrations/contentful-cms/generated/contentful.d.ts @@ -0,0 +1,517 @@ +// THIS FILE IS AUTOMATICALLY GENERATED. DO NOT MODIFY IT. +import { Entry } from 'contentful'; + +export interface IBlockFaqFields { + /** title */ + title: string; + + /** subtitle */ + subtitle: string; + + /** items */ + items: IComponentFaqItem[]; + + /** banner */ + banner?: IComponentBanner | undefined; +} + +export interface IBlockFaq extends Entry { + sys: { + id: string; + type: string; + createdAt: string; + updatedAt: string; + locale: string; + contentType: { + sys: { + id: 'blockFaq'; + linkType: 'ContentType'; + type: 'Link'; + }; + }; + }; +} + +export interface IBlockTicketListFields { + /** title */ + title: string; + + /** subTitle */ + subTitle: string; + + /** noResults */ + noResults: IComponentNoResult; + + /** detailsUrl */ + detailsUrl: string; + + /** table */ + table: IComponentTable; + + /** pagination */ + pagination: IComponentPagination; + + /** fields */ + fields: IComponentFieldMapping[]; + + /** labels */ + labels: IDataConfigurableTexts; +} + +export interface IBlockTicketList extends Entry { + sys: { + id: string; + type: string; + createdAt: string; + updatedAt: string; + locale: string; + contentType: { + sys: { + id: 'blockTicketList'; + linkType: 'ContentType'; + type: 'Link'; + }; + }; + }; +} + +export interface IComponentBannerFields { + /** title */ + title: string; + + /** description */ + description?: string | undefined; + + /** link */ + link?: IComponentLink | undefined; +} + +export interface IComponentBanner extends Entry { + sys: { + id: string; + type: string; + createdAt: string; + updatedAt: string; + locale: string; + contentType: { + sys: { + id: 'componentBanner'; + linkType: 'ContentType'; + type: 'Link'; + }; + }; + }; +} + +export interface IComponentFaqItemFields { + /** title */ + title: string; + + /** content */ + content: string; +} + +export interface IComponentFaqItem extends Entry { + sys: { + id: string; + type: string; + createdAt: string; + updatedAt: string; + locale: string; + contentType: { + sys: { + id: 'componentFaqItem'; + linkType: 'ContentType'; + type: 'Link'; + }; + }; + }; +} + +export interface IComponentFieldMappingFields { + /** name */ + name: string; + + /** values */ + values: IComponentKeyValue[]; +} + +export interface IComponentFieldMapping extends Entry { + sys: { + id: string; + type: string; + createdAt: string; + updatedAt: string; + locale: string; + contentType: { + sys: { + id: 'componentFieldMapping'; + linkType: 'ContentType'; + type: 'Link'; + }; + }; + }; +} + +export interface IComponentKeyValueFields { + /** key */ + key: string; + + /** value */ + value: string; +} + +export interface IComponentKeyValue extends Entry { + sys: { + id: string; + type: string; + createdAt: string; + updatedAt: string; + locale: string; + contentType: { + sys: { + id: 'componentKeyValue'; + linkType: 'ContentType'; + type: 'Link'; + }; + }; + }; +} + +export interface IComponentLinkFields { + /** label */ + label: string; + + /** page */ + page?: IPage | undefined; + + /** url */ + url?: string | undefined; +} + +export interface IComponentLink extends Entry { + sys: { + id: string; + type: string; + createdAt: string; + updatedAt: string; + locale: string; + contentType: { + sys: { + id: 'componentLink'; + linkType: 'ContentType'; + type: 'Link'; + }; + }; + }; +} + +export interface IComponentNoResultFields { + /** title */ + title: string; + + /** description */ + description: string; +} + +export interface IComponentNoResult extends Entry { + sys: { + id: string; + type: string; + createdAt: string; + updatedAt: string; + locale: string; + contentType: { + sys: { + id: 'componentNoResult'; + linkType: 'ContentType'; + type: 'Link'; + }; + }; + }; +} + +export interface IComponentPaginationFields { + /** description */ + description: string; + + /** perPage */ + perPage: number; + + /** previousLabel */ + previousLabel: string; + + /** nextLabel */ + nextLabel: string; + + /** selectPageLabel */ + selectPageLabel: string; +} + +export interface IComponentPagination extends Entry { + sys: { + id: string; + type: string; + createdAt: string; + updatedAt: string; + locale: string; + contentType: { + sys: { + id: 'componentPagination'; + linkType: 'ContentType'; + type: 'Link'; + }; + }; + }; +} + +export interface IComponentTableFields { + /** columns */ + columns: IComponentTableColumn[]; + + /** actionsTitle */ + actionsTitle: string; + + /** actionsLabel */ + actionsLabel: string; +} + +export interface IComponentTable extends Entry { + sys: { + id: string; + type: string; + createdAt: string; + updatedAt: string; + locale: string; + contentType: { + sys: { + id: 'componentTable'; + linkType: 'ContentType'; + type: 'Link'; + }; + }; + }; +} + +export interface IComponentTableColumnFields { + /** field */ + field: string; + + /** title */ + title: string; +} + +export interface IComponentTableColumn extends Entry { + sys: { + id: string; + type: string; + createdAt: string; + updatedAt: string; + locale: string; + contentType: { + sys: { + id: 'componentTableColumn'; + linkType: 'ContentType'; + type: 'Link'; + }; + }; + }; +} + +export interface IDataConfigurableTextsFields { + /** today */ + today: string; + + /** yesterday */ + yesterday: string; + + /** showMore */ + showMore: string; + + /** showLess */ + showLess: string; + + /** show */ + show: string; + + /** hide */ + hide: string; + + /** clear */ + clear: string; + + /** apply */ + apply: string; + + /** edit */ + edit: string; + + /** cancel */ + cancel: string; + + /** save */ + save: string; + + /** delete */ + delete: string; + + /** logIn */ + logIn: string; + + /** logOut */ + logOut: string; + + /** renew */ + renew: string; + + /** settings */ + settings: string; +} + +export interface IDataConfigurableTexts extends Entry { + sys: { + id: string; + type: string; + createdAt: string; + updatedAt: string; + locale: string; + contentType: { + sys: { + id: 'dataConfigurableTexts'; + linkType: 'ContentType'; + type: 'Link'; + }; + }; + }; +} + +export interface IPageFields { + /** slug */ + slug: string; + + /** hasOwnTitle */ + hasOwnTitle: boolean; + + /** parent */ + parent?: IPage | undefined; + + /** seo */ + seo?: IPageSeo | undefined; + + /** template */ + template: IPageOneColumnTemplate; +} + +export interface IPage extends Entry { + sys: { + id: string; + type: string; + createdAt: string; + updatedAt: string; + locale: string; + contentType: { + sys: { + id: 'page'; + linkType: 'ContentType'; + type: 'Link'; + }; + }; + }; +} + +export interface IPageOneColumnTemplateFields { + /** title */ + title: string; + + /** mainSlot */ + mainSlot: (IBlockTicketList | IBlockFaq)[]; +} + +export interface IPageOneColumnTemplate extends Entry { + sys: { + id: string; + type: string; + createdAt: string; + updatedAt: string; + locale: string; + contentType: { + sys: { + id: 'pageOneColumnTemplate'; + linkType: 'ContentType'; + type: 'Link'; + }; + }; + }; +} + +export interface IPageSeoFields { + /** title */ + title: string; + + /** description */ + description: string; + + /** noIndex */ + noIndex: boolean; + + /** noFollow */ + noFollow: boolean; + + /** keywords */ + keywords?: string[] | undefined; +} + +export interface IPageSeo extends Entry { + sys: { + id: string; + type: string; + createdAt: string; + updatedAt: string; + locale: string; + contentType: { + sys: { + id: 'pageSeo'; + linkType: 'ContentType'; + type: 'Link'; + }; + }; + }; +} + +export type CONTENT_TYPE = + | 'blockFaq' + | 'blockTicketList' + | 'componentBanner' + | 'componentFaqItem' + | 'componentFieldMapping' + | 'componentKeyValue' + | 'componentLink' + | 'componentNoResult' + | 'componentPagination' + | 'componentTable' + | 'componentTableColumn' + | 'dataConfigurableTexts' + | 'page' + | 'pageOneColumnTemplate' + | 'pageSeo'; + +export type IEntry = + | IBlockFaq + | IBlockTicketList + | IComponentBanner + | IComponentFaqItem + | IComponentFieldMapping + | IComponentKeyValue + | IComponentLink + | IComponentNoResult + | IComponentPagination + | IComponentTable + | IComponentTableColumn + | IDataConfigurableTexts + | IPage + | IPageOneColumnTemplate + | IPageSeo; + +export type LOCALE_CODE = 'en'; + +export type CONTENTFUL_DEFAULT_LOCALE_CODE = 'en'; diff --git a/packages/integrations/contentful-cms/getContentfulEnvironment.ts b/packages/integrations/contentful-cms/getContentfulEnvironment.ts new file mode 100644 index 00000000..25293864 --- /dev/null +++ b/packages/integrations/contentful-cms/getContentfulEnvironment.ts @@ -0,0 +1,19 @@ +import { strict as assert } from 'assert'; +import * as contentfulManagement from 'contentful-management'; +import { EnvironmentGetter } from 'contentful-typescript-codegen'; + +const { CF_MANAGEMENT_TOKEN, CF_SPACE_ID, CF_ENV } = process.env; + +assert(CF_MANAGEMENT_TOKEN); +assert(CF_SPACE_ID); +assert(CF_ENV); + +const getContentfulEnvironment: EnvironmentGetter = () => { + const contentfulClient = contentfulManagement.createClient({ + accessToken: CF_MANAGEMENT_TOKEN, + }); + + return contentfulClient.getSpace(CF_SPACE_ID).then((space) => space.getEnvironment(CF_ENV)); +}; + +module.exports = getContentfulEnvironment; diff --git a/packages/integrations/contentful-cms/lint-staged.config.mjs b/packages/integrations/contentful-cms/lint-staged.config.mjs new file mode 100644 index 00000000..ff4483cc --- /dev/null +++ b/packages/integrations/contentful-cms/lint-staged.config.mjs @@ -0,0 +1,4 @@ +export default { + '*.{js,jsx,ts,tsx,css,scss}': ['prettier --write'], + '*.{js,jsx,ts,tsx}': () => 'tsc --noEmit', +}; diff --git a/packages/integrations/contentful-cms/package.json b/packages/integrations/contentful-cms/package.json new file mode 100644 index 00000000..755fc335 --- /dev/null +++ b/packages/integrations/contentful-cms/package.json @@ -0,0 +1,41 @@ +{ + "name": "@o2s/integrations.contentful-cms", + "version": "0.1.0", + "private": false, + "license": "MIT", + "exports": { + "./integration": "./dist/src/integration.js" + }, + "files": [ + "dist" + ], + "scripts": { + "dev": "tsc && (concurrently \"tsc -w\" \"tsc-alias -w\")", + "build": "tsc && tsc-alias", + "lint": "tsc --noEmit && eslint . --max-warnings 0", + "format": "prettier --write \"src/**/*.{js,jsx,ts,tsx,css,scss,json}\"", + "generate": "contentful-typescript-codegen --output generated/contentful.d.ts && prettier --write \"generated/**/*.ts\"" + }, + "dependencies": { + "@nestjs/axios": "^3.1.2", + "@nestjs/common": "^10.0.0", + "@nestjs/config": "^3.3.0", + "@nestjs/core": "^10.0.0", + "@o2s/framework": "*", + "@o2s/utils.logger": "*", + "contentful": "^9.3.5", + "contentful-management": "^10.36.0", + "contentful-typescript-codegen": "^3.4.0", + "rxjs": "^7.8.1" + }, + "devDependencies": { + "@o2s/eslint-config": "*", + "@o2s/prettier-config": "*", + "@o2s/typescript-config": "*", + "concurrently": "^9.1.2", + "eslint": "^8.57.0", + "prettier": "^3.5.3", + "tsc-alias": "^1.8.13", + "typescript": "^5.8.2" + } +} \ No newline at end of file diff --git a/packages/integrations/contentful-cms/src/integration.ts b/packages/integrations/contentful-cms/src/integration.ts new file mode 100644 index 00000000..ec0e74f8 --- /dev/null +++ b/packages/integrations/contentful-cms/src/integration.ts @@ -0,0 +1,13 @@ +import { ApiConfig, Cache } from '@o2s/framework/modules'; + +import { Service as CmsService } from './modules/cms'; +import { ContentfulModule } from './modules/contentful/contentful.module'; + +export * as Integration from './modules/index'; + +export const Config: Partial = { + cms: { + service: CmsService, + imports: [ContentfulModule, Cache.Module], + }, +}; diff --git a/packages/integrations/contentful-cms/src/modules/cms/cms.service.ts b/packages/integrations/contentful-cms/src/modules/cms/cms.service.ts new file mode 100644 index 00000000..e9e687c2 --- /dev/null +++ b/packages/integrations/contentful-cms/src/modules/cms/cms.service.ts @@ -0,0 +1,230 @@ +import { Injectable, NotFoundException } from '@nestjs/common'; +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore module type mismatch +import { parse, stringify } from 'flatted'; +import { Observable, forkJoin, from, map, mergeMap, of } from 'rxjs'; + +import { CMS, Cache } from '@o2s/framework/modules'; + +import { ContentfulService } from '../contentful/contentful.service'; + +import { mapArticleDetailsBlock } from './mappers/blocks/cms.article-details.mapper'; +import { mapArticleListBlock } from './mappers/blocks/cms.article-list.mapper'; +import { mapFaqBlock } from './mappers/blocks/cms.faq.mapper'; +import { mapInvoiceDetailsBlock } from './mappers/blocks/cms.invoice-details.mapper'; +import { mapInvoiceListBlock } from './mappers/blocks/cms.invoice-list.mapper'; +import { mapNotificationDetailsBlock } from './mappers/blocks/cms.notification-details.mapper'; +import { mapNotificationListBlock } from './mappers/blocks/cms.notification-list.mapper'; +import { mapPaymentsHistoryBlock } from './mappers/blocks/cms.payments-history.mapper'; +import { mapPaymentsSummaryBlock } from './mappers/blocks/cms.payments-summary.mapper'; +import { mapResourceDetailsBlock } from './mappers/blocks/cms.resource-details.mapper'; +import { mapResourceListBlock } from './mappers/blocks/cms.resource-list.mapper'; +import { mapServiceDetailsBlock } from './mappers/blocks/cms.service-details.mapper'; +import { mapServiceListBlock } from './mappers/blocks/cms.service-list.mapper'; +import { mapTicketDetailsBlock } from './mappers/blocks/cms.ticket-details.mapper'; +import { mapTicketListBlock } from './mappers/blocks/cms.ticket-list.mapper'; +import { mapTicketRecentBlock } from './mappers/blocks/cms.ticket-recent.mapper'; +import { mapUserAccountBlock } from './mappers/blocks/cms.user-account.mapper'; +import { mapAppConfig } from './mappers/cms.app-config.mapper'; +import { mapFooter } from './mappers/cms.footer.mapper'; +import { mapHeader } from './mappers/cms.header.mapper'; +import { mapLoginPage } from './mappers/cms.login-page.mapper'; +import { mapNotFoundPage } from './mappers/cms.not-found-page.mapper'; +import { getAllPages, getAlternativePages, mapMockPage, mapPage } from './mappers/cms.page.mapper'; +import { IEntry, IPageFields } from '@/generated/contentful'; + +@Injectable() +export class CmsService implements CMS.Service { + constructor( + private readonly cms: ContentfulService, + private readonly cacheService: Cache.Service, + ) {} + + private getBlock = (options: CMS.Request.GetCmsEntryParams) => { + const key = `component-${options.id}-${options.locale}`; + + return from(this.cacheService.get(key)).pipe( + mergeMap((cachedBlock) => { + if (cachedBlock) { + return of(parse(cachedBlock)); + } + + const component = this.cms.getEntry(options.id, { + locale: options.locale, + include: 5, + }); + + return forkJoin([component]).pipe( + map(([component]) => { + if (!component?.fields) { + throw new NotFoundException(); + } + + const data = component; + this.cacheService.set(key, stringify(data)); + return data; + }), + ); + }), + ); + }; + + private getCachedBlock(key: string, getData: () => Observable): Observable { + return from(this.cacheService.get(key)).pipe( + mergeMap((cachedData) => { + if (cachedData) { + console.log('cachedData', cachedData); + return of(parse(cachedData)); + } + return getData().pipe( + map((data) => { + this.cacheService.set(key, stringify(data)); + return data; + }), + ); + }), + ); + } + + getEntry(_options: CMS.Request.GetCmsEntryParams) { + return of({} as T); + } + + getEntries(_options: CMS.Request.GetCmsEntriesParams) { + return of({} as T); + } + + getAppConfig(options: CMS.Request.GetCmsAppConfigParams) { + return of(mapAppConfig(options.referrer, options.locale)); + } + + getPage(options: CMS.Request.GetCmsPageParams) { + const key = `page-${options.slug}-${options.locale}`; + + // TODO: remove this once we have a full implementation of the page mapper + if (options.slug !== '/cases') { + return of(mapMockPage(options.slug, options.locale)); + } + + return this.getCachedBlock(key, () => { + const pages = this.cms.findEntries({ + locale: options.locale, + content_type: 'page', + include: 5, + }); + + return forkJoin([pages]).pipe( + map(([pages]) => { + if (!pages?.items?.length) { + throw new NotFoundException(); + } + + const page = pages.items.find((page) => { + const pattern = new RegExp(`^${page.fields.slug}$`, 'i'); + return pattern.test(options.slug); + }); + + if (!page) { + throw new NotFoundException(); + } + + return mapPage(page); + }), + ); + }); + } + + getPages(options: CMS.Request.GetCmsPagesParams) { + return of(getAllPages(options.locale)); + } + + getAlternativePages(options: CMS.Request.GetCmsAlternativePagesParams) { + return of(getAlternativePages(options.id, options.slug, options.locale)); + } + + getLoginPage(options: CMS.Request.GetCmsLoginPageParams) { + return of(mapLoginPage(options.locale)); + } + + getNotFoundPage(options: CMS.Request.GetCmsNotFoundPageParams) { + return of(mapNotFoundPage(options.locale)); + } + + getHeader(options: CMS.Request.GetCmsHeaderParams) { + return of(mapHeader(options.id, options.locale)); + } + + getFooter(options: CMS.Request.GetCmsFooterParams) { + return of(mapFooter(options.locale)); + } + + getFaqBlock(options: CMS.Request.GetCmsEntryParams) { + const key = `faq-component-${options.id}-${options.locale}`; + return this.getCachedBlock(key, () => this.getBlock(options).pipe(map(mapFaqBlock))); + } + + getTicketListBlock(options: CMS.Request.GetCmsEntryParams) { + const key = `ticket-list-component-${options.id}-${options.locale}`; + return this.getCachedBlock(key, () => this.getBlock(options).pipe(map(mapTicketListBlock))); + } + + getTicketDetailsBlock(_options: CMS.Request.GetCmsEntryParams) { + return of(mapTicketDetailsBlock(_options.locale)); + } + + getNotificationListBlock(options: CMS.Request.GetCmsEntryParams) { + return of(mapNotificationListBlock(options.locale)); + } + + getNotificationDetailsBlock(_options: CMS.Request.GetCmsEntryParams) { + return of(mapNotificationDetailsBlock(_options.locale)); + } + + getArticleListBlock(options: CMS.Request.GetCmsEntryParams) { + return of(mapArticleListBlock(options.locale)); + } + + getArticleDetailsBlock(_options: CMS.Request.GetCmsEntryParams) { + return of(mapArticleDetailsBlock()); + } + + getInvoiceListBlock(options: CMS.Request.GetCmsEntryParams) { + return of(mapInvoiceListBlock(options.locale)); + } + + getInvoiceDetailsBlock(_options: CMS.Request.GetCmsEntryParams) { + return of(mapInvoiceDetailsBlock()); + } + + getServiceListBlock(options: CMS.Request.GetCmsEntryParams) { + return of(mapServiceListBlock(options.locale)); + } + + getServiceDetailsBlock(options: CMS.Request.GetCmsEntryParams) { + return of(mapServiceDetailsBlock(options.locale)); + } + + getResourceListBlock(options: CMS.Request.GetCmsEntryParams) { + return of(mapResourceListBlock(options.locale)); + } + + getResourceDetailsBlock(_options: CMS.Request.GetCmsEntryParams) { + return of(mapResourceDetailsBlock()); + } + + getPaymentsSummaryBlock(options: CMS.Request.GetCmsEntryParams) { + return of(mapPaymentsSummaryBlock(options.locale)); + } + + getPaymentsHistoryBlock(options: CMS.Request.GetCmsEntryParams) { + return of(mapPaymentsHistoryBlock(options.locale)); + } + + getUserAccountBlock(_options: CMS.Request.GetCmsEntryParams) { + return of(mapUserAccountBlock(_options.locale)); + } + + getTicketRecentBlock(options: CMS.Request.GetCmsEntryParams) { + return of(mapTicketRecentBlock(options.locale)); + } +} diff --git a/packages/integrations/contentful-cms/src/modules/cms/index.ts b/packages/integrations/contentful-cms/src/modules/cms/index.ts new file mode 100644 index 00000000..c88862c3 --- /dev/null +++ b/packages/integrations/contentful-cms/src/modules/cms/index.ts @@ -0,0 +1,6 @@ +import { CMS } from '@o2s/framework/modules'; + +export { CmsService as Service } from './cms.service'; + +export import Request = CMS.Request; +export import Model = CMS.Model; diff --git a/packages/integrations/contentful-cms/src/modules/cms/mappers/blocks/cms.article-details.mapper.ts b/packages/integrations/contentful-cms/src/modules/cms/mappers/blocks/cms.article-details.mapper.ts new file mode 100644 index 00000000..223d38d4 --- /dev/null +++ b/packages/integrations/contentful-cms/src/modules/cms/mappers/blocks/cms.article-details.mapper.ts @@ -0,0 +1,25 @@ +import { CMS } from '@o2s/framework/modules'; + +const MOCK_ARTICLE_DETAILS_BLOCK: CMS.Model.ArticleDetailsBlock.ArticleDetailsBlock = { + id: 'article-details-1', + properties: { + id: 'ID', + title: 'Title', + lead: 'Lead', + createdAt: 'Created At', + updatedAt: 'Updated At', + image: 'Image', + thumbnail: 'Thumbnail', + sections: 'Sections', + }, + labels: { + today: 'Today', + yesterday: 'Yesterday', + }, +}; + +export const mapArticleDetailsBlock = (): CMS.Model.ArticleDetailsBlock.ArticleDetailsBlock => { + return { + ...MOCK_ARTICLE_DETAILS_BLOCK, + }; +}; diff --git a/packages/integrations/contentful-cms/src/modules/cms/mappers/blocks/cms.article-list.mapper.ts b/packages/integrations/contentful-cms/src/modules/cms/mappers/blocks/cms.article-list.mapper.ts new file mode 100644 index 00000000..796fab0f --- /dev/null +++ b/packages/integrations/contentful-cms/src/modules/cms/mappers/blocks/cms.article-list.mapper.ts @@ -0,0 +1,233 @@ +import { CMS } from '@o2s/framework/modules'; + +const MOCK_ARTICLE_LIST_BLOCK_EN: CMS.Model.ArticleListBlock.ArticleListBlock = { + id: 'article-list-1', + title: 'Articles Overview', + subtitle: 'Your recent articles', + table: { + columns: [ + { id: 'title', title: 'Title' }, + { id: 'lead', title: 'Lead' }, + { id: 'createdAt', title: 'Date Created' }, + { id: 'updatedAt', title: 'Date Updated' }, + ], + actions: { + title: 'Actions', + label: 'View details', + }, + }, + pagination: { + limit: 5, + legend: 'of {totalPages} pages', + prev: 'Previous', + next: 'Next', + selectPage: 'Select page', + }, + filters: { + label: 'Filter', + title: 'Filter articles', + description: 'Use filters to find specific articles', + submit: 'Apply filters', + reset: 'Reset Filters', + close: 'Close filters', + items: [ + { + __typename: 'FilterDateRange', + id: 'createdAt', + label: 'Date Created', + from: { + label: 'From', + }, + to: { + label: 'To', + }, + }, + ], + }, + noResults: { + title: 'No articles found', + description: 'There are no articles matching your criteria', + }, + labels: { + today: 'Today', + yesterday: 'Yesterday', + }, + detailsUrl: '/articles/:id', + fieldMapping: { + category: { + SELF_SERVICE: 'Self Service', + WARRANTY_REPAIR: 'Warranty Repair', + TOOL_MANAGEMENT: 'Tool Management', + HOW_TO_GUIDES: 'How To Guides', + MAINTENANCE_TIPS: 'Maintenance Tips', + GENERAL: 'General', + OTHER: 'Other', + }, + }, +}; + +const MOCK_ARTICLE_LIST_BLOCK_DE: CMS.Model.ArticleListBlock.ArticleListBlock = { + id: 'article-list-1', + title: 'Artikelübersicht', + subtitle: 'Ihre neuesten Artikel', + table: { + columns: [ + { id: 'title', title: 'Titel' }, + { id: 'lead', title: 'Einleitung' }, + { id: 'createdAt', title: 'Erstellungsdatum' }, + { id: 'updatedAt', title: 'Aktualisierungsdatum' }, + ], + actions: { + title: 'Aktionen', + label: 'Details anzeigen', + }, + }, + pagination: { + limit: 5, + legend: 'von {totalPages} Seiten', + prev: 'Zurück', + next: 'Weiter', + selectPage: 'Seite auswählen', + }, + filters: { + label: 'Filter', + title: 'Artikel filtern', + description: 'Verwenden Sie Filter, um bestimmte Artikel zu finden', + submit: 'Filter anwenden', + reset: 'Filter zurücksetzen', + close: 'Filter schließen', + items: [ + { + __typename: 'FilterDateRange', + id: 'createdAt', + label: 'Erstellungsdatum', + from: { + label: 'Von', + }, + to: { + label: 'Bis', + }, + }, + ], + }, + noResults: { + title: 'Keine Artikel gefunden', + description: 'Es gibt keine Artikel, die Ihren Kriterien entsprechen', + }, + labels: { + today: 'Heute', + yesterday: 'Gestern', + }, + detailsUrl: '/artikel/:id', + fieldMapping: { + category: { + SELF_SERVICE: 'Self Service', + WARRANTY_REPAIR: 'Garantiereparatur', + TOOL_MANAGEMENT: 'Werkzeugverwaltung', + HOW_TO_GUIDES: 'Anleitungen', + MAINTENANCE_TIPS: 'Wartungstipps', + GENERAL: 'Allgemein', + OTHER: 'Sonstiges', + }, + }, +}; + +const MOCK_ARTICLE_LIST_BLOCK_PL: CMS.Model.ArticleListBlock.ArticleListBlock = { + id: 'article-list-1', + title: 'Przegląd artykułów', + subtitle: 'Twoje ostatnie artykuły', + table: { + columns: [ + { id: 'title', title: 'Tytuł' }, + { id: 'lead', title: 'Wstęp' }, + { id: 'createdAt', title: 'Data utworzenia' }, + { id: 'updatedAt', title: 'Data aktualizacji' }, + ], + actions: { + title: 'Akcje', + label: 'Zobacz szczegóły', + }, + }, + pagination: { + limit: 5, + legend: 'of {totalPages} pages', + prev: 'Poprzedni', + next: 'Następny', + selectPage: 'Wybierz stronę', + }, + filters: { + label: 'Filtr', + title: 'Filtruj artykuły', + description: 'Użyj filtrów, aby znaleźć konkretne artykuły', + submit: 'Zastosuj filtry', + reset: 'Resetuj filtry', + close: 'Zamknij filtry', + items: [ + { + __typename: 'FilterDateRange', + id: 'createdAt', + label: 'Data utworzenia', + from: { + label: 'Od', + }, + to: { + label: 'Do', + }, + }, + ], + }, + noResults: { + title: 'Nie znaleziono artykułów', + description: 'Nie ma artykułów spełniających Twoje kryteria', + }, + labels: { + today: 'Dzisiaj', + yesterday: 'Wczoraj', + }, + detailsUrl: '/artykuły/:id', + fieldMapping: { + category: { + SELF_SERVICE: 'Self Service', + WARRANTY_REPAIR: 'Naprawa gwarancyjna', + TOOL_MANAGEMENT: 'Zarządzanie narzędziami', + HOW_TO_GUIDES: 'Przewodniki', + MAINTENANCE_TIPS: 'Porady konserwacyjne', + GENERAL: 'Ogólne', + OTHER: 'Inne', + }, + }, +}; + +export const mapArticleListBlock = (locale: string): CMS.Model.ArticleListBlock.ArticleListBlock => { + const getDetailsUrl = () => { + switch (locale) { + case 'en': + return `/articles/{id}`; + case 'de': + return `/artikel/{id}`; + case 'pl': + return `/artykuły/{id}`; + } + + return ''; + }; + + switch (locale) { + case 'de': + return { + ...MOCK_ARTICLE_LIST_BLOCK_DE, + detailsUrl: getDetailsUrl(), + }; + case 'pl': + return { + ...MOCK_ARTICLE_LIST_BLOCK_PL, + detailsUrl: getDetailsUrl(), + }; + case 'en': + default: + return { + ...MOCK_ARTICLE_LIST_BLOCK_EN, + detailsUrl: getDetailsUrl(), + }; + } +}; diff --git a/packages/integrations/contentful-cms/src/modules/cms/mappers/blocks/cms.faq.mapper.ts b/packages/integrations/contentful-cms/src/modules/cms/mappers/blocks/cms.faq.mapper.ts new file mode 100644 index 00000000..219dd8e4 --- /dev/null +++ b/packages/integrations/contentful-cms/src/modules/cms/mappers/blocks/cms.faq.mapper.ts @@ -0,0 +1,35 @@ +import { NotFoundException } from '@nestjs/common'; +import { Entry } from 'contentful'; + +import { CMS } from '@o2s/framework/modules'; + +import { IBlockFaqFields } from '@/generated/contentful'; + +export const mapFaqBlock = (data: Entry): CMS.Model.FaqBlock.FaqBlock => { + if (!data) { + throw new NotFoundException(); + } + + switch (data.sys.contentType.sys.id) { + case 'blockFaq': + return { + id: data.sys.id, + title: data.fields.title, + subtitle: data.fields.subtitle, + items: data.fields.items?.map( + (item): CMS.Model.FaqBlock.FaqItem => ({ + title: item.fields.title, + content: item.fields.content, + }), + ), + banner: { + title: data.fields.banner?.fields.title, + description: data.fields.banner?.fields.description, + button: data.fields.banner?.fields.link?.fields, + } as CMS.Model.FaqBlock.FaqBoxWithButton, + }; + } + // return MOCK_FAQ_LIST_BLOCK_EN; + + throw new NotFoundException(); +}; diff --git a/packages/integrations/contentful-cms/src/modules/cms/mappers/blocks/cms.invoice-details.mapper.ts b/packages/integrations/contentful-cms/src/modules/cms/mappers/blocks/cms.invoice-details.mapper.ts new file mode 100644 index 00000000..07439013 --- /dev/null +++ b/packages/integrations/contentful-cms/src/modules/cms/mappers/blocks/cms.invoice-details.mapper.ts @@ -0,0 +1,50 @@ +import { CMS } from '@o2s/framework/modules'; + +const MOCK_INVOICE_DETAILS_BLOCK: CMS.Model.InvoiceDetailsBlock.InvoiceDetailsBlock = { + id: 'invoice-details-1', + fieldMapping: { + type: { + STANDARD: 'Standard Invoice', + PROFORMA: 'Proforma Invoice', + CREDIT_NOTE: 'Credit Note', + DEBIT_NOTE: 'Debit Note', + }, + paymentStatus: { + PAYMENT_COMPLETE: 'Paid', + PAYMENT_DECLINED: 'Declined', + PAYMENT_DUE: 'Due', + PAYMENT_PAST_DUE: 'Past Due', + }, + currency: { + PLN: 'Polish Zloty', + EUR: 'Euro', + USD: 'US Dollar', + GBP: 'British Pound', + }, + }, + properties: { + id: 'Invoice Number', + externalId: 'External Number', + billingAccountId: 'Billing Account Number', + billingPeriod: 'Billing Period', + paymentMethodId: 'Payment Method', + type: 'Invoice Type', + paymentStatus: 'Payment Status', + issuedDate: 'Issue Date', + paymentDueDate: 'Due Date', + currency: 'Currency', + totalAmountDue: 'Total Amount Due', + totalNetAmountDue: 'Net Amount', + totalAmountPaid: 'Amount Paid', + }, + labels: { + today: 'Today', + yesterday: 'Yesterday', + }, +}; + +export const mapInvoiceDetailsBlock = (): CMS.Model.InvoiceDetailsBlock.InvoiceDetailsBlock => { + return { + ...MOCK_INVOICE_DETAILS_BLOCK, + }; +}; diff --git a/packages/integrations/contentful-cms/src/modules/cms/mappers/blocks/cms.invoice-list.mapper.ts b/packages/integrations/contentful-cms/src/modules/cms/mappers/blocks/cms.invoice-list.mapper.ts new file mode 100644 index 00000000..17451d72 --- /dev/null +++ b/packages/integrations/contentful-cms/src/modules/cms/mappers/blocks/cms.invoice-list.mapper.ts @@ -0,0 +1,357 @@ +import { CMS } from '@o2s/framework/modules'; + +const MOCK_INVOICE_LIST_BLOCK_EN: CMS.Model.InvoiceListBlock.InvoiceListBlock = { + id: 'invoice-list-1', + title: 'Invoices', + fieldMapping: { + type: { + STANDARD: 'Standard', + PROFORMA: 'Proforma', + CREDIT_NOTE: 'Credit Note', + DEBIT_NOTE: 'Debit Note', + }, + paymentStatus: { + PAYMENT_COMPLETE: 'Paid', + PAYMENT_DECLINED: 'Declined', + PAYMENT_DUE: 'Due', + PAYMENT_PAST_DUE: 'Past Due', + }, + }, + tableTitle: 'List of your invoices', + table: { + columns: [ + { id: 'type', title: 'Invoice Type' }, + { id: 'id', title: 'Invoice Number' }, + { id: 'paymentDueDate', title: 'Due Date' }, + { id: 'paymentStatus', title: 'Payment Status' }, + { id: 'totalAmountDue', title: 'Total Amount Due' }, + { id: 'amountToPay', title: 'To be Paid' }, + ], + actions: { + title: 'Actions', + label: 'Download', + }, + }, + pagination: { + limit: 5, + legend: 'of {totalPages} pages', + prev: 'Previous', + next: 'Next', + selectPage: 'Select page', + }, + filters: { + label: 'Filter & Sort', + title: 'Filter Invoices', + description: 'Use filters to find specific invoices', + submit: 'Apply Filters', + reset: 'Reset Filters', + close: 'Close filters', + removeFilters: 'Remove filters ({active})', + items: [ + { + __typename: 'FilterSelect', + id: 'sort', + label: 'Sort', + allowMultiple: false, + options: [ + { label: 'Document Type (ascending)', value: 'type_ASC' }, + { label: 'Document Type (descending)', value: 'type_DESC' }, + { label: 'Payment Status (ascending)', value: 'paymentStatus_ASC' }, + { label: 'Payment Status (descending)', value: 'paymentStatus_DESC' }, + { label: 'Payment Due Date (ascending)', value: 'paymentDueDate_ASC' }, + { label: 'Payment Due Date (descending)', value: 'paymentDueDate_DESC' }, + { label: 'Total Amount Due (ascending)', value: 'totalAmountDue_ASC' }, + { label: 'Total Amount Due (descending)', value: 'totalAmountDue_DESC' }, + { label: 'To Be Paid (ascending)', value: 'totalToBePaid_ASC' }, + { label: 'To Be Paid (descending)', value: 'totalToBePaid_DESC' }, + ], + }, + { + __typename: 'FilterSelect', + id: 'type', + label: 'Invoice Type', + allowMultiple: true, + options: [ + { label: 'Standard', value: 'STANDARD' }, + { label: 'Proforma', value: 'PROFORMA' }, + { label: 'Credit Note', value: 'CREDIT_NOTE' }, + { label: 'Debit Note', value: 'DEBIT_NOTE' }, + ], + }, + { + __typename: 'FilterSelect', + id: 'paymentStatus', + label: 'Payment Status', + allowMultiple: true, + options: [ + { label: 'Paid', value: 'PAYMENT_COMPLETE' }, + { label: 'Declined', value: 'PAYMENT_DECLINED' }, + { label: 'Due', value: 'PAYMENT_DUE' }, + { label: 'Past Due', value: 'PAYMENT_PAST_DUE' }, + ], + }, + { + __typename: 'FilterDateRange', + id: 'issuedDate', + label: 'Issue Date', + from: { + label: 'From', + }, + to: { + label: 'To', + }, + }, + ], + }, + noResults: { + title: 'No Invoices Found', + description: 'There are no invoices matching your criteria', + }, + labels: { + today: 'Today', + yesterday: 'Yesterday', + }, + downloadFileName: 'invoice-{id}.pdf', + downloadButtonAriaDescription: 'Download invoice {id}', +}; + +const MOCK_INVOICE_LIST_BLOCK_DE: CMS.Model.InvoiceListBlock.InvoiceListBlock = { + id: 'invoice-list-1', + title: 'Rechnungen', + fieldMapping: { + type: { + STANDARD: 'Standard', + PROFORMA: 'Proforma', + CREDIT_NOTE: 'Gutschrift', + DEBIT_NOTE: 'Lastschrift', + }, + paymentStatus: { + PAYMENT_COMPLETE: 'Bezahlt', + PAYMENT_DECLINED: 'Abgelehnt', + PAYMENT_DUE: 'Fällig', + PAYMENT_PAST_DUE: 'Überfällig', + }, + }, + tableTitle: 'Liste Ihrer Rechnungen', + table: { + columns: [ + { id: 'type', title: 'Rechnungstyp' }, + { id: 'id', title: 'Rechnungsnummer' }, + { id: 'paymentDueDate', title: 'Fälligkeitsdatum' }, + { id: 'paymentStatus', title: 'Zahlungsstatus' }, + { id: 'totalAmountDue', title: 'Gesamtbetrag' }, + { id: 'amountToPay', title: 'Zu zahlen' }, + ], + actions: { + title: 'Aktionen', + label: 'Herunterladen', + }, + }, + pagination: { + limit: 5, + legend: 'von {totalPages} Seiten', + prev: 'Zurück', + next: 'Weiter', + selectPage: 'Seite auswählen', + }, + filters: { + label: 'Filtern & Sortieren', + title: 'Rechnungen filtern', + description: 'Verwenden Sie Filter, um bestimmte Rechnungen zu finden', + submit: 'Filter anwenden', + reset: 'Filter zurücksetzen', + close: 'Filter schließen', + removeFilters: 'Filter entfernen ({active})', + items: [ + { + __typename: 'FilterSelect', + id: 'sort', + label: 'Sortieren', + allowMultiple: false, + options: [ + { label: 'Dokumenttyp (aufsteigend)', value: 'type_ASC' }, + { label: 'Dokumenttyp (absteigend)', value: 'type_DESC' }, + { label: 'Zahlungsstatus (aufsteigend)', value: 'paymentStatus_ASC' }, + { label: 'Zahlungsstatus (absteigend)', value: 'paymentStatus_DESC' }, + { label: 'Fälligkeitsdatum (aufsteigend)', value: 'paymentDueDate_ASC' }, + { label: 'Fälligkeitsdatum (absteigend)', value: 'paymentDueDate_DESC' }, + { label: 'Gesamtbetrag (aufsteigend)', value: 'totalAmountDue_ASC' }, + { label: 'Gesamtbetrag (absteigend)', value: 'totalAmountDue_DESC' }, + { label: 'Zu zahlen (aufsteigend)', value: 'amountToPay_ASC' }, + { label: 'Zu zahlen (absteigend)', value: 'amountToPay_DESC' }, + ], + }, + { + __typename: 'FilterSelect', + id: 'type', + label: 'Rechnungstyp', + allowMultiple: true, + options: [ + { label: 'Standard', value: 'STANDARD' }, + { label: 'Proforma', value: 'PROFORMA' }, + { label: 'Gutschrift', value: 'CREDIT_NOTE' }, + { label: 'Lastschrift', value: 'DEBIT_NOTE' }, + ], + }, + { + __typename: 'FilterSelect', + id: 'paymentStatus', + label: 'Zahlungsstatus', + allowMultiple: true, + options: [ + { label: 'Bezahlt', value: 'PAYMENT_COMPLETE' }, + { label: 'Abgelehnt', value: 'PAYMENT_DECLINED' }, + { label: 'Fällig', value: 'PAYMENT_DUE' }, + { label: 'Überfällig', value: 'PAYMENT_PAST_DUE' }, + ], + }, + { + __typename: 'FilterDateRange', + id: 'issuedDate', + label: 'Ausstellungsdatum', + from: { + label: 'Von', + }, + to: { + label: 'Bis', + }, + }, + ], + }, + noResults: { + title: 'Keine Rechnungen gefunden', + description: 'Es wurden keine Rechnungen gefunden, die Ihren Kriterien entsprechen', + }, + labels: { + today: 'Heute', + yesterday: 'Gestern', + }, + downloadFileName: 'rechnung-{id}.pdf', + downloadButtonAriaDescription: 'Rechnung {id} herunterladen', +}; + +const MOCK_INVOICE_LIST_BLOCK_PL: CMS.Model.InvoiceListBlock.InvoiceListBlock = { + id: 'invoice-list-1', + title: 'Faktury', + fieldMapping: { + type: { + STANDARD: 'Standardowa', + PROFORMA: 'Proforma', + CREDIT_NOTE: 'Nota kredytowa', + DEBIT_NOTE: 'Nota debetowa', + }, + paymentStatus: { + PAYMENT_COMPLETE: 'Opłacona', + PAYMENT_DECLINED: 'Odrzucona', + PAYMENT_DUE: 'Do zapłaty', + PAYMENT_PAST_DUE: 'Zaległa', + }, + }, + tableTitle: 'Lista Twoich faktur', + table: { + columns: [ + { id: 'type', title: 'Typ faktury' }, + { id: 'id', title: 'Numer faktury' }, + { id: 'paymentDueDate', title: 'Termin płatności' }, + { id: 'paymentStatus', title: 'Status płatności' }, + { id: 'totalAmountDue', title: 'Kwota do zapłaty' }, + { id: 'amountToPay', title: 'Do zapłacenia' }, + ], + actions: { + title: 'Akcje', + label: 'Pobierz', + }, + }, + pagination: { + limit: 5, + legend: 'z {totalPages} stron', + prev: 'Poprzednia', + next: 'Następna', + selectPage: 'Wybierz stronę', + }, + filters: { + label: 'Filtruj i sortuj', + title: 'Filtruj faktury', + description: 'Użyj filtrów, aby znaleźć konkretne faktury', + submit: 'Zastosuj filtry', + reset: 'Resetuj filtry', + close: 'Zamknij filtry', + removeFilters: 'Usuń filtry ({active})', + items: [ + { + __typename: 'FilterSelect', + id: 'sort', + label: 'Sortuj', + allowMultiple: false, + options: [ + { label: 'Typ dokumentu (rosnąco)', value: 'type_ASC' }, + { label: 'Typ dokumentu (malejąco)', value: 'type_DESC' }, + { label: 'Status płatności (rosnąco)', value: 'paymentStatus_ASC' }, + { label: 'Status płatności (malejąco)', value: 'paymentStatus_DESC' }, + { label: 'Termin płatności (rosnąco)', value: 'paymentDueDate_ASC' }, + { label: 'Termin płatności (malejąco)', value: 'paymentDueDate_DESC' }, + { label: 'Kwota do zapłaty (rosnąco)', value: 'totalAmountDue_ASC' }, + { label: 'Kwota do zapłaty (malejąco)', value: 'totalAmountDue_DESC' }, + { label: 'Do zapłacenia (rosnąco)', value: 'amountToPay_ASC' }, + { label: 'Do zapłacenia (malejąco)', value: 'amountToPay_DESC' }, + ], + }, + { + __typename: 'FilterSelect', + id: 'type', + label: 'Typ faktury', + allowMultiple: true, + options: [ + { label: 'Standardowa', value: 'STANDARD' }, + { label: 'Proforma', value: 'PROFORMA' }, + { label: 'Nota kredytowa', value: 'CREDIT_NOTE' }, + { label: 'Nota debetowa', value: 'DEBIT_NOTE' }, + ], + }, + { + __typename: 'FilterSelect', + id: 'paymentStatus', + label: 'Status płatności', + allowMultiple: true, + options: [ + { label: 'Opłacona', value: 'PAYMENT_COMPLETE' }, + { label: 'Odrzucona', value: 'PAYMENT_DECLINED' }, + { label: 'Do zapłaty', value: 'PAYMENT_DUE' }, + { label: 'Zaległa', value: 'PAYMENT_PAST_DUE' }, + ], + }, + { + __typename: 'FilterDateRange', + id: 'issuedDate', + label: 'Data wystawienia', + from: { + label: 'Od', + }, + to: { + label: 'Do', + }, + }, + ], + }, + noResults: { + title: 'Nie znaleziono faktur', + description: 'Nie znaleziono faktur spełniających podane kryteria', + }, + labels: { + today: 'Dzisiaj', + yesterday: 'Wczoraj', + }, + downloadFileName: 'faktura-{id}.pdf', + downloadButtonAriaDescription: 'Pobierz fakturę {id}', +}; + +export const mapInvoiceListBlock = (locale: string): CMS.Model.InvoiceListBlock.InvoiceListBlock => { + switch (locale) { + case 'de': + return MOCK_INVOICE_LIST_BLOCK_DE; + case 'pl': + return MOCK_INVOICE_LIST_BLOCK_PL; + default: + return MOCK_INVOICE_LIST_BLOCK_EN; + } +}; diff --git a/packages/integrations/contentful-cms/src/modules/cms/mappers/blocks/cms.notification-details.mapper.ts b/packages/integrations/contentful-cms/src/modules/cms/mappers/blocks/cms.notification-details.mapper.ts new file mode 100644 index 00000000..6b18feeb --- /dev/null +++ b/packages/integrations/contentful-cms/src/modules/cms/mappers/blocks/cms.notification-details.mapper.ts @@ -0,0 +1,128 @@ +import { CMS } from '@o2s/framework/modules'; + +const MOCK_NOTIFICATION_DETAILS_BLOCK_EN: CMS.Model.NotificationDetailsBlock.NotificationDetailsBlock = { + id: 'notification-list-1', + fieldMapping: { + type: { + GENERAL_NOTIFICATION: 'General notification', + TICKET_UPDATE: 'Ticket update', + TYPE_1: 'Special offer', + TYPE_2: 'Changes', + TYPE_3: 'Important news', + }, + status: { + UNVIEWED: 'Not viewed', + VIEWED: 'Viewed', + READ: 'Read', + }, + priority: { + low: 'Low Priority', + medium: 'Medium Priority', + high: 'High Priority', + critical: 'Critical Priority', + }, + }, + properties: { + id: 'ID', + title: 'Title', + content: 'Content', + type: 'Type', + status: 'Status', + priority: 'Priority', + createdAt: 'Created At', + updatedAt: 'Updated At', + }, + labels: { + today: 'Today', + yesterday: 'Yesterday', + }, +}; + +const MOCK_NOTIFICATION_DETAILS_BLOCK_DE: CMS.Model.NotificationDetailsBlock.NotificationDetailsBlock = { + id: 'notification-list-1', + fieldMapping: { + type: { + GENERAL_NOTIFICATION: 'Allgemeine Benachrichtigung', + TICKET_UPDATE: 'Ticket-Aktualisierung', + TYPE_1: 'Sonderangebot', + TYPE_2: 'Änderungen', + TYPE_3: 'Wichtige Neuigkeiten', + }, + status: { + UNVIEWED: 'Nicht angesehen', + VIEWED: 'Angesehen', + READ: 'Gelesen', + }, + priority: { + low: 'Niedrige Priorität', + medium: 'Mittlere Priorität', + high: 'Hohe Priorität', + critical: 'Kritische Priorität', + }, + }, + properties: { + id: 'ID', + title: 'Titel', + content: 'Inhalt', + type: 'Typ', + status: 'Status', + priority: 'Priorität', + createdAt: 'Erstellt am', + updatedAt: 'Aktualisiert am', + }, + labels: { + today: 'Heute', + yesterday: 'Gestern', + }, +}; + +const MOCK_NOTIFICATION_DETAILS_BLOCK_PL: CMS.Model.NotificationDetailsBlock.NotificationDetailsBlock = { + id: 'notification-list-1', + fieldMapping: { + type: { + GENERAL_NOTIFICATION: 'Ogólne powiadomienie', + TICKET_UPDATE: 'Aktualizacja biletu', + TYPE_1: 'Oferta specjalna', + TYPE_2: 'Zmiany', + TYPE_3: 'Ważne wiadomości', + }, + status: { + UNVIEWED: 'Nieprzeczytane', + VIEWED: 'Wyświetlone', + READ: 'Przeczytane', + }, + priority: { + low: 'Niski priorytet', + medium: 'Średni priorytet', + high: 'Wysoki priorytet', + critical: 'Krytyczny priorytet', + }, + }, + properties: { + id: 'ID', + title: 'Tytuł', + content: 'Treść', + type: 'Typ', + status: 'Status', + priority: 'Priorytet', + createdAt: 'Utworzono', + updatedAt: 'Zaktualizowano', + }, + labels: { + today: 'Dziś', + yesterday: 'Wczoraj', + }, +}; + +export const mapNotificationDetailsBlock = ( + locale: string, +): CMS.Model.NotificationDetailsBlock.NotificationDetailsBlock => { + switch (locale) { + case 'de': + return MOCK_NOTIFICATION_DETAILS_BLOCK_DE; + case 'pl': + return MOCK_NOTIFICATION_DETAILS_BLOCK_PL; + default: + return MOCK_NOTIFICATION_DETAILS_BLOCK_EN; + } +}; diff --git a/packages/integrations/contentful-cms/src/modules/cms/mappers/blocks/cms.notification-list.mapper.ts b/packages/integrations/contentful-cms/src/modules/cms/mappers/blocks/cms.notification-list.mapper.ts new file mode 100644 index 00000000..ba5ca1ad --- /dev/null +++ b/packages/integrations/contentful-cms/src/modules/cms/mappers/blocks/cms.notification-list.mapper.ts @@ -0,0 +1,396 @@ +import { CMS } from '@o2s/framework/modules'; + +const MOCK_NOTIFICATION_LIST_BLOCK_EN: CMS.Model.NotificationListBlock.NotificationListBlock = { + id: 'notification-list-1', + title: 'Notifications', + subtitle: 'List of your notifications', + table: { + columns: [ + { id: 'status', title: 'Status' }, + { id: 'title', title: 'Title' }, + { id: 'type', title: 'Type' }, + { id: 'priority', title: 'Priority' }, + { id: 'createdAt', title: 'Date' }, + ], + actions: { + title: 'Actions', + label: 'View details', + }, + }, + fieldMapping: { + type: { + GENERAL_NOTIFICATION: 'General', + TICKET_UPDATE: 'Ticket update', + TYPE_1: 'Special offer', + TYPE_2: 'Important news', + }, + status: { + UNVIEWED: 'Not viewed', + VIEWED: 'Viewed', + READ: 'Read', + }, + priority: { + LOW: 'Low', + MEDIUM: 'Medium', + HIGH: 'High', + CRITICAL: 'Critical', + }, + }, + pagination: { + limit: 5, + legend: 'of {totalPages} pages', + prev: 'Previous', + next: 'Next', + selectPage: 'Select page', + }, + filters: { + label: 'Filter & Sort', + title: 'Filter & Sort notifications', + description: 'Use filters to find specific notifications', + submit: 'Apply', + reset: 'Clear', + close: 'Close filters', + removeFilters: 'Remove filters ({active})', + items: [ + { + __typename: 'FilterSelect', + id: 'sort', + label: 'Sort', + allowMultiple: false, + options: [ + { label: 'Type (ascending)', value: 'type_ASC' }, + { label: 'Type (descending)', value: 'type_DESC' }, + { label: 'Status (ascending)', value: 'status_ASC' }, + { label: 'Status (descending)', value: 'status_DESC' }, + { label: 'Priority (ascending)', value: 'priority_ASC' }, + { label: 'Priority (descending)', value: 'priority_DESC' }, + { label: 'Date (ascending)', value: 'createdAt_ASC' }, + { label: 'Date (descending)', value: 'createdAt_DESC' }, + ], + }, + { + __typename: 'FilterSelect', + id: 'type', + label: 'Type', + allowMultiple: true, + options: [ + { label: 'General notification', value: 'GENERAL_NOTIFICATION' }, + { label: 'Ticket update', value: 'TICKET_UPDATE' }, + { label: 'Special offer', value: 'TYPE_1' }, + { label: 'Changes', value: 'TYPE_2' }, + { label: 'Important news', value: 'TYPE_3' }, + ], + }, + { + __typename: 'FilterSelect', + id: 'status', + label: 'Status', + allowMultiple: true, + options: [ + { label: 'Not viewed', value: 'UNVIEWED' }, + { label: 'Viewed', value: 'VIEWED' }, + { label: 'Read', value: 'READ' }, + ], + }, + { + __typename: 'FilterSelect', + id: 'priority', + label: 'Priority', + allowMultiple: true, + options: [ + { label: 'Low Priority', value: 'low' }, + { label: 'Medium Priority', value: 'medium' }, + { label: 'High Priority', value: 'high' }, + { label: 'Critical Priority', value: 'critical' }, + ], + }, + { + __typename: 'FilterDateRange', + id: 'createdAt', + label: 'Date', + from: { + label: 'From', + }, + to: { + label: 'To', + }, + }, + ], + }, + noResults: { + title: 'No notifications found', + description: 'There are no notifications matching your criteria', + }, + labels: { + today: 'Today', + yesterday: 'Yesterday', + }, + detailsUrl: '/notifications/:id', +}; + +const MOCK_NOTIFICATION_LIST_BLOCK_DE: CMS.Model.NotificationListBlock.NotificationListBlock = { + id: 'notification-list-1', + title: 'Benachrichtigungen', + subtitle: 'Liste Ihrer Benachrichtigungen', + table: { + columns: [ + { id: 'status', title: 'Status' }, + { id: 'title', title: 'Titel' }, + { id: 'type', title: 'Typ' }, + { id: 'priority', title: 'Priorität' }, + { id: 'createdAt', title: 'Datum' }, + ], + actions: { + title: 'Aktionen', + label: 'Details anzeigen', + }, + }, + fieldMapping: { + type: { + GENERAL_NOTIFICATION: 'Allgemein', + TICKET_UPDATE: 'Ticket-Aktualisierung', + TYPE_1: 'Sonderangebot', + TYPE_2: 'Wichtige Neuigkeiten', + }, + status: { + UNVIEWED: 'Nicht angesehen', + VIEWED: 'Angesehen', + READ: 'Gelesen', + }, + priority: { + LOW: 'Niedrig', + MEDIUM: 'Mittel', + HIGH: 'Hoch', + CRITICAL: 'Kritisch', + }, + }, + pagination: { + limit: 5, + legend: 'von {totalPages} Seiten', + prev: 'Zurück', + next: 'Weiter', + selectPage: 'Seite auswählen', + }, + filters: { + label: 'Filtern & Sortieren', + title: 'Benachrichtigungen filtern & sortieren', + description: 'Verwenden Sie Filter, um bestimmte Benachrichtigungen zu finden', + submit: 'Anwenden', + reset: 'Zurücksetzen', + close: 'Filter schließen', + removeFilters: 'Filter entfernen ({active})', + items: [ + { + __typename: 'FilterSelect', + id: 'sort', + label: 'Sortieren', + allowMultiple: false, + options: [ + { label: 'Typ (aufsteigend)', value: 'type_ASC' }, + { label: 'Typ (absteigend)', value: 'type_DESC' }, + { label: 'Status (aufsteigend)', value: 'status_ASC' }, + { label: 'Status (absteigend)', value: 'status_DESC' }, + { label: 'Priorität (aufsteigend)', value: 'priority_ASC' }, + { label: 'Priorität (absteigend)', value: 'priority_DESC' }, + { label: 'Datum (aufsteigend)', value: 'createdAt_ASC' }, + { label: 'Datum (absteigend)', value: 'createdAt_DESC' }, + ], + }, + { + __typename: 'FilterSelect', + id: 'type', + label: 'Typ', + allowMultiple: true, + options: [ + { label: 'Allgemeine Benachrichtigung', value: 'GENERAL_NOTIFICATION' }, + { label: 'Ticket-Aktualisierung', value: 'TICKET_UPDATE' }, + { label: 'Sonderangebot', value: 'TYPE_1' }, + { label: 'Änderungen', value: 'TYPE_2' }, + { label: 'Wichtige Neuigkeiten', value: 'TYPE_3' }, + ], + }, + { + __typename: 'FilterSelect', + id: 'status', + label: 'Status', + allowMultiple: true, + options: [ + { label: 'Nicht angesehen', value: 'UNVIEWED' }, + { label: 'Angesehen', value: 'VIEWED' }, + { label: 'Gelesen', value: 'READ' }, + ], + }, + { + __typename: 'FilterSelect', + id: 'priority', + label: 'Priorität', + allowMultiple: true, + options: [ + { label: 'Niedrige Priorität', value: 'low' }, + { label: 'Mittlere Priorität', value: 'medium' }, + { label: 'Hohe Priorität', value: 'high' }, + { label: 'Kritische Priorität', value: 'critical' }, + ], + }, + { + __typename: 'FilterDateRange', + id: 'createdAt', + label: 'Datum', + from: { + label: 'Von', + }, + to: { + label: 'Bis', + }, + }, + ], + }, + noResults: { + title: 'Keine Benachrichtigungen gefunden', + description: 'Es gibt keine Benachrichtigungen, die Ihren Kriterien entsprechen', + }, + labels: { + today: 'Heute', + yesterday: 'Gestern', + }, + detailsUrl: '/benachrichtigungen/:id', +}; + +const MOCK_NOTIFICATION_LIST_BLOCK_PL: CMS.Model.NotificationListBlock.NotificationListBlock = { + id: 'notification-list-1', + title: 'Powiadomienia', + subtitle: 'Lista twoich powiadomień', + table: { + columns: [ + { id: 'status', title: 'Status' }, + { id: 'title', title: 'Tytuł' }, + { id: 'type', title: 'Typ' }, + { id: 'priority', title: 'Priorytet' }, + { id: 'createdAt', title: 'Data' }, + ], + actions: { + title: 'Akcje', + label: 'Zobacz szczegóły', + }, + }, + fieldMapping: { + type: { + GENERAL_NOTIFICATION: 'Ogólne', + TICKET_UPDATE: 'Aktualizacja zgłoszenia', + TYPE_1: 'Oferta specjalna', + TYPE_2: 'Ważne wiadomości', + }, + status: { + UNVIEWED: 'Nieprzeczytane', + VIEWED: 'Wyświetlone', + READ: 'Przeczytane', + }, + priority: { + LOW: 'Niski', + MEDIUM: 'Średni', + HIGH: 'Wysoki', + CRITICAL: 'Krytyczny', + }, + }, + pagination: { + limit: 5, + legend: 'z {totalPages} stron', + prev: 'Poprzednia', + next: 'Następna', + selectPage: 'Wybierz stronę', + }, + filters: { + label: 'Filtruj i sortuj', + title: 'Filtruj i sortuj powiadomienia', + description: 'Użyj filtrów, aby znaleźć konkretne powiadomienia', + submit: 'Zastosuj', + reset: 'Wyczyść', + close: 'Zamknij filtry', + removeFilters: 'Usuń filtry ({active})', + items: [ + { + __typename: 'FilterSelect', + id: 'sort', + label: 'Sortuj', + allowMultiple: false, + options: [ + { label: 'Typ (rosnąco)', value: 'type_ASC' }, + { label: 'Typ (malejąco)', value: 'type_DESC' }, + { label: 'Status (rosnąco)', value: 'status_ASC' }, + { label: 'Status (malejąco)', value: 'status_DESC' }, + { label: 'Priorytet (rosnąco)', value: 'priority_ASC' }, + { label: 'Priorytet (malejąco)', value: 'priority_DESC' }, + { label: 'Data (rosnąco)', value: 'createdAt_ASC' }, + { label: 'Data (malejąco)', value: 'createdAt_DESC' }, + ], + }, + { + __typename: 'FilterSelect', + id: 'type', + label: 'Typ', + allowMultiple: true, + options: [ + { label: 'Powiadomienie ogólne', value: 'GENERAL_NOTIFICATION' }, + { label: 'Aktualizacja zgłoszenia', value: 'TICKET_UPDATE' }, + { label: 'Oferta specjalna', value: 'TYPE_1' }, + { label: 'Zmiany', value: 'TYPE_2' }, + { label: 'Ważne wiadomości', value: 'TYPE_3' }, + ], + }, + { + __typename: 'FilterSelect', + id: 'status', + label: 'Status', + allowMultiple: true, + options: [ + { label: 'Nieprzeczytane', value: 'UNVIEWED' }, + { label: 'Wyświetlone', value: 'VIEWED' }, + { label: 'Przeczytane', value: 'READ' }, + ], + }, + { + __typename: 'FilterSelect', + id: 'priority', + label: 'Priorytet', + allowMultiple: true, + options: [ + { label: 'Niski priorytet', value: 'low' }, + { label: 'Średni priorytet', value: 'medium' }, + { label: 'Wysoki priorytet', value: 'high' }, + { label: 'Krytyczny priorytet', value: 'critical' }, + ], + }, + { + __typename: 'FilterDateRange', + id: 'createdAt', + label: 'Data', + from: { + label: 'Od', + }, + to: { + label: 'Do', + }, + }, + ], + }, + noResults: { + title: 'Nie znaleziono powiadomień', + description: 'Nie ma powiadomień spełniających Twoje kryteria', + }, + labels: { + today: 'Dzisiaj', + yesterday: 'Wczoraj', + }, + detailsUrl: '/powiadomienia/:id', +}; + +export const mapNotificationListBlock = (locale: string): CMS.Model.NotificationListBlock.NotificationListBlock => { + switch (locale) { + case 'de': + return { ...MOCK_NOTIFICATION_LIST_BLOCK_DE, detailsUrl: '/benachrichtigungen/{id}' }; + case 'pl': + return { ...MOCK_NOTIFICATION_LIST_BLOCK_PL, detailsUrl: '/powiadomienia/{id}' }; + default: + return { ...MOCK_NOTIFICATION_LIST_BLOCK_EN, detailsUrl: '/notifications/{id}' }; + } +}; diff --git a/packages/integrations/contentful-cms/src/modules/cms/mappers/blocks/cms.payments-history.mapper.ts b/packages/integrations/contentful-cms/src/modules/cms/mappers/blocks/cms.payments-history.mapper.ts new file mode 100644 index 00000000..150d18b1 --- /dev/null +++ b/packages/integrations/contentful-cms/src/modules/cms/mappers/blocks/cms.payments-history.mapper.ts @@ -0,0 +1,42 @@ +import { CMS } from '@o2s/framework/modules'; + +const MOCK_PAYMENTS_HISTORY_BLOCK_EN: CMS.Model.PaymentsHistoryBlock.PaymentsHistoryBlock = { + id: 'payments-history-1', + title: '6-months history', + topSegment: 'Overdue', + middleSegment: 'To be paid', + bottomSegment: 'Paid', + total: 'Total', + monthsToShow: 6, +}; + +const MOCK_PAYMENTS_HISTORY_BLOCK_DE: CMS.Model.PaymentsHistoryBlock.PaymentsHistoryBlock = { + id: 'payments-history-1', + title: '6-Monats-Historie', + topSegment: 'Überfällig', + middleSegment: 'Zu bezahlen', + bottomSegment: 'Bezahlt', + total: 'Gesamt', + monthsToShow: 6, +}; + +const MOCK_PAYMENTS_HISTORY_BLOCK_PL: CMS.Model.PaymentsHistoryBlock.PaymentsHistoryBlock = { + id: 'payments-history-1', + title: 'Historia 6-miesięczna', + topSegment: 'Zaległe', + middleSegment: 'Do zapłaty', + bottomSegment: 'Zapłacone', + total: 'Suma', + monthsToShow: 6, +}; + +export const mapPaymentsHistoryBlock = (locale: string): CMS.Model.PaymentsHistoryBlock.PaymentsHistoryBlock => { + switch (locale) { + case 'de': + return MOCK_PAYMENTS_HISTORY_BLOCK_DE; + case 'pl': + return MOCK_PAYMENTS_HISTORY_BLOCK_PL; + default: + return MOCK_PAYMENTS_HISTORY_BLOCK_EN; + } +}; diff --git a/packages/integrations/contentful-cms/src/modules/cms/mappers/blocks/cms.payments-summary.mapper.ts b/packages/integrations/contentful-cms/src/modules/cms/mappers/blocks/cms.payments-summary.mapper.ts new file mode 100644 index 00000000..59cf8d73 --- /dev/null +++ b/packages/integrations/contentful-cms/src/modules/cms/mappers/blocks/cms.payments-summary.mapper.ts @@ -0,0 +1,60 @@ +import { CMS } from '@o2s/framework/modules'; + +const MOCK_PAYMENTS_SUMMARY_BLOCK_EN: CMS.Model.PaymentsSummaryBlock.PaymentsSummaryBlock = { + id: 'payments-summary-1', + overdue: { + title: 'Overdue', + message: '{days} days overdue', + buttonLabel: 'Pay online', + noPaymentsMessage: 'No overdue invoices', + }, + toBePaid: { + title: 'To be paid', + message: 'New and overdue', + noPaymentsMessage: 'No invoices to be paid', + buttonLabel: 'Pay online', + }, +}; + +const MOCK_PAYMENTS_SUMMARY_BLOCK_PL: CMS.Model.PaymentsSummaryBlock.PaymentsSummaryBlock = { + id: 'payments-summary-1', + overdue: { + title: 'Zaległe', + message: '{days} dni po terminie', + buttonLabel: 'Zapłać online', + noPaymentsMessage: 'Brak zaległych faktur', + }, + toBePaid: { + title: 'Do zapłaty', + message: 'Nowe i zaległe', + noPaymentsMessage: 'Brak faktur do zapłaty', + buttonLabel: 'Zapłać online', + }, +}; + +const MOCK_PAYMENTS_SUMMARY_BLOCK_DE: CMS.Model.PaymentsSummaryBlock.PaymentsSummaryBlock = { + id: 'payments-summary-1', + overdue: { + title: 'Überfällig', + message: '{days} Tage überfällig', + buttonLabel: 'Online bezahlen', + noPaymentsMessage: 'Keine überfälligen Rechnungen', + }, + toBePaid: { + title: 'Zu bezahlen', + message: 'Neue und überfällige', + noPaymentsMessage: 'Keine zu zahlenden Rechnungen', + buttonLabel: 'Online bezahlen', + }, +}; + +export const mapPaymentsSummaryBlock = (locale: string): CMS.Model.PaymentsSummaryBlock.PaymentsSummaryBlock => { + switch (locale) { + case 'pl': + return MOCK_PAYMENTS_SUMMARY_BLOCK_PL; + case 'de': + return MOCK_PAYMENTS_SUMMARY_BLOCK_DE; + default: + return MOCK_PAYMENTS_SUMMARY_BLOCK_EN; + } +}; diff --git a/packages/integrations/contentful-cms/src/modules/cms/mappers/blocks/cms.resource-details.mapper.ts b/packages/integrations/contentful-cms/src/modules/cms/mappers/blocks/cms.resource-details.mapper.ts new file mode 100644 index 00000000..b9dca5f5 --- /dev/null +++ b/packages/integrations/contentful-cms/src/modules/cms/mappers/blocks/cms.resource-details.mapper.ts @@ -0,0 +1,47 @@ +import { CMS } from '@o2s/framework/modules'; + +const MOCK_RESOURCE_DETAILS_BLOCK: CMS.Model.ResourceDetailsBlock.ResourceDetailsBlock = { + id: 'resource-details-1', + fieldMapping: { + 'asset.status': { + ACTIVE: 'Active', + INACTIVE: 'Inactive', + RETIRED: 'Retired', + }, + 'contract.status': { + ACTIVE: 'Active', + EXPIRED: 'Expired', + INACTIVE: 'Inactive', + }, + // 'product.type': { + // PHYSICAL: 'Physical', + // VIRTUAL: 'Virtual', + // }, + }, + properties: { + id: 'Resource ID', + billingAccountId: 'Billing Account', + __typename: 'Resource Type', + 'product.type': 'Product Type', + 'product.name': 'Product Name', + 'product.category': 'Product Category', + 'asset.manufacturer': 'Manufacturer', + 'asset.model': 'Model', + 'asset.serialNo': 'Serial Number', + 'asset.status': 'Status', + 'contract.startDate': 'Start Date', + 'contract.endDate': 'End Date', + }, + labels: { + today: 'Today', + yesterday: 'Yesterday', + status: 'Status', + type: 'Type', + }, +}; + +export const mapResourceDetailsBlock = (): CMS.Model.ResourceDetailsBlock.ResourceDetailsBlock => { + return { + ...MOCK_RESOURCE_DETAILS_BLOCK, + }; +}; diff --git a/packages/integrations/contentful-cms/src/modules/cms/mappers/blocks/cms.resource-list.mapper.ts b/packages/integrations/contentful-cms/src/modules/cms/mappers/blocks/cms.resource-list.mapper.ts new file mode 100644 index 00000000..7223a358 --- /dev/null +++ b/packages/integrations/contentful-cms/src/modules/cms/mappers/blocks/cms.resource-list.mapper.ts @@ -0,0 +1,98 @@ +import { CMS } from '@o2s/framework/modules'; + +const MOCK_RESOURCE_LIST_BLOCK: CMS.Model.ResourceListBlock.ResourceListBlock = { + id: 'resource-list-1', + title: 'Resources', + subtitle: 'List of your resources', + table: { + columns: [ + { id: 'id', title: 'Resource ID' }, + { id: 'product.type', title: 'Product Type' }, + { id: 'product.name', title: 'Product Name' }, + { id: 'product.category', title: 'Product Category' }, + ], + actions: { + title: 'Actions', + label: 'View Details', + }, + }, + pagination: { + limit: 5, + legend: 'of {totalPages} pages', + prev: 'Previous', + next: 'Next', + selectPage: 'Select page', + }, + filters: { + label: 'Filter', + title: 'Filter Resources', + description: 'Use filters to find specific resources', + submit: 'Apply Filters', + reset: 'Reset Filters', + close: 'Close filters', + items: [ + { + __typename: 'FilterSelect', + id: '__typename', + label: 'Resource Type', + allowMultiple: true, + options: [ + { label: 'Asset', value: 'Asset' }, + { label: 'Service', value: 'Service' }, + ], + }, + { + __typename: 'FilterSelect', + id: 'product.type', + label: 'Product Type', + allowMultiple: true, + options: [ + { label: 'Physical', value: 'PHYSICAL' }, + { label: 'Virtual', value: 'VIRTUAL' }, + ], + }, + { + __typename: 'FilterSelect', + id: 'product.category', + label: 'Product Category', + allowMultiple: true, + options: [ + { label: 'Software', value: 'SOFTWARE' }, + { label: 'Tools', value: 'TOOLS' }, + { label: 'Hardware', value: 'HARDWARE' }, + ], + }, + ], + }, + noResults: { + title: 'No Resources Found', + description: 'There are no resources matching your criteria', + }, + labels: { + today: 'Today', + yesterday: 'Yesterday', + status: 'Status', + type: 'Type', + }, + detailsUrl: '/resources/:id', +}; + +export const mapResourceListBlock = (locale: string): CMS.Model.ResourceListBlock.ResourceListBlock => { + const getDetailsUrl = () => { + switch (locale) { + case 'en': + return `/resources/{id}`; + case 'de': + return `/ressourcen/{id}`; + case 'pl': + return `/zasoby/{id}`; + } + + return ''; + }; + + return { + ...MOCK_RESOURCE_LIST_BLOCK, + detailsUrl: getDetailsUrl(), + }; +}; diff --git a/packages/integrations/contentful-cms/src/modules/cms/mappers/blocks/cms.service-details.mapper.ts b/packages/integrations/contentful-cms/src/modules/cms/mappers/blocks/cms.service-details.mapper.ts new file mode 100644 index 00000000..242fd0ee --- /dev/null +++ b/packages/integrations/contentful-cms/src/modules/cms/mappers/blocks/cms.service-details.mapper.ts @@ -0,0 +1,138 @@ +import { CMS } from '@o2s/framework/modules'; + +const MOCK_SERVICE_DETAILS_BLOCK_EN: CMS.Model.ServiceDetailsBlock.ServiceDetailsBlock = { + id: 'service-list-1', + title: 'Service details', + properties: { + price: 'Price', + status: 'Status', + type: 'Type', + category: 'Category', + startDate: 'Start date', + endDate: 'End date', + }, + fields: { + type: { + PHYSICAL: 'Physical', + VIRTUAL: 'Virtual', + }, + category: { + TOOLS: 'Tools', + HARDWARE: 'Hardware', + SOFTWARE: 'Software', + MEASUREMENT: 'Measurement', + SAFETY: 'Safety', + TRAINING: 'Training', + RENTAL: 'Rental', + MAINTENANCE: 'Maintenance', + }, + status: { + ACTIVE: 'Active', + INACTIVE: 'Inactive', + EXPIRED: 'Expired', + }, + paymentPeriod: { + MONTHLY: 'mo', + YEARLY: 'ye', + }, + }, + labels: { + today: 'Today', + yesterday: 'Yesterday', + settings: 'Settings', + renew: 'Renew', + }, +}; + +const MOCK_SERVICE_DETAILS_BLOCK_PL: CMS.Model.ServiceDetailsBlock.ServiceDetailsBlock = { + id: 'service-list-1', + title: 'Szczegóły sprawy', + properties: { + id: 'ID sprawy', + topic: 'Temat', + type: 'Typ sprawy', + status: 'Status', + description: 'Dodatkowe notatki', + address: 'Adres serwisowy', + contact: 'Forma kontaktu', + }, + fields: { + type: { + PHYSICAL: 'Fizyczny', + VIRTUAL: 'Wirtualny', + }, + category: { + TOOLS: 'Narzędzia', + HARDWARE: 'Sprzęt', + SOFTWARE: 'Oprogramowanie', + MEASUREMENT: 'Pomiar', + }, + status: { + ACTIVE: 'Aktywny', + INACTIVE: 'Nieaktywny', + EXPIRED: 'Wygasły', + }, + paymentPeriod: { + MONTHLY: 'Miesięczny', + YEARLY: 'Roczny', + }, + }, + labels: { + today: 'Dzisiaj', + yesterday: 'Wczoraj', + settings: 'Ustawienia', + renew: 'Odnowić', + }, +}; + +const MOCK_SERVICE_DETAILS_BLOCK_DE: CMS.Model.ServiceDetailsBlock.ServiceDetailsBlock = { + id: 'service-list-1', + title: 'Falldetails', + properties: { + id: 'Fall-ID', + topic: 'Thema', + type: 'Falltyp', + status: 'Status', + description: 'Zusätzliche Notizen', + address: 'Serviceadresse', + contact: 'Kontaktform', + }, + fields: { + type: { + PHYSICAL: 'Physikalisch', + VIRTUAL: 'Virtuell', + }, + category: { + TOOLS: 'Werkzeuge', + HARDWARE: 'Hardware', + SOFTWARE: 'Software', + MEASUREMENT: 'Messung', + }, + status: { + ACTIVE: 'Aktiv', + INACTIVE: 'Inaktiv', + EXPIRED: 'Abgelaufen', + }, + paymentPeriod: { + MONTHLY: 'Monatlich', + YEARLY: 'Jährlich', + }, + }, + labels: { + today: 'Heute', + yesterday: 'Gestern', + settings: 'Einstellungen', + renew: 'Erneuern', + }, +}; + +export const mapServiceDetailsBlock = (_locale: string): CMS.Model.ServiceDetailsBlock.ServiceDetailsBlock => { + switch (_locale) { + case 'pl': + return MOCK_SERVICE_DETAILS_BLOCK_PL; + case 'de': + return MOCK_SERVICE_DETAILS_BLOCK_DE; + default: + return MOCK_SERVICE_DETAILS_BLOCK_EN; + } +}; diff --git a/packages/integrations/contentful-cms/src/modules/cms/mappers/blocks/cms.service-list.mapper.ts b/packages/integrations/contentful-cms/src/modules/cms/mappers/blocks/cms.service-list.mapper.ts new file mode 100644 index 00000000..616023af --- /dev/null +++ b/packages/integrations/contentful-cms/src/modules/cms/mappers/blocks/cms.service-list.mapper.ts @@ -0,0 +1,251 @@ +import { CMS } from '@o2s/framework/modules'; + +const MOCK_SERVICE_LIST_BLOCK_EN: CMS.Model.ServiceListBlock.ServiceListBlock = { + id: 'service-list-1', + title: 'Services', + subtitle: 'List of your services', + detailsLabel: 'Details', + fields: { + type: { + PHYSICAL: 'Physical', + VIRTUAL: 'Virtual', + }, + category: { + TOOLS: 'Tools', + HARDWARE: 'Hardware', + SOFTWARE: 'Software', + MEASUREMENT: 'Measurement', + }, + status: { + ACTIVE: 'Active', + INACTIVE: 'Inactive', + EXPIRED: 'Expired', + }, + paymentPeriod: { + MONTHLY: 'mo', + YEARLY: 'ye', + }, + }, + pagination: { + limit: 6, + legend: 'of {totalPages} pages', + prev: 'Previous', + next: 'Next', + selectPage: 'Select page', + }, + filters: { + label: 'Filter', + title: 'Filter Services', + description: 'Use filters to find specific services', + submit: 'Apply Filters', + reset: 'Reset Filters', + removeFilters: 'Remove filters ({active})', + close: 'Close filters', + items: [ + { + __typename: 'FilterSelect', + id: 'type', + label: 'Product Type', + allowMultiple: true, + options: [ + { label: 'Physical', value: 'PHYSICAL' }, + { label: 'Virtual', value: 'VIRTUAL' }, + ], + }, + { + __typename: 'FilterSelect', + id: 'category', + label: 'Product Category', + allowMultiple: true, + options: [ + { label: 'Software', value: 'SOFTWARE' }, + { label: 'Tools', value: 'TOOLS' }, + { label: 'Hardware', value: 'HARDWARE' }, + { label: 'Measurement', value: 'MEASUREMENT' }, + { label: 'Cloud', value: 'CLOUD' }, + { label: 'Support', value: 'SUPPORT' }, + { label: 'Subscription', value: 'SUBSCRIPTION' }, + { label: 'Warranty', value: 'WARRANTY' }, + { label: 'Maintenance', value: 'MAINTENANCE' }, + { label: 'Training', value: 'TRAINING' }, + ], + }, + ], + }, + noResults: { + title: 'No Services Found', + description: 'There are no services matching your criteria', + }, + detailsUrl: '/services/{id}', +}; + +const MOCK_SERVICE_LIST_BLOCK_DE: CMS.Model.ServiceListBlock.ServiceListBlock = { + id: 'service-list-1', + title: 'Dienstleistungen', + subtitle: 'Liste Ihrer Dienstleistungen', + detailsLabel: 'Details', + fields: { + type: { + PHYSICAL: 'Physikalisch', + VIRTUAL: 'Virtuell', + }, + category: { + TOOLS: 'Werkzeuge', + HARDWARE: 'Hardware', + SOFTWARE: 'Software', + MEASUREMENT: 'Messung', + }, + status: { + ACTIVE: 'Aktiv', + INACTIVE: 'Inaktiv', + EXPIRED: 'Abgelaufen', + }, + paymentPeriod: { + MONTHLY: 'mo', + YEARLY: 'ye', + }, + }, + pagination: { + limit: 6, + legend: 'von {totalPages} Seiten', + prev: 'Vorherige', + next: 'Nächste', + selectPage: 'Seite auswählen', + }, + filters: { + label: 'Filter', + title: 'Filter Dienstleistungen', + description: 'Verwenden Sie Filter, um spezifische Dienstleistungen zu finden', + submit: 'Filter anwenden', + reset: 'Filter zurücksetzen', + removeFilters: 'Filter entfernen ({active})', + close: 'Filter schließen', + items: [ + { + __typename: 'FilterSelect', + id: 'type', + label: 'Produkttyp', + allowMultiple: true, + options: [ + { label: 'Physikalisch', value: 'PHYSICAL' }, + { label: 'Virtuell', value: 'VIRTUAL' }, + ], + }, + { + __typename: 'FilterSelect', + id: 'category', + label: 'Produktkategorie', + allowMultiple: true, + options: [ + { label: 'Software', value: 'SOFTWARE' }, + { label: 'Werkzeuge', value: 'TOOLS' }, + { label: 'Hardware', value: 'HARDWARE' }, + { label: 'Messung', value: 'MEASUREMENT' }, + { label: 'Cloud', value: 'CLOUD' }, + { label: 'Support', value: 'SUPPORT' }, + { label: 'Subskryption', value: 'SUBSCRIPTION' }, + { label: 'Garantie', value: 'WARRANTY' }, + { label: 'Wartung', value: 'MAINTENANCE' }, + { label: 'Training', value: 'TRAINING' }, + ], + }, + ], + }, + noResults: { + title: 'Keine Dienstleistungen gefunden', + description: 'Es gibt keine Dienstleistungen, die Ihren Kriterien entsprechen', + }, + detailsUrl: '/dienstleistungen/{id}', +}; + +const MOCK_SERVICE_LIST_BLOCK_PL: CMS.Model.ServiceListBlock.ServiceListBlock = { + id: 'service-list-1', + title: 'Usługi', + subtitle: 'Lista Twoich usług', + detailsLabel: 'Szczegóły', + fields: { + type: { + PHYSICAL: 'Fizyczny', + VIRTUAL: 'Wirtualny', + }, + category: { + TOOLS: 'Narzędzia', + HARDWARE: 'Sprzęt', + SOFTWARE: 'Oprogramowanie', + MEASUREMENT: 'Pomiar', + }, + status: { + ACTIVE: 'Aktywny', + INACTIVE: 'Nieaktywny', + EXPIRED: 'Wygasły', + }, + paymentPeriod: { + MONTHLY: 'miesięczny', + YEARLY: 'roczny', + }, + }, + pagination: { + limit: 6, + legend: 'z {totalPages} stron', + prev: 'Poprzednia', + next: 'Następna', + selectPage: 'Wybierz stronę', + }, + filters: { + label: 'Filtruj', + title: 'Filtruj Usługi', + description: 'Użyj filtrów, aby znaleźć konkretne usługi', + submit: 'Zastosuj Filtry', + reset: 'Resetuj Filtry', + removeFilters: 'Usuń filtry ({active})', + close: 'Zamknij filtry', + items: [ + { + __typename: 'FilterSelect', + id: 'type', + label: 'Typ Produktu', + allowMultiple: true, + options: [ + { label: 'Fizyczny', value: 'PHYSICAL' }, + { label: 'Wirtualny', value: 'VIRTUAL' }, + ], + }, + { + __typename: 'FilterSelect', + id: 'category', + label: 'Kategoria Produktu', + allowMultiple: true, + options: [ + { label: 'Oprogramowanie', value: 'SOFTWARE' }, + { label: 'Narzędzia', value: 'TOOLS' }, + { label: 'Sprzęt', value: 'HARDWARE' }, + { label: 'Pomiar', value: 'MEASUREMENT' }, + { label: 'Chmura', value: 'CLOUD' }, + { label: 'Wsparcie', value: 'SUPPORT' }, + { label: 'Subskrypcja', value: 'SUBSCRIPTION' }, + { label: 'Gwarancja', value: 'WARRANTY' }, + { label: 'Utrzymanie', value: 'MAINTENANCE' }, + { label: 'Szkolenie', value: 'TRAINING' }, + ], + }, + ], + }, + noResults: { + title: 'Nie znaleziono usług', + description: 'Nie znaleziono usług spełniających Twoje kryteria', + }, + detailsUrl: '/usługi/{id}', +}; + +export const mapServiceListBlock = (locale: string): CMS.Model.ServiceListBlock.ServiceListBlock => { + switch (locale) { + case 'en': + return MOCK_SERVICE_LIST_BLOCK_EN; + case 'de': + return MOCK_SERVICE_LIST_BLOCK_DE; + case 'pl': + return MOCK_SERVICE_LIST_BLOCK_PL; + default: + return MOCK_SERVICE_LIST_BLOCK_EN; + } +}; diff --git a/packages/integrations/contentful-cms/src/modules/cms/mappers/blocks/cms.ticket-details.mapper.ts b/packages/integrations/contentful-cms/src/modules/cms/mappers/blocks/cms.ticket-details.mapper.ts new file mode 100644 index 00000000..76acbdf9 --- /dev/null +++ b/packages/integrations/contentful-cms/src/modules/cms/mappers/blocks/cms.ticket-details.mapper.ts @@ -0,0 +1,141 @@ +import { CMS } from '@o2s/framework/modules'; + +const MOCK_TICKET_DETAILS_BLOCK_EN: CMS.Model.TicketDetailsBlock.TicketDetailsBlock = { + id: 'ticket-list-1', + title: 'Case details', + commentsTitle: 'Comments', + attachmentsTitle: 'Attachments', + properties: { + id: 'Case ID', + topic: 'Topic', + type: 'Case type', + status: 'Status', + description: 'Additional notes', + address: 'Service address', + contact: 'Form of contact', + }, + fieldMapping: { + topic: { + TOOL_REPAIR: 'Tool Repair', + FLEET_EXCHANGE: 'Fleet Exchange', + CALIBRATION: 'Calibration', + THEFT_REPORT: 'Theft Report', + SOFTWARE_SUPPORT: 'Software Support', + RENTAL_REQUEST: 'Rental Request', + TRAINING_REQUEST: 'Training Request', + }, + type: { + URGENT: 'Urgent', + STANDARD: 'Standard', + LOW_PRIORITY: 'Low Priority', + }, + status: { + OPEN: 'Under consideration', + CLOSED: 'Resolved', + IN_PROGRESS: 'New response', + }, + }, + labels: { + showMore: 'Show case details', + showLess: 'Show less details', + today: 'Today', + yesterday: 'Yesterday', + }, +}; + +const MOCK_TICKET_DETAILS_BLOCK_PL: CMS.Model.TicketDetailsBlock.TicketDetailsBlock = { + id: 'ticket-list-1', + title: 'Szczegóły sprawy', + commentsTitle: 'Komentarze', + attachmentsTitle: 'Załączniki', + properties: { + id: 'ID sprawy', + topic: 'Temat', + type: 'Typ sprawy', + status: 'Status', + description: 'Dodatkowe notatki', + address: 'Adres serwisowy', + contact: 'Forma kontaktu', + }, + fieldMapping: { + topic: { + TOOL_REPAIR: 'Naprawa narzędzia', + FLEET_EXCHANGE: 'Wymiana floty', + CALIBRATION: 'Kalibracja', + THEFT_REPORT: 'Zgłoszenie kradzieży', + SOFTWARE_SUPPORT: 'Wsparcie oprogramowania', + RENTAL_REQUEST: 'Prośba o wynajem', + TRAINING_REQUEST: 'Prośba o szkolenie', + }, + type: { + URGENT: 'Pilne', + STANDARD: 'Standardowe', + LOW_PRIORITY: 'Niski priorytet', + }, + status: { + OPEN: 'W trakcie rozpatrywania', + CLOSED: 'Rozwiązane', + IN_PROGRESS: 'Nowa odpowiedź', + }, + }, + labels: { + showMore: 'Pokaż szczegóły sprawy', + showLess: 'Pokaż mniej szczegółów', + today: 'Dzisiaj', + yesterday: 'Wczoraj', + }, +}; + +const MOCK_TICKET_DETAILS_BLOCK_DE: CMS.Model.TicketDetailsBlock.TicketDetailsBlock = { + id: 'ticket-list-1', + title: 'Falldetails', + commentsTitle: 'Kommentare', + attachmentsTitle: 'Anhänge', + properties: { + id: 'Fall-ID', + topic: 'Thema', + type: 'Falltyp', + status: 'Status', + description: 'Zusätzliche Notizen', + address: 'Serviceadresse', + contact: 'Kontaktform', + }, + fieldMapping: { + topic: { + TOOL_REPAIR: 'Werkzeugreparatur', + FLEET_EXCHANGE: 'Flottenaustausch', + CALIBRATION: 'Kalibrierung', + THEFT_REPORT: 'Diebstahlmeldung', + SOFTWARE_SUPPORT: 'Software-Support', + RENTAL_REQUEST: 'Mietanfrage', + TRAINING_REQUEST: 'Schulungsanfrage', + }, + type: { + URGENT: 'Dringend', + STANDARD: 'Standard', + LOW_PRIORITY: 'Niedrige Priorität', + }, + status: { + OPEN: 'In Bearbeitung', + CLOSED: 'Gelöst', + IN_PROGRESS: 'Neue Antwort', + }, + }, + labels: { + showMore: 'Falldetails anzeigen', + showLess: 'Weniger Details anzeigen', + today: 'Heute', + yesterday: 'Gestern', + }, +}; + +export const mapTicketDetailsBlock = (_locale: string): CMS.Model.TicketDetailsBlock.TicketDetailsBlock => { + switch (_locale) { + case 'pl': + return MOCK_TICKET_DETAILS_BLOCK_PL; + case 'de': + return MOCK_TICKET_DETAILS_BLOCK_DE; + default: + return MOCK_TICKET_DETAILS_BLOCK_EN; + } +}; diff --git a/packages/integrations/contentful-cms/src/modules/cms/mappers/blocks/cms.ticket-list.mapper.ts b/packages/integrations/contentful-cms/src/modules/cms/mappers/blocks/cms.ticket-list.mapper.ts new file mode 100644 index 00000000..7618032b --- /dev/null +++ b/packages/integrations/contentful-cms/src/modules/cms/mappers/blocks/cms.ticket-list.mapper.ts @@ -0,0 +1,40 @@ +import { NotFoundException } from '@nestjs/common'; +import { Entry } from 'contentful'; + +import { CMS } from '@o2s/framework/modules'; + +import { mapFields } from '../cms.fieldMapping.mapper'; +import { mapPagination } from '../cms.pagination.mapper'; +import { mapTable } from '../cms.table.mapper'; + +import { IBlockTicketListFields } from '@/generated/contentful'; + +export const mapTicketListBlock = (data: Entry): CMS.Model.TicketListBlock.TicketListBlock => { + if (!data) { + throw new NotFoundException(); + } + + const { fields, sys } = data; + + switch (sys.contentType.sys.id) { + case 'blockTicketList': + return { + id: sys.id, + title: fields.title, + subtitle: fields.subTitle, + table: mapTable(fields.table), + fieldMapping: mapFields(fields.fields), + pagination: mapPagination(fields.pagination), + // TODO: add filters + // filters: mapFilters(component.filters), + noResults: fields.noResults.fields, + labels: { + today: fields.labels.fields.today, + yesterday: fields.labels.fields.yesterday, + }, + detailsUrl: fields.detailsUrl, + }; + } + + throw new NotFoundException(); +}; diff --git a/packages/integrations/contentful-cms/src/modules/cms/mappers/blocks/cms.ticket-recent.mapper.ts b/packages/integrations/contentful-cms/src/modules/cms/mappers/blocks/cms.ticket-recent.mapper.ts new file mode 100644 index 00000000..ed800af7 --- /dev/null +++ b/packages/integrations/contentful-cms/src/modules/cms/mappers/blocks/cms.ticket-recent.mapper.ts @@ -0,0 +1,51 @@ +import { CMS } from '@o2s/framework/modules'; + +const MOCK_TICKET_RECENT_BLOCK_EN: CMS.Model.TicketRecentBlock.TicketRecentBlock = { + id: 'ticket-recent-1', + title: 'Recent activity in cases', + commentsTitle: 'Comments', + labels: { + today: 'Today', + yesterday: 'Yesterday', + details: 'Details', + }, + limit: 3, + detailsUrl: '/cases/{id}', +}; + +const MOCK_TICKET_RECENT_BLOCK_DE: CMS.Model.TicketRecentBlock.TicketRecentBlock = { + id: 'ticket-recent-1', + title: 'Letzte Aktivität in Fällen', + commentsTitle: 'Kommentare', + labels: { + today: 'Heute', + yesterday: 'Gestern', + details: 'Einzelheiten', + }, + limit: 3, + detailsUrl: '/faelle/{id}', +}; + +const MOCK_TICKET_RECENT_BLOCK_PL: CMS.Model.TicketRecentBlock.TicketRecentBlock = { + id: 'ticket-recent-1', + title: 'Ostatnia aktywność w zgłoszeniach', + commentsTitle: 'Komentarze', + labels: { + today: 'Dzisiaj', + yesterday: 'Wczoraj', + details: 'Szczegóły', + }, + limit: 3, + detailsUrl: '/zgloszenia/{id}', +}; + +export const mapTicketRecentBlock = (locale: string): CMS.Model.TicketRecentBlock.TicketRecentBlock => { + switch (locale) { + case 'pl': + return MOCK_TICKET_RECENT_BLOCK_PL; + case 'de': + return MOCK_TICKET_RECENT_BLOCK_DE; + default: + return MOCK_TICKET_RECENT_BLOCK_EN; + } +}; diff --git a/packages/integrations/contentful-cms/src/modules/cms/mappers/blocks/cms.user-account.mapper.ts b/packages/integrations/contentful-cms/src/modules/cms/mappers/blocks/cms.user-account.mapper.ts new file mode 100644 index 00000000..d01269f7 --- /dev/null +++ b/packages/integrations/contentful-cms/src/modules/cms/mappers/blocks/cms.user-account.mapper.ts @@ -0,0 +1,224 @@ +import { CMS } from '@o2s/framework/modules'; + +const MOCK_USER_ACCOUNT_BLOCK_EN: CMS.Model.UserAccountBlock.UserAccountBlock = { + id: 'user-account-1', + title: 'User Account', + basicInformationTitle: 'Basic Information', + basicInformationDescription: 'Update your personal information to keep your account details current and accurate.', + fields: [ + { + id: 'first-name-1', + name: 'firstName', + label: 'First Name', + placeholder: 'Enter first name', + errorMessages: [ + { + type: 'required', + description: 'First name is required', + id: 'required-1', + name: 'Required', + }, + { + type: 'matches', + description: 'First name can contain only letters, numbers, dots and hyphens', + id: 'matches-1', + name: 'Matches', + }, + { + type: 'min', + description: 'First name must be at least 3 characters long', + id: 'min-1', + name: 'Min', + }, + ], + }, + { + id: 'last-name-1', + name: 'lastName', + label: 'Last Name', + placeholder: 'Enter last name', + errorMessages: [ + { + type: 'required', + description: 'Last name is required', + id: 'required-1', + name: 'Required', + }, + ], + }, + { + id: 'email-1', + name: 'email', + label: 'Email', + placeholder: 'Enter email', + errorMessages: [ + { + type: 'required', + description: 'Email is required', + id: 'required-1', + name: 'Required', + }, + ], + }, + ], + labels: { + edit: 'Edit', + save: 'Save', + cancel: 'Cancel', + delete: 'Delete', + logOut: 'Log out', + }, +}; + +const MOCK_USER_ACCOUNT_BLOCK_PL: CMS.Model.UserAccountBlock.UserAccountBlock = { + id: 'user-account-1', + title: 'Konto Użytkownika', + basicInformationTitle: 'Podstawowe Informacje', + basicInformationDescription: + 'Zaktualizuj swoje dane osobowe, aby utrzymać aktualne i dokładne informacje o koncie.', + fields: [ + { + id: 'first-name-1', + name: 'firstName', + label: 'Imię', + placeholder: 'Wprowadź imię', + errorMessages: [ + { + type: 'required', + description: 'Imię jest wymagane', + id: 'required-1', + name: 'Wymagane', + }, + { + type: 'matches', + description: 'Imię może zawierać tylko litery, cyfry, kropki i myślniki', + id: 'matches-1', + name: 'Dopasowanie', + }, + { + type: 'min', + description: 'Imię musi mieć co najmniej 3 znaki', + id: 'min-1', + name: 'Minimum', + }, + ], + }, + { + id: 'last-name-1', + name: 'lastName', + label: 'Nazwisko', + placeholder: 'Wprowadź nazwisko', + errorMessages: [ + { + type: 'required', + description: 'Nazwisko jest wymagane', + id: 'required-1', + name: 'Wymagane', + }, + ], + }, + { + id: 'email-1', + name: 'email', + label: 'Email', + placeholder: 'Wprowadź email', + errorMessages: [ + { + type: 'required', + description: 'Email jest wymagany', + id: 'required-1', + name: 'Wymagane', + }, + ], + }, + ], + labels: { + edit: 'Edytuj', + save: 'Zapisz', + cancel: 'Anuluj', + delete: 'Usuń', + logOut: 'Wyloguj', + }, +}; + +const MOCK_USER_ACCOUNT_BLOCK_DE: CMS.Model.UserAccountBlock.UserAccountBlock = { + id: 'user-account-1', + title: 'Benutzerkonto', + basicInformationTitle: 'Grundinformationen', + basicInformationDescription: + 'Aktualisieren Sie Ihre persönlichen Daten, um Ihre Kontoinformationen aktuell und genau zu halten.', + fields: [ + { + id: 'first-name-1', + name: 'firstName', + label: 'Vorname', + placeholder: 'Vorname eingeben', + errorMessages: [ + { + type: 'required', + description: 'Vorname ist erforderlich', + id: 'required-1', + name: 'Erforderlich', + }, + { + type: 'matches', + description: 'Vorname darf nur Buchstaben, Zahlen, Punkte und Bindestriche enthalten', + id: 'matches-1', + name: 'Übereinstimmung', + }, + { + type: 'min', + description: 'Vorname muss mindestens 3 Zeichen lang sein', + id: 'min-1', + name: 'Minimum', + }, + ], + }, + { + id: 'last-name-1', + name: 'lastName', + label: 'Nachname', + placeholder: 'Nachname eingeben', + errorMessages: [ + { + type: 'required', + description: 'Nachname ist erforderlich', + id: 'required-1', + name: 'Erforderlich', + }, + ], + }, + { + id: 'email-1', + name: 'email', + label: 'E-Mail', + placeholder: 'E-Mail eingeben', + errorMessages: [ + { + type: 'required', + description: 'E-Mail ist erforderlich', + id: 'required-1', + name: 'Erforderlich', + }, + ], + }, + ], + labels: { + edit: 'Bearbeiten', + save: 'Speichern', + cancel: 'Abbrechen', + delete: 'Löschen', + logOut: 'Abmelden', + }, +}; + +export const mapUserAccountBlock = (locale: string): CMS.Model.UserAccountBlock.UserAccountBlock => { + switch (locale) { + case 'pl': + return MOCK_USER_ACCOUNT_BLOCK_PL; + case 'de': + return MOCK_USER_ACCOUNT_BLOCK_DE; + default: + return MOCK_USER_ACCOUNT_BLOCK_EN; + } +}; diff --git a/packages/integrations/contentful-cms/src/modules/cms/mappers/cms.app-config.mapper.ts b/packages/integrations/contentful-cms/src/modules/cms/mappers/cms.app-config.mapper.ts new file mode 100644 index 00000000..9ada2042 --- /dev/null +++ b/packages/integrations/contentful-cms/src/modules/cms/mappers/cms.app-config.mapper.ts @@ -0,0 +1,71 @@ +import { CMS } from '@o2s/framework/modules'; + +const APP_CONFIG_EN: CMS.Model.AppConfig.AppConfig = { + locales: [ + { + value: 'en', + label: 'EN', + }, + { + value: 'de', + label: 'DE', + }, + { + value: 'pl', + label: 'PL', + }, + ], + header: 'fqj6nnyk4irqq5b7rnc4ogsj', + footer: 'footer-1', +}; + +const APP_CONFIG_DE: CMS.Model.AppConfig.AppConfig = { + locales: [ + { + value: 'en', + label: 'EN', + }, + { + value: 'de', + label: 'DE', + }, + { + value: 'pl', + label: 'PL', + }, + ], + header: 'fqj6nnyk4irqq5b7rnc4ogsj', + footer: 'footer-1', +}; + +const APP_CONFIG_PL: CMS.Model.AppConfig.AppConfig = { + locales: [ + { + value: 'en', + label: 'EN', + }, + { + value: 'de', + label: 'DE', + }, + { + value: 'pl', + label: 'PL', + }, + ], + header: 'fqj6nnyk4irqq5b7rnc4ogsj', + footer: 'footer-1', +}; + +export const mapAppConfig = (_referrer: string, locale: string): CMS.Model.AppConfig.AppConfig => { + switch (locale) { + case 'en': + return APP_CONFIG_EN; + case 'de': + return APP_CONFIG_DE; + case 'pl': + return APP_CONFIG_PL; + } + + return APP_CONFIG_EN; +}; diff --git a/packages/integrations/contentful-cms/src/modules/cms/mappers/cms.fieldMapping.mapper.ts b/packages/integrations/contentful-cms/src/modules/cms/mappers/cms.fieldMapping.mapper.ts new file mode 100644 index 00000000..8951dd5f --- /dev/null +++ b/packages/integrations/contentful-cms/src/modules/cms/mappers/cms.fieldMapping.mapper.ts @@ -0,0 +1,21 @@ +import { Entry } from 'contentful'; + +import { Models } from '@o2s/framework/modules'; + +import { IComponentFieldMappingFields } from '@/generated/contentful'; + +export const mapFields = (component: Entry[]): Models.Mapping.Mapping => { + return component.reduce( + (acc, field) => ({ + ...acc, + [field.fields.name]: field.fields.values.reduce( + (acc, item) => ({ + ...acc, + [item.fields.key]: item.fields.value, + }), + {} as { [key: string]: string }, + ), + }), + {} as Models.Mapping.Mapping, + ); +}; diff --git a/packages/integrations/contentful-cms/src/modules/cms/mappers/cms.footer.mapper.ts b/packages/integrations/contentful-cms/src/modules/cms/mappers/cms.footer.mapper.ts new file mode 100644 index 00000000..7efeff71 --- /dev/null +++ b/packages/integrations/contentful-cms/src/modules/cms/mappers/cms.footer.mapper.ts @@ -0,0 +1,145 @@ +import { CMS } from '@o2s/framework/modules'; + +const MOCK_FOOTER_EN: CMS.Model.Footer.Footer = { + id: 'laee0xa1zmm9uraev3hpruho', + title: 'Legal and privacy', + logo: { + url: 'https://raw.githubusercontent.com/o2sdev/openselfservice/refs/heads/main/packages/integrations/mocked/public/images/logo.svg', + alternativeText: 'Logo', + width: 92, + height: 24, + name: 'Logo', + }, + items: [ + { + __typename: 'NavigationGroup', + title: 'Privacy Policy', + items: [ + { + label: 'Privacy Policy 1', + url: 'https://hycom.digital/privacy-policy', + __typename: 'NavigationItem', + }, + { + label: 'Privacy Policy 2', + url: 'https://hycom.digital/privacy-policy', + __typename: 'NavigationItem', + }, + ], + }, + { + __typename: 'NavigationGroup', + title: 'Terms of Service', + items: [ + { + label: 'Terms of Service 1', + url: 'https://hycom.digital/terms-and-conditions', + __typename: 'NavigationItem', + }, + { + label: 'Terms of Service 2', + url: 'https://hycom.digital/terms-and-conditions', + __typename: 'NavigationItem', + }, + ], + }, + { + __typename: 'NavigationGroup', + title: 'Cookies Settings', + items: [ + { label: 'Cookies Settings 1', url: '/', __typename: 'NavigationItem' }, + { label: 'Cookies Settings 2', url: '/', __typename: 'NavigationItem' }, + ], + }, + ], + copyright: '© Open Self Service 2025', +}; + +const MOCK_FOOTER_PL: CMS.Model.Footer.Footer = { + id: 'laee0xa1zmm9uraev3hpruho', + title: 'Informacje prawne i prywatność', + logo: { + url: 'https://raw.githubusercontent.com/o2sdev/openselfservice/refs/heads/main/packages/integrations/mocked/public/images/logo.svg', + alternativeText: 'Logo', + width: 92, + height: 24, + name: 'Logo', + }, + items: [ + { + __typename: 'NavigationGroup', + title: 'Polityka Prywatności', + items: [ + { label: 'Polityka Prywatności 1', url: '/powiadomienia', __typename: 'NavigationItem' }, + { label: 'Polityka Prywatności 2', url: '/rachunki', __typename: 'NavigationItem' }, + ], + }, + { + __typename: 'NavigationGroup', + title: 'Warunki Korzystania', + items: [ + { label: 'Warunki Korzystania 1', url: '/powiadomienia', __typename: 'NavigationItem' }, + { label: 'Warunki Korzystania 2', url: '/rachunki', __typename: 'NavigationItem' }, + ], + }, + { + __typename: 'NavigationGroup', + title: 'Ustawienia Plików Cookie', + items: [ + { label: 'Ustawienia Plików Cookie 1', url: '/powiadomienia', __typename: 'NavigationItem' }, + { label: 'Ustawienia Plików Cookie 2', url: '/rachunki', __typename: 'NavigationItem' }, + ], + }, + ], + copyright: '© Open Self Service 2025', +}; + +const MOCK_FOOTER_DE: CMS.Model.Footer.Footer = { + id: 'laee0xa1zmm9uraev3hpruho', + title: 'Rechtliches und Datenschutz', + logo: { + url: 'https://raw.githubusercontent.com/o2sdev/openselfservice/refs/heads/main/packages/integrations/mocked/public/images/logo.svg', + alternativeText: 'Logo', + width: 92, + height: 24, + name: 'Logo', + }, + items: [ + { + __typename: 'NavigationGroup', + title: 'Datenschutzrichtlinie', + items: [ + { label: 'Datenschutzrichtlinie 1', url: '/benachrichtigungen', __typename: 'NavigationItem' }, + { label: 'Datenschutzrichtlinie 2', url: '/invoices', __typename: 'NavigationItem' }, + ], + }, + { + __typename: 'NavigationGroup', + title: 'Nutzungsbedingungen', + items: [ + { label: 'Nutzungsbedingungen 1', url: '/benachrichtigungen', __typename: 'NavigationItem' }, + { label: 'Nutzungsbedingungen 2', url: '/invoices', __typename: 'NavigationItem' }, + ], + }, + { + __typename: 'NavigationGroup', + title: 'Cookie-Einstellungen', + items: [ + { label: 'Cookie-Einstellungen 1', url: '/benachrichtigungen', __typename: 'NavigationItem' }, + { label: 'Cookie-Einstellungen 2', url: '/invoices', __typename: 'NavigationItem' }, + ], + }, + ], + copyright: '© Open Self Service 2025', +}; + +export const mapFooter = (locale: string): CMS.Model.Footer.Footer => { + switch (locale) { + case 'pl': + return MOCK_FOOTER_PL; + case 'de': + return MOCK_FOOTER_DE; + default: + return MOCK_FOOTER_EN; + } +}; diff --git a/packages/integrations/contentful-cms/src/modules/cms/mappers/cms.header.mapper.ts b/packages/integrations/contentful-cms/src/modules/cms/mappers/cms.header.mapper.ts new file mode 100644 index 00000000..eb4bde15 --- /dev/null +++ b/packages/integrations/contentful-cms/src/modules/cms/mappers/cms.header.mapper.ts @@ -0,0 +1,325 @@ +import { CMS } from '@o2s/framework/modules'; + +const MOCK_HEADER_LOGON_EN: CMS.Model.Header.Header = { + id: 'fqj6nnyk4irqq5b7rnc4ogsj', + title: 'MOCK_HEADER_LOGON_EN', + logo: { + url: 'https://raw.githubusercontent.com/o2sdev/openselfservice/refs/heads/main/packages/integrations/mocked/public/images/logo.svg', + alternativeText: 'Logo', + width: 92, + height: 24, + name: 'Logo', + }, + languageSwitcherLabel: 'Language', + mobileMenuLabel: { + open: 'Open menu', + close: 'Close Menu', + }, + userInfo: { + url: '/user-account', + label: 'User Profile', + }, + items: [ + { + __typename: 'NavigationGroup', + title: 'Customer Portal', + items: [ + { + __typename: 'NavigationItem', + label: 'Dashboard', + url: '/', + }, + { + __typename: 'NavigationItem', + label: 'Cases', + url: '/cases', + }, + { + __typename: 'NavigationItem', + label: 'Invoices', + url: '/invoices', + }, + { + __typename: 'NavigationItem', + label: 'Notifications', + url: '/notifications', + }, + { + __typename: 'NavigationItem', + label: 'Services', + url: '/services', + }, + ], + }, + { + __typename: 'NavigationGroup', + title: 'Help & Support', + items: [ + { + __typename: 'NavigationItem', + label: 'O2S Documentation', + url: 'https://www.openselfservice.com/', + }, + ], + }, + ], + notification: { + url: '/notifications', + label: 'Notifications', + }, + contextSwitcher: { + label: 'Power Construction AG', + clear: 'Clear', + apply: 'Apply', + close: 'Close', + }, +}; +const MOCK_HEADER_LOGON_DE: CMS.Model.Header.Header = { + id: 'fqj6nnyk4irqq5b7rnc4ogsj', + title: 'MOCK_HEADER_LOGON_DE', + logo: { + url: 'https://raw.githubusercontent.com/o2sdev/openselfservice/refs/heads/main/packages/integrations/mocked/public/images/logo.svg', + alternativeText: 'Logo', + width: 92, + height: 24, + name: 'Logo', + }, + languageSwitcherLabel: 'Sprache', + mobileMenuLabel: { + open: 'Menü öffnen', + close: 'Menü schließen', + }, + items: [ + { + __typename: 'NavigationGroup', + title: 'Kunden Portal', + items: [ + { + __typename: 'NavigationItem', + label: 'Startseite', + url: '/', + }, + { + __typename: 'NavigationItem', + label: 'Fälle', + url: '/faelle', + }, + { + __typename: 'NavigationItem', + label: 'Rechnungen', + url: '/rechnungen', + }, + { + __typename: 'NavigationItem', + label: 'Benachrichtigungen', + url: '/benachrichtigungen', + }, + { + __typename: 'NavigationItem', + label: 'Dienstleistungen', + url: '/dienstleistungen', + }, + ], + }, + { + __typename: 'NavigationGroup', + title: 'Hilfe & Support', + items: [ + { + __typename: 'NavigationItem', + label: 'O2S Dokumentation', + url: 'https://www.openselfservice.com/', + }, + ], + }, + ], + notification: { + url: '/benachrichtigungen', + label: 'Benachrichtigungen', + }, + contextSwitcher: { + label: 'Power Construction AG', + clear: 'Löschen', + apply: 'Anwenden', + close: 'Schließen', + }, +}; +const MOCK_HEADER_LOGON_PL: CMS.Model.Header.Header = { + id: 'fqj6nnyk4irqq5b7rnc4ogsj', + title: 'MOCK_HEADER_LOGON_PL', + logo: { + url: 'https://raw.githubusercontent.com/o2sdev/openselfservice/refs/heads/main/packages/integrations/mocked/public/images/logo.svg', + alternativeText: 'Logo', + width: 92, + height: 24, + name: 'Logo', + }, + languageSwitcherLabel: 'Język', + mobileMenuLabel: { + open: 'Otwórz menu', + close: 'Zamknij menu', + }, + userInfo: { + url: '/konto-uzytkownika', + label: 'Profil użytkownika', + }, + items: [ + { + __typename: 'NavigationGroup', + title: 'Portal klienta', + items: [ + { + __typename: 'NavigationItem', + label: 'Strona główna', + url: '/', + }, + { + __typename: 'NavigationItem', + label: 'Zgłoszenia', + url: '/zgloszenia', + }, + { + __typename: 'NavigationItem', + label: 'Rachunki', + url: '/rachunki', + }, + { + __typename: 'NavigationItem', + label: 'Powiadomienia', + url: '/powiadomienia', + }, + { + __typename: 'NavigationItem', + label: 'Usługi', + url: '/uslugi', + }, + ], + }, + { + __typename: 'NavigationGroup', + title: 'Pomoc & Wsparcie', + items: [ + { + __typename: 'NavigationItem', + label: 'Dokumentacja O2S', + url: 'https://www.openselfservice.com/', + }, + ], + }, + ], + notification: { + url: '/powiadomienia', + label: 'Powiadomienia', + }, + contextSwitcher: { + label: 'Power Construction AG', + clear: 'Wyczyść', + apply: 'Zastosuj', + close: 'Zamknij', + }, +}; + +const MOCK_HEADER_LOGOUT_EN: CMS.Model.Header.Header = { + id: 'lwvbmnaayn6w7xy5soicv1k2', + title: 'MOCK_HEADER_LOGOUT_EN', + logo: { + url: 'https://raw.githubusercontent.com/o2sdev/openselfservice/refs/heads/main/packages/integrations/mocked/public/images/logo.svg', + alternativeText: 'Logo', + width: 92, + height: 24, + name: 'Logo', + }, + languageSwitcherLabel: 'Language', + mobileMenuLabel: { + open: 'Open menu', + close: 'Close Menu', + }, + items: [ + { + __typename: 'NavigationItem', + label: 'Customer Portal', + url: '/', + }, + { + __typename: 'NavigationItem', + label: 'Help & Support', + url: '/cases', + }, + ], +}; +const MOCK_HEADER_LOGOUT_DE: CMS.Model.Header.Header = { + id: 'lwvbmnaayn6w7xy5soicv1k2', + title: 'MOCK_HEADER_LOGOUT_DE', + logo: { + url: 'https://raw.githubusercontent.com/o2sdev/openselfservice/refs/heads/main/packages/integrations/mocked/public/images/logo.svg', + alternativeText: 'Logo', + width: 92, + height: 24, + name: 'Logo', + }, + languageSwitcherLabel: 'Sprache', + mobileMenuLabel: { + open: 'Menü öffnen', + close: 'Menü schließen', + }, + items: [ + { + __typename: 'NavigationItem', + label: 'Kunden Portal', + url: '/', + }, + { + __typename: 'NavigationItem', + label: 'Hilfe & Unterstützung', + url: '/faelle', + }, + ], +}; +const MOCK_HEADER_LOGOUT_PL: CMS.Model.Header.Header = { + id: 'lwvbmnaayn6w7xy5soicv1k2', + title: 'MOCK_HEADER_LOGOUT_PL', + logo: { + url: 'https://raw.githubusercontent.com/o2sdev/openselfservice/refs/heads/main/packages/integrations/mocked/public/images/logo.svg', + alternativeText: 'Logo', + width: 92, + height: 24, + name: 'Logo', + }, + languageSwitcherLabel: 'Język', + mobileMenuLabel: { + open: 'Otwórz menu', + close: 'Zamknij menu', + }, + items: [ + { + __typename: 'NavigationItem', + label: 'Portal Klienta', + url: '/', + }, + { + __typename: 'NavigationItem', + label: 'Pomoc & Wsparcie', + url: '/zgloszenia', + }, + ], +}; + +export const mapHeader = (id: string, locale: string): CMS.Model.Header.Header => { + const headerList = [ + MOCK_HEADER_LOGON_EN, + MOCK_HEADER_LOGON_DE, + MOCK_HEADER_LOGON_PL, + MOCK_HEADER_LOGOUT_EN, + MOCK_HEADER_LOGOUT_DE, + MOCK_HEADER_LOGOUT_PL, + ]; + + const header = headerList + .filter((header) => header.title?.endsWith(locale.toUpperCase())) + .find((header) => header.id === id); + + if (!header) { + return MOCK_HEADER_LOGON_EN; + } + return header; +}; diff --git a/packages/integrations/contentful-cms/src/modules/cms/mappers/cms.login-page.mapper.ts b/packages/integrations/contentful-cms/src/modules/cms/mappers/cms.login-page.mapper.ts new file mode 100644 index 00000000..6b8834da --- /dev/null +++ b/packages/integrations/contentful-cms/src/modules/cms/mappers/cms.login-page.mapper.ts @@ -0,0 +1,308 @@ +import { CMS } from '@o2s/framework/modules'; + +const LOGIN_PAGE_PL: CMS.Model.LoginPage.LoginPage = { + title: 'Zaloguj się', + subtitle: 'Wprowadź swój email i hasło, aby uzyskać dostęp do konta', + username: { + id: 'username-1', + name: 'username', + label: 'Nazwa użytkownika', + placeholder: 'Wprowadź nazwę użytkownika', + errorMessages: [ + { + type: 'required', + description: 'Nazwa użytkownika jest wymagana', + id: 'required-1', + name: 'Required', + }, + { + type: 'matches', + description: 'Nazwa użytkownika może zawierać tylko litery, cyfry, kropki, myślniki i znak @', + id: 'matches-1', + name: 'Matches', + }, + { + type: 'min', + description: 'Nazwa użytkownika musi zawierać co najmniej 5 znaków', + id: 'min-1', + name: 'Min', + }, + ], + }, + password: { + id: 'password-1', + name: 'password', + label: 'Hasło', + placeholder: 'Wprowadź swoje hasło', + errorMessages: [ + { + type: 'required', + description: 'Hasło jest wymagane', + id: 'required-1', + name: 'Required', + }, + { + type: 'min', + description: 'Hasło musi zawierać co najmniej 4 znaki', + id: 'min-1', + name: 'Min', + }, + { + type: 'matches', + description: 'Hasło musi zawierać co najmniej 4 znaki', + id: 'matches-1', + name: 'Matches', + }, + ], + }, + signIn: 'Zaloguj się', + providers: { + title: 'LUB', + label: 'Zaloguj się przez ', + }, + createdAt: '2024-01-01', + updatedAt: '2024-01-01', + publishedAt: '2024-01-01', + labels: { + show: 'Pokaż', + hide: 'Ukryj', + }, + image: { + url: 'https://raw.githubusercontent.com/o2sdev/openselfservice/refs/heads/main/packages/integrations/mocked/public/images/sign-in.jpg', + alternativeText: 'Sign in image', + width: 640, + height: 656, + name: 'Sign in image', + }, + seo: { + title: 'Zaloguj się', + description: 'Wprowadź swój email i hasło, aby uzyskać dostęp do konta', + keywords: ['zaloguj się', 'hasło', 'email'], + image: { + url: 'https://raw.githubusercontent.com/o2sdev/openselfservice/refs/heads/main/packages/integrations/mocked/public/images/sign-in.jpg', + alternativeText: 'Sign in image', + width: 640, + height: 656, + name: 'Sign in image', + }, + noIndex: false, + noFollow: false, + }, + invalidCredentials: 'Nieprawidłowe dane logowania', +}; + +const LOGIN_PAGE_EN: CMS.Model.LoginPage.LoginPage = { + title: 'Sign in', + subtitle: 'Please enter your email and password below to access your account.', + username: { + id: 'username-1', + name: 'username', + label: 'Username', + placeholder: 'Enter your username', + errorMessages: [ + { + type: 'required', + description: 'Username is required', + id: 'required-1', + name: 'Required', + }, + { + type: 'matches', + description: 'Username can only contain letters, numbers, dots, hyphens and @', + id: 'matches-1', + name: 'Matches', + }, + { + type: 'min', + description: 'Username must contain at least 5 characters', + id: 'min-1', + name: 'Min', + }, + ], + }, + password: { + id: 'password-1', + name: 'password', + label: 'Password', + placeholder: 'Enter your password', + errorMessages: [ + { + type: 'required', + description: 'Password is required', + id: 'required-1', + name: 'Required', + }, + { + type: 'min', + description: 'Password must contain at least 4 characters', + id: 'min-1', + name: 'Min', + }, + { + type: 'matches', + description: 'Password must contain at least 4 characters', + id: 'matches-1', + name: 'Matches', + }, + ], + }, + signIn: 'Sign in', + providers: { + title: 'OR', + label: 'Sign in with ', + }, + createdAt: '2024-01-01', + updatedAt: '2024-01-01', + publishedAt: '2024-01-01', + labels: { + show: 'Show', + hide: 'Hide', + }, + image: { + url: 'https://raw.githubusercontent.com/o2sdev/openselfservice/refs/heads/main/packages/integrations/mocked/public/images/sign-in.jpg', + alternativeText: 'Sign in image', + width: 640, + height: 656, + name: 'Sign in image', + }, + seo: { + title: 'Sign in | Open Self Service demo application', + description: + "Demo app of Open Self Service. Build future-proof Customer Portals with composable architecture and a modern frontend tech stack. Open Self Service offers a Next.js boilerplate, an API integration & data normalization server, and capabilities to integrate headless APIs like CMS, CRM, Search or headless e-commerce. It's powered by Next.js, React.js, TypeScript, and NestJS.", + keywords: [ + 'Open Self Service', + 'open source customer portal', + 'headless customer portal', + 'composable frontend', + 'fullstack framework', + 'composable architecture', + 'MACH', + 'Next.js', + 'TypeScript', + 'NestJS', + 'headless integration', + 'customer portal framework', + 'headless CMS', + 'headless self service', + 'CRM headless frontend', + 'e-commerce API', + 'self-service platform', + 'open-source frontend', + 'composable CX', + ], + image: { + url: 'https://raw.githubusercontent.com/o2sdev/openselfservice/refs/heads/main/packages/integrations/mocked/public/images/o2s-social-card-1.jpg', + alternativeText: 'Open Self Service - The Open Source Composable Frontend for Customer Portals', + width: 640, + height: 656, + name: 'Sign in image', + }, + noIndex: false, + noFollow: false, + }, + invalidCredentials: 'Invalid credentials', +}; + +const LOGIN_PAGE_DE: CMS.Model.LoginPage.LoginPage = { + title: 'Einloggen', + subtitle: 'Geben Sie Ihre E-Mail und Ihr Passwort ein, um auf Ihr Konto zuzugreifen', + username: { + id: 'username-1', + name: 'username', + label: 'Benutzername', + placeholder: 'Geben Sie Ihren Benutzernamen ein', + errorMessages: [ + { + type: 'required', + description: 'Benutzername ist erforderlich', + id: 'required-1', + name: 'Required', + }, + { + type: 'min', + description: 'Benutzername muss mindestens 5 Zeichen lang sein', + id: 'min-1', + name: 'Min', + }, + { + type: 'matches', + description: 'Benutzername kann nur Buchstaben, Zahlen, Punkte, Bindestriche und @ enthalten', + id: 'matches-1', + name: 'Matches', + }, + ], + }, + password: { + id: 'password-1', + name: 'password', + label: 'Passwort', + placeholder: 'Geben Sie Ihr Passwort ein', + errorMessages: [ + { + type: 'required', + description: 'Passwort ist erforderlich', + id: 'required-1', + name: 'Required', + }, + { + type: 'min', + description: 'Passwort muss mindestens 4 Zeichen lang sein', + id: 'min-1', + name: 'Min', + }, + { + type: 'matches', + description: 'Passwort muss mindestens 4 Zeichen lang sein', + id: 'matches-1', + name: 'Matches', + }, + ], + }, + signIn: 'Anmelden', + providers: { + title: 'ODER', + label: 'Anmelden mit ', + }, + createdAt: '2024-01-01', + updatedAt: '2024-01-01', + publishedAt: '2024-01-01', + labels: { + show: 'Anzeigen', + hide: 'Verbergen', + }, + image: { + url: 'https://raw.githubusercontent.com/o2sdev/openselfservice/refs/heads/main/packages/integrations/mocked/public/images/sign-in.jpg', + alternativeText: 'Sign in image', + width: 640, + height: 656, + name: 'Sign in image', + }, + seo: { + title: 'Einloggen', + description: 'Geben Sie Ihre E-Mail und Ihr Passwort ein, um auf Ihr Konto zuzugreifen', + keywords: ['einloggen', 'passwort', 'email'], + image: { + url: 'https://raw.githubusercontent.com/o2sdev/openselfservice/refs/heads/main/packages/integrations/mocked/public/images/sign-in.jpg', + alternativeText: 'Sign in image', + width: 640, + height: 656, + name: 'Sign in image', + }, + noIndex: false, + noFollow: false, + }, + invalidCredentials: 'Ungültige Anmeldeinformationen', +}; + +export const mapLoginPage = (locale: string): CMS.Model.LoginPage.LoginPage => { + switch (locale) { + case 'en': + return LOGIN_PAGE_EN; + case 'de': + return LOGIN_PAGE_DE; + case 'pl': + return LOGIN_PAGE_PL; + } + + return LOGIN_PAGE_EN; +}; diff --git a/packages/integrations/contentful-cms/src/modules/cms/mappers/cms.not-found-page.mapper.ts b/packages/integrations/contentful-cms/src/modules/cms/mappers/cms.not-found-page.mapper.ts new file mode 100644 index 00000000..21d1a788 --- /dev/null +++ b/packages/integrations/contentful-cms/src/modules/cms/mappers/cms.not-found-page.mapper.ts @@ -0,0 +1,35 @@ +import { CMS } from '@o2s/framework/modules'; + +const NOT_FOUND_PAGE_PL: CMS.Model.NotFoundPage.NotFoundPage = { + title: 'Strona nie znaleziona', + description: 'Strona, której szukasz, nie istnieje.', + url: '/', + urlLabel: 'Przejdź do strony głównej', +}; + +const NOT_FOUND_PAGE_EN: CMS.Model.NotFoundPage.NotFoundPage = { + title: 'Page not found', + description: 'The page you are looking for does not exist.', + url: '/', + urlLabel: 'Go to home page', +}; + +const NOT_FOUND_PAGE_DE: CMS.Model.NotFoundPage.NotFoundPage = { + title: 'Seite nicht gefunden', + description: 'Die Seite, die Sie suchen, existiert nicht.', + url: '/', + urlLabel: 'Zur Startseite wechseln', +}; + +export const mapNotFoundPage = (locale: string): CMS.Model.NotFoundPage.NotFoundPage => { + switch (locale) { + case 'en': + return NOT_FOUND_PAGE_EN; + case 'de': + return NOT_FOUND_PAGE_DE; + case 'pl': + return NOT_FOUND_PAGE_PL; + } + + return NOT_FOUND_PAGE_EN; +}; diff --git a/packages/integrations/contentful-cms/src/modules/cms/mappers/cms.page.mapper.ts b/packages/integrations/contentful-cms/src/modules/cms/mappers/cms.page.mapper.ts new file mode 100644 index 00000000..8e2f082f --- /dev/null +++ b/packages/integrations/contentful-cms/src/modules/cms/mappers/cms.page.mapper.ts @@ -0,0 +1,1535 @@ +import { NotFoundException } from '@nestjs/common'; +import { Entry } from 'contentful'; + +import { CMS } from '@o2s/framework/modules'; +import { Models } from '@o2s/framework/modules'; + +import { IBlockFaq, IBlockTicketList, IPageFields, IPageOneColumnTemplate, IPageSeo } from '@/generated/contentful'; + +export type IBlocks = IBlockTicketList | IBlockFaq; + +const PAGE_DASHBOARD_PL: CMS.Model.Page.Page = { + slug: '/', + id: '1', + locale: 'pl', + seo: { + noIndex: false, + noFollow: false, + title: 'Strona główna', + description: 'Strona główna', + keywords: [], + image: { + url: 'https://picsum.photos/150', + width: 150, + height: 150, + alternativeText: 'Placeholder', + }, + }, + hasOwnTitle: false, + parent: { + slug: '', + }, + template: { + __typename: 'TwoColumnTemplate', + slots: { + top: [], + left: [ + { + __typename: 'PaymentsSummaryBlock', + id: 'payments-summary-1', + }, + ], + right: [ + { + __typename: 'TicketRecentBlock', + id: 'ticket-recent-1', + }, + ], + + bottom: [ + { + __typename: 'FaqBlock', + id: '1buaEvtriqqz7diUjOUP4x', + }, + ], + }, + }, + updatedAt: '2025-01-01', +}; + +const PAGE_DASHBOARD_EN: CMS.Model.Page.Page = { + slug: '/', + id: '1', + locale: 'en', + seo: { + noIndex: false, + noFollow: false, + title: 'Dashboard', + description: 'Dashboard', + keywords: [], + image: { + url: 'https://picsum.photos/150', + width: 150, + height: 150, + alternativeText: 'Placeholder', + }, + }, + hasOwnTitle: false, + parent: { + slug: '', + }, + template: { + __typename: 'TwoColumnTemplate', + slots: { + top: [], + left: [ + { + __typename: 'PaymentsSummaryBlock', + id: 'payments-summary-1', + }, + ], + right: [ + { + __typename: 'TicketRecentBlock', + id: 'ticket-recent-1', + }, + ], + + bottom: [ + { + __typename: 'FaqBlock', + id: '1buaEvtriqqz7diUjOUP4x', + }, + ], + }, + }, + updatedAt: '2025-01-01', +}; + +const PAGE_DASHBOARD_DE: CMS.Model.Page.Page = { + slug: '/', + id: '1', + locale: 'de', + seo: { + noIndex: false, + noFollow: false, + title: 'Startseite', + description: 'Startseite', + keywords: [], + image: { + url: 'https://picsum.photos/150', + width: 150, + height: 150, + alternativeText: 'Placeholder', + }, + }, + hasOwnTitle: false, + parent: { + slug: '', + }, + template: { + __typename: 'TwoColumnTemplate', + slots: { + top: [], + left: [ + { + __typename: 'PaymentsSummaryBlock', + id: 'payments-summary-1', + }, + ], + right: [ + { + __typename: 'TicketRecentBlock', + id: 'ticket-recent-1', + }, + ], + + bottom: [ + { + __typename: 'FaqBlock', + id: '1buaEvtriqqz7diUjOUP4x', + }, + ], + }, + }, + updatedAt: '2025-01-01', +}; + +const PAGE_TICKET_LIST_EN: CMS.Model.Page.Page = { + id: '2', + slug: '/cases', + locale: 'en', + seo: { + noIndex: false, + noFollow: false, + title: 'Cases', + description: 'Cases', + keywords: ['cases', 'case', 'casescase'], + image: { + url: 'https://picsum.photos/150', + width: 150, + height: 150, + alternativeText: 'Placeholder', + }, + }, + hasOwnTitle: false, + parent: { + slug: '/', + seo: { + title: 'Dashboard', + }, + }, + template: { + __typename: 'OneColumnTemplate', + slots: { + main: [ + { + __typename: 'TicketListBlock', + id: '2OKt2CbF5fmZZs3HLcTfk8', + }, + { + __typename: 'FaqBlock', + id: '1buaEvtriqqz7diUjOUP4x', + }, + ], + }, + }, + updatedAt: '2025-01-01', +}; + +const PAGE_TICKET_LIST_DE: CMS.Model.Page.Page = { + id: '2', + slug: '/faelle', + locale: 'de', + seo: { + noIndex: false, + noFollow: false, + title: 'Fälle', + description: 'Fälle', + keywords: ['kassen', 'kassenfall', 'kassenfallfall'], + image: { + url: 'https://picsum.photos/150', + width: 150, + height: 150, + alternativeText: 'Placeholder', + }, + }, + hasOwnTitle: false, + parent: { + slug: '/', + seo: { + title: 'Startseite', + }, + }, + template: { + __typename: 'OneColumnTemplate', + slots: { + main: [ + { + __typename: 'TicketListBlock', + id: 'ticket-list-1', + }, + { + __typename: 'FaqBlock', + id: '1buaEvtriqqz7diUjOUP4x', + }, + ], + }, + }, + updatedAt: '2025-01-01', +}; + +const PAGE_TICKET_LIST_PL: CMS.Model.Page.Page = { + id: '2', + slug: '/zgloszenia', + locale: 'pl', + seo: { + noIndex: false, + noFollow: false, + title: 'Zgłoszenia', + description: 'Zgłoszenia', + keywords: ['zgloszenia', 'zgloszenie', 'zgloszeniazgloszenia'], + image: { + url: 'https://picsum.photos/150', + width: 150, + height: 150, + alternativeText: 'Placeholder', + }, + }, + hasOwnTitle: false, + parent: { + slug: '/', + seo: { + title: 'Strona główna', + }, + }, + template: { + __typename: 'OneColumnTemplate', + slots: { + main: [ + { + __typename: 'TicketListBlock', + id: 'ticket-list-1', + }, + { + __typename: 'FaqBlock', + id: '1buaEvtriqqz7diUjOUP4x', + }, + ], + }, + }, + updatedAt: '2025-01-01', +}; + +const PAGE_TICKET_DETAILS_EN: CMS.Model.Page.Page = { + id: '3', + slug: '/cases/(.+)', + locale: 'en', + seo: { + noIndex: false, + noFollow: false, + title: 'Ticket Details', + description: 'Ticket Details', + keywords: [], + image: { + url: 'https://picsum.photos/150', + width: 150, + height: 150, + alternativeText: 'Placeholder', + }, + }, + hasOwnTitle: true, + parent: { + slug: '/cases', + seo: { + title: 'Cases', + }, + parent: { + slug: '/', + seo: { + title: 'Dashboard', + }, + }, + }, + template: { + __typename: 'OneColumnTemplate', + slots: { + main: [ + { + __typename: 'TicketDetailsBlock', + id: 'ticket-details-1', + }, + { + __typename: 'FaqBlock', + id: '1buaEvtriqqz7diUjOUP4x', + }, + ], + }, + }, + updatedAt: '2025-01-01', +}; + +const PAGE_TICKET_DETAILS_DE: CMS.Model.Page.Page = { + id: '3', + slug: '/faelle/(.+)', + locale: 'de', + seo: { + noIndex: false, + noFollow: false, + title: 'Fälle', + description: 'Fälle', + keywords: [], + image: { + url: 'https://picsum.photos/150', + width: 150, + height: 150, + alternativeText: 'Placeholder', + }, + }, + hasOwnTitle: true, + parent: { + slug: '/faelle', + seo: { + title: 'Fälle', + }, + parent: { + slug: '/', + seo: { + title: 'Startseite', + }, + }, + }, + template: { + __typename: 'OneColumnTemplate', + slots: { + main: [ + { + __typename: 'TicketDetailsBlock', + id: 'ticket-details-1', + }, + { + __typename: 'FaqBlock', + id: '1buaEvtriqqz7diUjOUP4x', + }, + ], + }, + }, + updatedAt: '2025-01-01', +}; + +const PAGE_TICKET_DETAILS_PL: CMS.Model.Page.Page = { + id: '3', + slug: '/zgloszenia/(.+)', + locale: 'pl', + seo: { + noIndex: false, + noFollow: false, + title: 'Zgłoszenia', + description: 'Zgłoszenia', + keywords: [], + image: { + url: 'https://picsum.photos/150', + width: 150, + height: 150, + alternativeText: 'Placeholder', + }, + }, + hasOwnTitle: true, + parent: { + slug: '/zgloszenia', + seo: { + title: 'Zgłoszenia', + }, + parent: { + slug: '/', + seo: { + title: 'Strona główna', + }, + }, + }, + template: { + __typename: 'OneColumnTemplate', + slots: { + main: [ + { + __typename: 'TicketDetailsBlock', + id: 'ticket-details-1', + }, + { + __typename: 'FaqBlock', + id: '1buaEvtriqqz7diUjOUP4x', + }, + ], + }, + }, + updatedAt: '2025-01-01', +}; + +const PAGE_NOTIFICATION_LIST_EN: CMS.Model.Page.Page = { + id: '4', + slug: '/notifications', + locale: 'en', + seo: { + noIndex: false, + noFollow: false, + title: 'Notifications', + description: 'Notifications', + keywords: [], + image: { + url: 'https://picsum.photos/150', + width: 150, + height: 150, + alternativeText: 'Placeholder', + }, + }, + hasOwnTitle: false, + parent: { + slug: '/', + seo: { + title: 'Dashboard', + }, + }, + template: { + __typename: 'OneColumnTemplate', + slots: { + main: [ + { + __typename: 'NotificationListBlock', + id: 'notification-list-1', + }, + { + __typename: 'FaqBlock', + id: '1buaEvtriqqz7diUjOUP4x', + }, + ], + }, + }, + updatedAt: '2025-01-01', +}; + +const PAGE_NOTIFICATION_LIST_DE: CMS.Model.Page.Page = { + id: '4', + slug: '/benachrichtigungen', + locale: 'de', + seo: { + noIndex: false, + noFollow: false, + title: 'Benachrichtigungen', + description: 'Benachrichtigungen', + keywords: [], + image: { + url: 'https://picsum.photos/150', + width: 150, + height: 150, + alternativeText: 'Placeholder', + }, + }, + hasOwnTitle: false, + parent: { + slug: '/', + seo: { + title: 'Dashboard', + }, + }, + template: { + __typename: 'OneColumnTemplate', + slots: { + main: [ + { + __typename: 'NotificationListBlock', + id: 'notification-list-1', + }, + { + __typename: 'FaqBlock', + id: '1buaEvtriqqz7diUjOUP4x', + }, + ], + }, + }, + updatedAt: '2025-01-01', +}; + +const PAGE_NOTIFICATION_LIST_PL: CMS.Model.Page.Page = { + id: '4', + slug: '/powiadomienia', + locale: 'pl', + seo: { + noIndex: false, + noFollow: false, + title: 'Powiadomienia', + description: 'Powiadomienia', + keywords: [], + image: { + url: 'https://picsum.photos/150', + width: 150, + height: 150, + alternativeText: 'Placeholder', + }, + }, + hasOwnTitle: false, + parent: { + slug: '/', + seo: { + title: 'Dashboard', + }, + }, + template: { + __typename: 'OneColumnTemplate', + slots: { + main: [ + { + __typename: 'NotificationListBlock', + id: 'notification-list-1', + }, + { + __typename: 'FaqBlock', + id: '1buaEvtriqqz7diUjOUP4x', + }, + ], + }, + }, + updatedAt: '2025-01-01', +}; + +const PAGE_INVOICE_LIST_EN: CMS.Model.Page.Page = { + id: '5', + slug: '/invoices', + locale: 'en', + seo: { + noIndex: false, + noFollow: false, + title: 'Invoices', + description: 'Invoices', + keywords: [], + image: { + url: 'https://picsum.photos/150', + width: 150, + height: 150, + alternativeText: 'Placeholder', + }, + }, + hasOwnTitle: false, + parent: { + slug: '/', + seo: { + title: 'Dashboard', + }, + }, + template: { + __typename: 'TwoColumnTemplate', + slots: { + top: [], + left: [ + { + __typename: 'PaymentsSummaryBlock', + id: 'payments-summary-1', + }, + ], + right: [ + { + __typename: 'PaymentsHistoryBlock', + id: 'payments-history-1', + }, + ], + bottom: [ + { + __typename: 'InvoiceListBlock', + id: 'invoice-list-1', + }, + { + __typename: 'FaqBlock', + id: '1buaEvtriqqz7diUjOUP4x', + }, + ], + }, + }, + updatedAt: '2025-01-01', +}; + +const PAGE_INVOICE_LIST_DE: CMS.Model.Page.Page = { + id: '5', + slug: '/rechnungen', + locale: 'de', + seo: { + noIndex: false, + noFollow: false, + title: 'Rechnungen', + description: 'Rechnungen', + keywords: [], + image: { + url: 'https://picsum.photos/150', + width: 150, + height: 150, + alternativeText: 'Placeholder', + }, + }, + hasOwnTitle: false, + parent: { + slug: '/', + seo: { + title: 'Startseite', + }, + }, + template: { + __typename: 'TwoColumnTemplate', + slots: { + top: [], + left: [ + { + __typename: 'PaymentsSummaryBlock', + id: 'payments-summary-1', + }, + ], + right: [ + { + __typename: 'PaymentsHistoryBlock', + id: 'payments-history-1', + }, + ], + bottom: [ + { + __typename: 'InvoiceListBlock', + id: 'invoice-list-1', + }, + { + __typename: 'FaqBlock', + id: '1buaEvtriqqz7diUjOUP4x', + }, + ], + }, + }, + updatedAt: '2025-01-01', +}; + +const PAGE_INVOICE_LIST_PL: CMS.Model.Page.Page = { + id: '5', + slug: '/rachunki', + locale: 'pl', + seo: { + noIndex: false, + noFollow: false, + title: 'Rachunki', + description: 'Rachunki', + keywords: [], + image: { + url: 'https://picsum.photos/150', + width: 150, + height: 150, + alternativeText: 'Placeholder', + }, + }, + hasOwnTitle: false, + parent: { + slug: '/', + seo: { + title: 'Strona główna', + }, + }, + template: { + __typename: 'TwoColumnTemplate', + slots: { + top: [], + left: [ + { + __typename: 'PaymentsSummaryBlock', + id: 'payments-summary-1', + }, + ], + right: [ + { + __typename: 'PaymentsHistoryBlock', + id: 'payments-history-1', + }, + ], + bottom: [ + { + __typename: 'InvoiceListBlock', + id: 'invoice-list-1', + }, + { + __typename: 'FaqBlock', + id: '1buaEvtriqqz7diUjOUP4x', + }, + ], + }, + }, + updatedAt: '2025-01-01', +}; + +const PAGE_NOTIFICATION_DETAILS_EN: CMS.Model.Page.Page = { + id: '6', + slug: '/notifications/(.+)', + locale: 'en', + seo: { + noIndex: false, + noFollow: false, + title: 'Notification Details', + description: 'Notification Details', + keywords: [], + image: { + url: 'https://picsum.photos/150', + width: 150, + height: 150, + alternativeText: 'Placeholder', + }, + }, + hasOwnTitle: true, + parent: { + slug: '/notifications', + seo: { + title: 'Notifications', + }, + parent: { + slug: '/', + seo: { + title: 'Dashboard', + }, + }, + }, + template: { + __typename: 'OneColumnTemplate', + slots: { + main: [ + { + __typename: 'NotificationDetailsBlock', + id: 'notification-details-1', + }, + { + __typename: 'FaqBlock', + id: '1buaEvtriqqz7diUjOUP4x', + }, + ], + }, + }, + updatedAt: '2025-01-01', +}; + +const PAGE_NOTIFICATION_DETAILS_DE: CMS.Model.Page.Page = { + id: '6', + slug: '/benachrichtigungen/(.+)', + locale: 'de', + seo: { + noIndex: false, + noFollow: false, + title: 'Benachrichtigung Details', + description: 'Benachrichtigung Details', + keywords: [], + image: { + url: 'https://picsum.photos/150', + width: 150, + height: 150, + alternativeText: 'Placeholder', + }, + }, + hasOwnTitle: true, + parent: { + slug: '/benachrichtigungen', + seo: { + title: 'Benachrichtigungen', + }, + parent: { + slug: '/', + seo: { + title: 'Startseite', + }, + }, + }, + template: { + __typename: 'OneColumnTemplate', + slots: { + main: [ + { + __typename: 'NotificationDetailsBlock', + id: 'notification-details-1', + }, + { + __typename: 'FaqBlock', + id: '1buaEvtriqqz7diUjOUP4x', + }, + ], + }, + }, + updatedAt: '2025-01-01', +}; + +const PAGE_NOTIFICATION_DETAILS_PL: CMS.Model.Page.Page = { + id: '6', + slug: '/powiadomienia/(.+)', + locale: 'pl', + seo: { + noIndex: false, + noFollow: false, + title: 'Powiadomienia', + description: 'Powiadomienia', + keywords: [], + image: { + url: 'https://picsum.photos/150', + width: 150, + height: 150, + alternativeText: 'Placeholder', + }, + }, + hasOwnTitle: true, + parent: { + slug: '/powiadomienia', + seo: { + title: 'Powiadomienia', + }, + parent: { + slug: '/', + seo: { + title: 'Strona główna', + }, + }, + }, + template: { + __typename: 'OneColumnTemplate', + slots: { + main: [ + { + __typename: 'NotificationDetailsBlock', + id: 'notification-details-1', + }, + { + __typename: 'FaqBlock', + id: '1buaEvtriqqz7diUjOUP4x', + }, + ], + }, + }, + updatedAt: '2025-01-01', +}; + +const PAGE_USER_ACCOUNT_EN: CMS.Model.Page.Page = { + id: '7', + slug: '/user-account', + locale: 'en', + seo: { + noIndex: false, + noFollow: false, + title: 'User Account', + description: 'User Account', + keywords: [], + image: { + url: 'https://picsum.photos/150', + width: 150, + height: 150, + alternativeText: 'Placeholder', + }, + }, + hasOwnTitle: true, + parent: { + slug: '/', + seo: { + title: 'Dashboard', + }, + }, + template: { + __typename: 'OneColumnTemplate', + slots: { + main: [ + { + __typename: 'UserAccountBlock', + id: 'user-account-1', + }, + ], + }, + }, + updatedAt: '2025-01-01', +}; + +const PAGE_USER_ACCOUNT_DE: CMS.Model.Page.Page = { + id: '7', + slug: '/benutzerkonto', + locale: 'de', + seo: { + noIndex: false, + noFollow: false, + title: 'Benutzerkonto', + description: 'Benutzerkonto', + keywords: [], + image: { + url: 'https://picsum.photos/150', + width: 150, + height: 150, + alternativeText: 'Placeholder', + }, + }, + hasOwnTitle: true, + parent: { + slug: '/', + seo: { + title: 'Startseite', + }, + }, + template: { + __typename: 'OneColumnTemplate', + slots: { + main: [ + { + __typename: 'UserAccountBlock', + id: 'user-account-1', + }, + ], + }, + }, + updatedAt: '2025-01-01', +}; + +const PAGE_USER_ACCOUNT_PL: CMS.Model.Page.Page = { + id: '7', + slug: '/konto-uzytkownika', + locale: 'pl', + seo: { + noIndex: false, + noFollow: false, + title: 'Konto użytkownika', + description: 'Konto użytkownika', + keywords: [], + image: { + url: 'https://picsum.photos/150', + width: 150, + height: 150, + alternativeText: 'Placeholder', + }, + }, + hasOwnTitle: true, + parent: { + slug: '/', + seo: { + title: 'Strona główna', + }, + }, + template: { + __typename: 'OneColumnTemplate', + slots: { + main: [ + { + __typename: 'UserAccountBlock', + id: 'user-account-1', + }, + ], + }, + }, + updatedAt: '2025-01-01', +}; + +const PAGE_SERVICE_LIST_EN: CMS.Model.Page.Page = { + id: '8', + slug: '/services', + locale: 'en', + seo: { + noIndex: false, + noFollow: false, + title: 'Services', + description: 'Services', + keywords: [], + image: { + url: 'https://picsum.photos/150', + width: 150, + height: 150, + alternativeText: 'Placeholder', + }, + }, + hasOwnTitle: false, + parent: { + slug: '/', + seo: { + title: 'Dashboard', + }, + }, + template: { + __typename: 'OneColumnTemplate', + slots: { + main: [ + { + __typename: 'ServiceListBlock', + id: 'service-list-1', + }, + { + __typename: 'FaqBlock', + id: '1buaEvtriqqz7diUjOUP4x', + }, + ], + }, + }, + updatedAt: '2025-01-01', +}; + +const PAGE_SERVICE_LIST_DE: CMS.Model.Page.Page = { + id: '8', + slug: '/dienstleistungen', + locale: 'de', + seo: { + noIndex: false, + noFollow: false, + title: 'Dienstleistungen', + description: 'Dienstleistungen', + keywords: [], + image: { + url: 'https://picsum.photos/150', + width: 150, + height: 150, + alternativeText: 'Placeholder', + }, + }, + hasOwnTitle: false, + parent: { + slug: '/', + seo: { + title: 'Startseite', + }, + }, + template: { + __typename: 'OneColumnTemplate', + slots: { + main: [ + { + __typename: 'ServiceListBlock', + id: 'service-list-1', + }, + { + __typename: 'FaqBlock', + id: '1buaEvtriqqz7diUjOUP4x', + }, + ], + }, + }, + updatedAt: '2025-01-01', +}; + +const PAGE_SERVICE_LIST_PL: CMS.Model.Page.Page = { + id: '8', + slug: '/uslugi', + locale: 'pl', + seo: { + noIndex: false, + noFollow: false, + title: 'Usługi', + description: 'Usługi', + keywords: [], + image: { + url: 'https://picsum.photos/150', + width: 150, + height: 150, + alternativeText: 'Placeholder', + }, + }, + hasOwnTitle: false, + parent: { + slug: '/', + seo: { + title: 'Strona główna', + }, + }, + template: { + __typename: 'OneColumnTemplate', + slots: { + main: [ + { + __typename: 'ServiceListBlock', + id: 'service-list-1', + }, + { + __typename: 'FaqBlock', + id: '1buaEvtriqqz7diUjOUP4x', + }, + ], + }, + }, + updatedAt: '2025-01-01', +}; + +const PAGE_SERVICE_DETAILS_EN: CMS.Model.Page.Page = { + id: '3', + slug: '/services/(.+)', + locale: 'en', + seo: { + noIndex: false, + noFollow: false, + title: 'Service Details', + description: 'Service Details', + keywords: [], + image: { + url: 'https://picsum.photos/150', + width: 150, + height: 150, + alternativeText: 'Placeholder', + }, + }, + hasOwnTitle: true, + parent: { + slug: '/services', + seo: { + title: 'Services', + }, + parent: { + slug: '/', + seo: { + title: 'Dashboard', + }, + }, + }, + template: { + __typename: 'OneColumnTemplate', + slots: { + main: [ + { + __typename: 'ServiceDetailsBlock', + id: 'service-details-1', + }, + { + __typename: 'FaqBlock', + id: '1buaEvtriqqz7diUjOUP4x', + }, + ], + }, + }, + updatedAt: '2025-01-01', +}; + +const PAGE_SERVICE_DETAILS_DE: CMS.Model.Page.Page = { + id: '3', + slug: '/dienstleistungen/(.+)', + locale: 'de', + seo: { + noIndex: false, + noFollow: false, + title: 'Dienstleistungen', + description: 'Dienstleistungen', + keywords: [], + image: { + url: 'https://picsum.photos/150', + width: 150, + height: 150, + alternativeText: 'Placeholder', + }, + }, + hasOwnTitle: true, + parent: { + slug: '/dienstleistungen', + seo: { + title: 'Dienstleistungen', + }, + parent: { + slug: '/', + seo: { + title: 'Startseite', + }, + }, + }, + template: { + __typename: 'OneColumnTemplate', + slots: { + main: [ + { + __typename: 'ServiceDetailsBlock', + id: 'service-details-1', + }, + { + __typename: 'FaqBlock', + id: '1buaEvtriqqz7diUjOUP4x', + }, + ], + }, + }, + updatedAt: '2025-01-01', +}; + +const PAGE_SERVICE_DETAILS_PL: CMS.Model.Page.Page = { + id: '3', + slug: '/uslugi/(.+)', + locale: 'pl', + seo: { + noIndex: false, + noFollow: false, + title: 'Usługi', + description: 'Usługi', + keywords: [], + image: { + url: 'https://picsum.photos/150', + width: 150, + height: 150, + alternativeText: 'Placeholder', + }, + }, + hasOwnTitle: true, + parent: { + slug: '/uslugi', + seo: { + title: 'Usługi', + }, + parent: { + slug: '/', + seo: { + title: 'Strona główna', + }, + }, + }, + template: { + __typename: 'OneColumnTemplate', + slots: { + main: [ + { + __typename: 'ServiceDetailsBlock', + id: 'service-details-1', + }, + { + __typename: 'FaqBlock', + id: '1buaEvtriqqz7diUjOUP4x', + }, + ], + }, + }, + updatedAt: '2025-01-01', +}; + +export const mapMockPage = (slug: string, locale: string): CMS.Model.Page.Page | undefined => { + switch (slug) { + case '/': + return locale === 'pl' ? PAGE_DASHBOARD_PL : locale === 'de' ? PAGE_DASHBOARD_DE : PAGE_DASHBOARD_EN; + + case '/zgloszenia': + return PAGE_TICKET_LIST_PL; + + case '/faelle': + return PAGE_TICKET_LIST_DE; + + case '/cases': + return PAGE_TICKET_LIST_EN; + + case slug.match(/\/cases\/.+/)?.[0]: + return { + ...PAGE_TICKET_DETAILS_EN, + slug: `/cases/${slug.match(/(.+)\/(.+)/)?.[2]}`, + updatedAt: '2025-01-01', + }; + case slug.match(/\/faelle\/.+/)?.[0]: + return { + ...PAGE_TICKET_DETAILS_DE, + slug: `/faelle/${slug.match(/(.+)\/(.+)/)?.[2]}`, + updatedAt: '2025-01-01', + }; + case slug.match(/\/zgloszenia\/.+/)?.[0]: + return { + ...PAGE_TICKET_DETAILS_PL, + slug: `/zgloszenia/${slug.match(/(.+)\/(.+)/)?.[2]}`, + updatedAt: '2025-01-01', + }; + + case '/notifications': + return PAGE_NOTIFICATION_LIST_EN; + case '/benachrichtigungen': + return PAGE_NOTIFICATION_LIST_DE; + case '/powiadomienia': + return PAGE_NOTIFICATION_LIST_PL; + + case slug.match(/\/notifications\/.+/)?.[0]: + return { + ...PAGE_NOTIFICATION_DETAILS_EN, + slug: `/notifications/${slug.match(/(.+)\/(.+)/)?.[2]}`, + updatedAt: '2025-01-01', + }; + + case slug.match(/\/benachrichtigungen\/.+/)?.[0]: + return { + ...PAGE_NOTIFICATION_DETAILS_DE, + slug: `/benachrichtigungen/${slug.match(/(.+)\/(.+)/)?.[2]}`, + updatedAt: '2025-01-01', + }; + case slug.match(/\/powiadomienia\/.+/)?.[0]: + return { + ...PAGE_NOTIFICATION_DETAILS_PL, + slug: `/powiadomienia/${slug.match(/(.+)\/(.+)/)?.[2]}`, + updatedAt: '2025-01-01', + }; + + case '/invoices': + return PAGE_INVOICE_LIST_EN; + case '/rechnungen': + return PAGE_INVOICE_LIST_DE; + case '/rachunki': + return PAGE_INVOICE_LIST_PL; + + case '/user-account': + return PAGE_USER_ACCOUNT_EN; + case '/benutzerkonto': + return PAGE_USER_ACCOUNT_DE; + case '/konto-uzytkownika': + return PAGE_USER_ACCOUNT_PL; + + case '/services': + return PAGE_SERVICE_LIST_EN; + case '/dienstleistungen': + return PAGE_SERVICE_LIST_DE; + case '/uslugi': + return PAGE_SERVICE_LIST_PL; + + case slug.match(/\/services\/.+/)?.[0]: + return { + ...PAGE_SERVICE_DETAILS_EN, + slug: `/services/${slug.match(/(.+)\/(.+)/)?.[2]}`, + updatedAt: '2025-01-01', + }; + case slug.match(/\/dienstleistungen\/.+/)?.[0]: + return { + ...PAGE_SERVICE_DETAILS_DE, + slug: `/dienstleistungen/${slug.match(/(.+)\/(.+)/)?.[2]}`, + updatedAt: '2025-01-01', + }; + case slug.match(/\/uslugi\/.+/)?.[0]: + return { + ...PAGE_SERVICE_DETAILS_PL, + slug: `/uslugi/${slug.match(/(.+)\/(.+)/)?.[2]}`, + updatedAt: '2025-01-01', + }; + default: + return undefined; + } +}; + +export const getAllPages = (locale: string): CMS.Model.Page.Page[] => { + switch (locale) { + case 'pl': + return [ + PAGE_DASHBOARD_PL, + PAGE_TICKET_LIST_PL, + PAGE_TICKET_DETAILS_PL, + PAGE_NOTIFICATION_LIST_PL, + PAGE_NOTIFICATION_DETAILS_PL, + PAGE_INVOICE_LIST_PL, + PAGE_USER_ACCOUNT_PL, + PAGE_SERVICE_LIST_PL, + PAGE_SERVICE_DETAILS_PL, + ]; + case 'de': + return [ + PAGE_DASHBOARD_DE, + PAGE_TICKET_LIST_DE, + PAGE_TICKET_DETAILS_DE, + PAGE_NOTIFICATION_LIST_DE, + PAGE_NOTIFICATION_DETAILS_DE, + PAGE_INVOICE_LIST_DE, + PAGE_USER_ACCOUNT_DE, + PAGE_SERVICE_LIST_DE, + PAGE_SERVICE_DETAILS_DE, + ]; + case 'en': + return [ + PAGE_DASHBOARD_EN, + PAGE_TICKET_LIST_EN, + PAGE_TICKET_DETAILS_EN, + PAGE_NOTIFICATION_LIST_EN, + PAGE_NOTIFICATION_DETAILS_EN, + PAGE_INVOICE_LIST_EN, + PAGE_USER_ACCOUNT_EN, + PAGE_SERVICE_LIST_EN, + PAGE_SERVICE_DETAILS_EN, + ]; + default: + return []; + } +}; + +export const getAlternativePages = (id: string, slug: string, locale: string): CMS.Model.Page.Page[] => { + return [ + PAGE_DASHBOARD_PL, + PAGE_TICKET_LIST_PL, + PAGE_TICKET_DETAILS_PL, + PAGE_NOTIFICATION_LIST_PL, + PAGE_NOTIFICATION_DETAILS_PL, + PAGE_INVOICE_LIST_PL, + PAGE_USER_ACCOUNT_PL, + PAGE_SERVICE_LIST_PL, + PAGE_DASHBOARD_DE, + PAGE_TICKET_LIST_DE, + PAGE_TICKET_DETAILS_DE, + PAGE_NOTIFICATION_LIST_DE, + PAGE_NOTIFICATION_DETAILS_DE, + PAGE_INVOICE_LIST_DE, + PAGE_USER_ACCOUNT_DE, + PAGE_SERVICE_LIST_DE, + PAGE_DASHBOARD_EN, + PAGE_TICKET_LIST_EN, + PAGE_TICKET_DETAILS_EN, + PAGE_NOTIFICATION_LIST_EN, + PAGE_NOTIFICATION_DETAILS_EN, + PAGE_INVOICE_LIST_EN, + PAGE_USER_ACCOUNT_EN, + PAGE_SERVICE_LIST_EN, + PAGE_SERVICE_DETAILS_EN, + PAGE_SERVICE_DETAILS_DE, + PAGE_SERVICE_DETAILS_PL, + ] + .filter((page) => page.id === id) + .map((page) => mapMockPage(page.slug, locale)!) + .map((page) => { + return { + ...page, + slug: page.slug.replace('(.+)', slug.match(/(.+)\/(.+)/)?.[2] || ''), + }; + }); +}; + +export const mapPage = (entryPage: Entry): CMS.Model.Page.Page => { + const template = mapTemplate(entryPage.fields.template); + + if (!template) throw new NotFoundException(); + + const seo = mapSeo(entryPage.fields.seo); + + return { + id: entryPage.sys.id, + slug: entryPage.fields.slug, + locale: entryPage.sys.locale, + template: template, + updatedAt: entryPage.sys.updatedAt, + seo: seo, + hasOwnTitle: entryPage.fields.hasOwnTitle, + parent: { + slug: entryPage.fields.parent?.fields.slug ?? '', + seo: entryPage.fields.parent?.fields.seo ? mapSeo(entryPage.fields.parent?.fields.seo) : undefined, + parent: entryPage.fields.parent?.fields.parent + ? { + slug: entryPage.fields.parent?.fields.parent?.fields.slug ?? '', + seo: entryPage.fields.parent?.fields.parent?.fields.seo + ? mapSeo(entryPage.fields.parent?.fields.parent?.fields.seo) + : undefined, + parent: entryPage.fields.parent?.fields.parent?.fields.parent + ? { + slug: entryPage.fields.parent?.fields.parent?.fields.parent?.fields.slug ?? '', + seo: entryPage.fields.parent?.fields.parent?.fields.parent?.fields.seo + ? mapSeo(entryPage.fields.parent?.fields.parent?.fields.parent?.fields.seo) + : undefined, + } + : undefined, + } + : undefined, + }, + }; +}; + +const mapSeo = (seo?: IPageSeo | undefined): Models.SEO.Page => { + if (!seo) throw new NotFoundException(); + + return { + title: seo.fields.title ?? '', + noIndex: seo.fields.noIndex ?? false, + noFollow: seo.fields.noFollow ?? false, + description: seo.fields.description ?? '', + keywords: seo.fields.keywords || [], + // TODO: implement image + // image: seo?.fields.image, + }; +}; + +const mapTemplate = (template?: IPageOneColumnTemplate): CMS.Model.Page.PageTemplate => { + if (!template) throw new NotFoundException(); + + switch (template.sys.contentType.sys.id) { + case 'pageOneColumnTemplate': + return { + __typename: 'OneColumnTemplate', + slots: { + main: mapSlot(template.fields.mainSlot), + }, + }; + // TODO: add two column template + // case 'pageTwoColumnTemplate': + // return { + // __typename: 'TwoColumnTemplate', + // slots: { + // top: mapSlot(template.fields.topSlot), + // left: mapSlot(template.fields.leftSlot), + // right: mapSlot(template.fields.rightSlot), + // bottom: mapSlot(template.fields.bottomSlot), + // }, + // }; + } +}; + +const mapSlot = (slot: IBlocks[]): CMS.Model.Page.SlotBlock[] => { + return slot.reduce((acc, component) => { + const __typename = mapComponent(component); + + if (!__typename) return acc; + + return [ + ...acc, + { + __typename, + id: component.sys.id, + }, + ]; + }, [] as CMS.Model.Page.SlotBlock[]); +}; + +// TODO: check where component names should be defined, currently they are placed in the api-harmonization so we cannot access them here +const mapComponent = (component: IBlocks) => { + switch (component.sys.contentType.sys.id) { + case 'blockFaq': + return 'FaqBlock'; + case 'blockTicketList': + return 'TicketListBlock'; + } +}; diff --git a/packages/integrations/contentful-cms/src/modules/cms/mappers/cms.pagination.mapper.ts b/packages/integrations/contentful-cms/src/modules/cms/mappers/cms.pagination.mapper.ts new file mode 100644 index 00000000..d10e56c1 --- /dev/null +++ b/packages/integrations/contentful-cms/src/modules/cms/mappers/cms.pagination.mapper.ts @@ -0,0 +1,19 @@ +import { Entry } from 'contentful'; + +import { Models } from '@o2s/framework/modules'; + +import { IComponentPaginationFields } from '@/generated/contentful'; + +export const mapPagination = ( + component?: Entry, +): Models.Pagination.Pagination | undefined => { + if (!component) return undefined; + + return { + limit: component.fields.perPage, + legend: component.fields.description, + prev: component.fields.previousLabel, + next: component.fields.nextLabel, + selectPage: component.fields.selectPageLabel, + }; +}; diff --git a/packages/integrations/contentful-cms/src/modules/cms/mappers/cms.table.mapper.ts b/packages/integrations/contentful-cms/src/modules/cms/mappers/cms.table.mapper.ts new file mode 100644 index 00000000..429d23b0 --- /dev/null +++ b/packages/integrations/contentful-cms/src/modules/cms/mappers/cms.table.mapper.ts @@ -0,0 +1,24 @@ +import { Entry } from 'contentful'; + +import { Models } from '@o2s/framework/modules'; + +import { IComponentTableFields } from '@/generated/contentful'; + +export const mapTable = (component: Entry): Models.DataTable.DataTable => { + const { fields } = component; + + return { + columns: fields.columns.map( + (column): Models.DataTable.DataTableColumn => ({ + id: column.fields.field as keyof T, + title: column.fields.title, + }), + ), + actions: component.fields.actionsTitle + ? { + title: fields.actionsTitle, + label: fields.actionsLabel, + } + : undefined, + }; +}; diff --git a/packages/integrations/contentful-cms/src/modules/contentful/contentful.module.ts b/packages/integrations/contentful-cms/src/modules/contentful/contentful.module.ts new file mode 100644 index 00000000..46b33cf8 --- /dev/null +++ b/packages/integrations/contentful-cms/src/modules/contentful/contentful.module.ts @@ -0,0 +1,10 @@ +import { Global, Module } from '@nestjs/common'; + +import { ContentfulService } from './contentful.service'; + +@Global() +@Module({ + providers: [ContentfulService], + exports: [ContentfulService], +}) +export class ContentfulModule {} diff --git a/packages/integrations/contentful-cms/src/modules/contentful/contentful.service.ts b/packages/integrations/contentful-cms/src/modules/contentful/contentful.service.ts new file mode 100644 index 00000000..30dfeead --- /dev/null +++ b/packages/integrations/contentful-cms/src/modules/contentful/contentful.service.ts @@ -0,0 +1,42 @@ +import { Global, Injectable } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { Asset, ContentfulClientApi, Entry, EntryCollection, LocaleCollection, createClient } from 'contentful'; +import { Observable, from } from 'rxjs'; + +export interface ContentfulServiceProps { + getClient: ContentfulClientApi; +} + +@Global() +@Injectable() +export class ContentfulService implements ContentfulServiceProps { + client: ContentfulClientApi; + + constructor(private readonly config: ConfigService) { + this.client = createClient({ + accessToken: this.config.get('CF_TOKEN') || '', + space: this.config.get('CF_SPACE_ID') || '', + environment: this.config.get('CF_ENV') || '', + }); + } + + get getClient() { + return this.client; + } + + public getEntry(id: string, query?: unknown): Observable> { + return from(this.client.getEntry(id, query)); + } + + public findEntries(query: unknown): Observable> { + return from(this.client.getEntries(query)); + } + + public getAsset(id: string, query?: unknown): Observable { + return from(this.client.getAsset(id, query)); + } + + public getLocales(): Observable { + return from(this.client.getLocales()); + } +} diff --git a/packages/integrations/contentful-cms/src/modules/contentful/index.ts b/packages/integrations/contentful-cms/src/modules/contentful/index.ts new file mode 100644 index 00000000..72e7c49e --- /dev/null +++ b/packages/integrations/contentful-cms/src/modules/contentful/index.ts @@ -0,0 +1,2 @@ +export { ContentfulModule as Module } from './contentful.module.js'; +export { ContentfulService as Service } from './contentful.service.js'; diff --git a/packages/integrations/contentful-cms/src/modules/index.ts b/packages/integrations/contentful-cms/src/modules/index.ts new file mode 100644 index 00000000..d2b510a7 --- /dev/null +++ b/packages/integrations/contentful-cms/src/modules/index.ts @@ -0,0 +1,2 @@ +export * as CMS from './cms'; +export * as Contentful from './contentful'; diff --git a/packages/integrations/contentful-cms/tsconfig.json b/packages/integrations/contentful-cms/tsconfig.json new file mode 100644 index 00000000..9ecfb1a2 --- /dev/null +++ b/packages/integrations/contentful-cms/tsconfig.json @@ -0,0 +1,12 @@ +{ + "extends": "@o2s/typescript-config/api.json", + "compilerOptions": { + "paths": { + "@/*": ["./src/*"], + "@/generated/*": ["./generated/*"] + }, + "outDir": "./dist" + }, + "include": ["src", "*.ts", "generated/*"], + "exclude": ["node_modules", "dist"] +} diff --git a/packages/integrations/contentful-cms/tsconfig.lint.json b/packages/integrations/contentful-cms/tsconfig.lint.json new file mode 100644 index 00000000..102692d6 --- /dev/null +++ b/packages/integrations/contentful-cms/tsconfig.lint.json @@ -0,0 +1,8 @@ +{ + "extends": "@o2s/typescript-config/ui.json", + "compilerOptions": { + "outDir": "dist" + }, + "include": ["src", "turbo"], + "exclude": ["node_modules", "dist"] +} diff --git a/packages/integrations/contentful-cms/turbo.json b/packages/integrations/contentful-cms/turbo.json new file mode 100644 index 00000000..6dac6aae --- /dev/null +++ b/packages/integrations/contentful-cms/turbo.json @@ -0,0 +1,11 @@ +{ + "extends": ["//"], + "tasks": { + "dev": { + "dependsOn": ["@o2s/utils.logger#build", "@o2s/framework#build"] + }, + "build": { + "dependsOn": ["@o2s/utils.logger#build", "@o2s/framework#build"] + } + } +}