added "news" section to billboard

This commit is contained in:
Deltora72 2024-09-28 17:25:25 +03:30
parent b6cc143511
commit e344eacc61
52 changed files with 1597 additions and 30 deletions

8
custom.d.ts vendored
View File

@ -8,4 +8,12 @@ declare module 'react' {
declare global {
function gtag(...args: any[]): void;
}
declare module 'colorthief/dist/color-thief.mjs' {
class ColorThief {
getColor(img: HTMLImageElement, quality?: number): [number, number, number];
getPalette(img: HTMLImageElement, colorCount?: number, quality?: number): [number, number, number][];
}
export default ColorThief;
}

470
package-lock.json generated
View File

@ -23,6 +23,7 @@
"@types/react-redux": "^7.1.23",
"axios": "^1.7.2",
"bcrypt": "^5.1.1",
"colorthief": "^2.4.0",
"concurrently": "^7.6.0",
"cookie": "^0.6.0",
"cors": "^2.8.5",
@ -3889,6 +3890,12 @@
"@jridgewell/sourcemap-codec": "^1.4.14"
}
},
"node_modules/@lokesh.dhakar/quantize": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/@lokesh.dhakar/quantize/-/quantize-1.3.0.tgz",
"integrity": "sha512-4KBSyaMj65d8A+2vnzLxtHFu4OmBU4IKO0yLxZ171Itdf9jGV4w+WbG7VsKts2jUdRkFSzsZqpZOz6hTB3qGAw==",
"license": "MIT"
},
"node_modules/@mapbox/node-pre-gyp": {
"version": "1.0.11",
"resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.11.tgz",
@ -8312,6 +8319,24 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/asn1": {
"version": "0.2.6",
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz",
"integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==",
"license": "MIT",
"dependencies": {
"safer-buffer": "~2.1.0"
}
},
"node_modules/assert-plus": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
"integrity": "sha512-NfJ4UzBCcQGLDlQq7nHxH+tv3kyZ0hHQqF5BO6J7tNJeP5do1llPr8dZ8zHonfhAu0PHAdMkSo+8o0wxg9lZWw==",
"license": "MIT",
"engines": {
"node": ">=0.8"
}
},
"node_modules/ast-types": {
"version": "0.14.2",
"resolved": "https://registry.npmjs.org/ast-types/-/ast-types-0.14.2.tgz",
@ -8429,6 +8454,21 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/aws-sign2": {
"version": "0.7.0",
"resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
"integrity": "sha512-08kcGqnYf/YmjoRhfxyu+CLxBjUtHLXLXX/vUfx9l2LYzG3c1m61nrpyFUZI6zeS+Li/wWMMidD9KgrqtGq3mA==",
"license": "Apache-2.0",
"engines": {
"node": "*"
}
},
"node_modules/aws4": {
"version": "1.13.2",
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.13.2.tgz",
"integrity": "sha512-lHe62zvbTB5eEABUVi/AwVh0ZKY9rMMDhmm+eeyuuUQbQ3+J+fONVQOZyj+DdrvD4BY33uYniyRJ4UJIaSKAfw==",
"license": "MIT"
},
"node_modules/axe-core": {
"version": "4.9.1",
"resolved": "https://registry.npmjs.org/axe-core/-/axe-core-4.9.1.tgz",
@ -8743,6 +8783,15 @@
"node": ">= 10.0.0"
}
},
"node_modules/bcrypt-pbkdf": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
"integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==",
"license": "BSD-3-Clause",
"dependencies": {
"tweetnacl": "^0.14.3"
}
},
"node_modules/big.js": {
"version": "5.2.2",
"resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
@ -9313,6 +9362,12 @@
],
"license": "CC-BY-4.0"
},
"node_modules/caseless": {
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
"integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==",
"license": "Apache-2.0"
},
"node_modules/chalk": {
"version": "2.4.2",
"resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz",
@ -9677,6 +9732,16 @@
"color-support": "bin.js"
}
},
"node_modules/colorthief": {
"version": "2.4.0",
"resolved": "https://registry.npmjs.org/colorthief/-/colorthief-2.4.0.tgz",
"integrity": "sha512-0U48RGNRo5fVO+yusBwgp+d3augWSorXabnqXUu9SabEhCpCgZJEUjUTTI41OOBBYuMMxawa3177POT6qLfLeQ==",
"license": "MIT",
"dependencies": {
"@lokesh.dhakar/quantize": "^1.3.0",
"get-pixels": "^3.3.2"
}
},
"node_modules/combined-stream": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
@ -10054,6 +10119,15 @@
"integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==",
"license": "MIT"
},
"node_modules/cwise-compiler": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/cwise-compiler/-/cwise-compiler-1.1.3.tgz",
"integrity": "sha512-WXlK/m+Di8DMMcCjcWr4i+XzcQra9eCdXIJrgh4TUgh0pIS/yJduLxS9JgefsHJ/YVLdgPtXm9r62W92MvanEQ==",
"license": "MIT",
"dependencies": {
"uniq": "^1.0.0"
}
},
"node_modules/damerau-levenshtein": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz",
@ -10061,6 +10135,18 @@
"dev": true,
"license": "BSD-2-Clause"
},
"node_modules/dashdash": {
"version": "1.14.1",
"resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
"integrity": "sha512-jRFi8UDGo6j+odZiEpjazZaWqEal3w/basFjQHQEwVtZJGDpxbH1MeYluwCS8Xq5wmLJooDlMgvVarmWfGM44g==",
"license": "MIT",
"dependencies": {
"assert-plus": "^1.0.0"
},
"engines": {
"node": ">=0.10"
}
},
"node_modules/data-uri-to-buffer": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz",
@ -10771,6 +10857,22 @@
"integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
"license": "MIT"
},
"node_modules/ecc-jsbn": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
"integrity": "sha512-eh9O+hwRHNbG4BLTjEl3nw044CkGm5X6LoaCf7LPp7UU8Qrt47JYNi6nPX8xjW97TKGKm1ouctg0QSpZe9qrnw==",
"license": "MIT",
"dependencies": {
"jsbn": "~0.1.0",
"safer-buffer": "^2.1.0"
}
},
"node_modules/ecc-jsbn/node_modules/jsbn": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
"integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==",
"license": "MIT"
},
"node_modules/edge-runtime": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/edge-runtime/-/edge-runtime-2.1.4.tgz",
@ -12675,8 +12777,7 @@
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
"license": "MIT",
"peer": true
"license": "MIT"
},
"node_modules/external-editor": {
"version": "3.1.0",
@ -12693,6 +12794,15 @@
"node": ">=4"
}
},
"node_modules/extsprintf": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
"integrity": "sha512-11Ndz7Nv+mvAC1j0ktTa7fAb0vLyGGX+rMHNBYQviQDGU0Hw7lhctJANqbPhu9nV9/izT/IntTgZ7Im/9LJs9g==",
"engines": [
"node >=0.6.0"
],
"license": "MIT"
},
"node_modules/fast-deep-equal": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
@ -12987,6 +13097,15 @@
"url": "https://github.com/sponsors/isaacs"
}
},
"node_modules/forever-agent": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
"integrity": "sha512-j0KLYPhm6zeac4lz3oJ3o65qvgQCcPubiyotZrXqEaG4hNagNYO8qdlUrX5vwqv9ohqeT/Z3j6+yW067yWWdUw==",
"license": "Apache-2.0",
"engines": {
"node": "*"
}
},
"node_modules/form-data": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz",
@ -13324,6 +13443,31 @@
"node": ">=8.0.0"
}
},
"node_modules/get-pixels": {
"version": "3.3.3",
"resolved": "https://registry.npmjs.org/get-pixels/-/get-pixels-3.3.3.tgz",
"integrity": "sha512-5kyGBn90i9tSMUVHTqkgCHsoWoR+/lGbl4yC83Gefyr0HLIhgSWEx/2F/3YgsZ7UpYNuM6pDhDK7zebrUJ5nXg==",
"license": "MIT",
"dependencies": {
"data-uri-to-buffer": "0.0.3",
"jpeg-js": "^0.4.1",
"mime-types": "^2.0.1",
"ndarray": "^1.0.13",
"ndarray-pack": "^1.1.1",
"node-bitmap": "0.0.1",
"omggif": "^1.0.5",
"parse-data-uri": "^0.2.0",
"pngjs": "^3.3.3",
"request": "^2.44.0",
"through": "^2.3.4"
}
},
"node_modules/get-pixels/node_modules/data-uri-to-buffer": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-0.0.3.tgz",
"integrity": "sha512-Cp+jOa8QJef5nXS5hU7M1DWzXPEIoVR3kbV0dQuVGwROZg8bGf1DcCnkmajBTnvghTtSNMUdRrPjgaT6ZQucbw==",
"license": "MIT"
},
"node_modules/get-port": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/get-port/-/get-port-5.1.1.tgz",
@ -13442,6 +13586,15 @@
"node": ">= 4.0.0"
}
},
"node_modules/getpass": {
"version": "0.1.7",
"resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
"integrity": "sha512-0fzj9JxOLfJ+XGLhR8ze3unN0KZCgZwiSSDz168VERjK8Wl8kVSdcu2kspd4s4wtAa1y/qrVRiAA0WclVsu0ng==",
"license": "MIT",
"dependencies": {
"assert-plus": "^1.0.0"
}
},
"node_modules/git-hooks-list": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/git-hooks-list/-/git-hooks-list-1.0.3.tgz",
@ -13648,6 +13801,29 @@
"gunzip-maybe": "bin.js"
}
},
"node_modules/har-schema": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
"integrity": "sha512-Oqluz6zhGX8cyRaTQlFMPw80bSJVG2x/cFb8ZPhUILGgHka9SsokCCOQgpveePerqidZOrT14ipqfJb7ILcW5Q==",
"license": "ISC",
"engines": {
"node": ">=4"
}
},
"node_modules/har-validator": {
"version": "5.1.5",
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz",
"integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==",
"deprecated": "this library is no longer supported",
"license": "MIT",
"dependencies": {
"ajv": "^6.12.3",
"har-schema": "^2.0.0"
},
"engines": {
"node": ">=6"
}
},
"node_modules/has-bigints": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz",
@ -13931,6 +14107,21 @@
"node": ">= 14"
}
},
"node_modules/http-signature": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
"integrity": "sha512-CAbnr6Rz4CYQkLYUtSNXxQPUH2gK8f3iWexVlsnMeD+GjlsQ0Xsy1cOX+mN3dtxYomRy21CiOzU8Uhw6OwncEQ==",
"license": "MIT",
"dependencies": {
"assert-plus": "^1.0.0",
"jsprim": "^1.2.2",
"sshpk": "^1.7.0"
},
"engines": {
"node": ">=0.8",
"npm": ">=1.3.7"
}
},
"node_modules/http2-wrapper": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz",
@ -14283,6 +14474,12 @@
"node": ">= 0.4"
}
},
"node_modules/iota-array": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/iota-array/-/iota-array-1.0.0.tgz",
"integrity": "sha512-pZ2xT+LOHckCatGQ3DcG/a+QuEqvoxqkiL7tvE8nn3uuu+f6i1TtpB5/FtWFbxUuVr5PZCx8KskuGatbJDXOWA==",
"license": "MIT"
},
"node_modules/ip": {
"version": "1.1.9",
"resolved": "https://registry.npmjs.org/ip/-/ip-1.1.9.tgz",
@ -14875,7 +15072,6 @@
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
"integrity": "sha512-cyA56iCMHAh5CdzjJIa4aohJyeO1YbwLi3Jc35MmRU6poroFjIGZzUzupGiRPOjgHg9TLu43xbpwXk523fMxKA==",
"dev": true,
"license": "MIT"
},
"node_modules/is-unicode-supported": {
@ -14959,6 +15155,12 @@
"node": ">=18"
}
},
"node_modules/isstream": {
"version": "0.1.2",
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
"integrity": "sha512-Yljz7ffyPbrLpLngrMtZ7NduUgVvi6wG9RJ9IUcyCd59YQ911PBJphODUcbOVbqYfxe1wuYf/LJ8PauMRwsM/g==",
"license": "MIT"
},
"node_modules/istanbul-lib-coverage": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz",
@ -18392,6 +18594,12 @@
"url": "https://github.com/sponsors/panva"
}
},
"node_modules/jpeg-js": {
"version": "0.4.4",
"resolved": "https://registry.npmjs.org/jpeg-js/-/jpeg-js-0.4.4.tgz",
"integrity": "sha512-WZzeDOEtTOBK4Mdsar0IqEU5sMr3vSV2RqkAIzUEV2BHnUfKGyswWFPFwK5EeDo93K3FohSHbLAjj0s1Wzd+dg==",
"license": "BSD-3-Clause"
},
"node_modules/js-beautify": {
"version": "1.15.1",
"resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.15.1.tgz",
@ -18590,6 +18798,12 @@
"dev": true,
"license": "MIT"
},
"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==",
"license": "ISC"
},
"node_modules/json5": {
"version": "2.2.3",
"resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz",
@ -18623,6 +18837,21 @@
"node": ">=0.10.0"
}
},
"node_modules/jsprim": {
"version": "1.4.2",
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.2.tgz",
"integrity": "sha512-P2bSOMAc/ciLz6DzgjVlGJP9+BrJWu5UDGK70C2iweC5QBIeFf0ZXRvGjEj2uYgrY2MkAAhsSWHDWlFtEroZWw==",
"license": "MIT",
"dependencies": {
"assert-plus": "1.0.0",
"extsprintf": "1.3.0",
"json-schema": "0.4.0",
"verror": "1.10.0"
},
"engines": {
"node": ">=0.6.0"
}
},
"node_modules/jsx-ast-utils": {
"version": "3.3.5",
"resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-3.3.5.tgz",
@ -20294,6 +20523,32 @@
"dev": true,
"license": "MIT"
},
"node_modules/ndarray": {
"version": "1.0.19",
"resolved": "https://registry.npmjs.org/ndarray/-/ndarray-1.0.19.tgz",
"integrity": "sha512-B4JHA4vdyZU30ELBw3g7/p9bZupyew5a7tX1Y/gGeF2hafrPaQZhgrGQfsvgfYbgdFZjYwuEcnaobeM/WMW+HQ==",
"license": "MIT",
"dependencies": {
"iota-array": "^1.0.0",
"is-buffer": "^1.0.2"
}
},
"node_modules/ndarray-pack": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/ndarray-pack/-/ndarray-pack-1.2.1.tgz",
"integrity": "sha512-51cECUJMT0rUZNQa09EoKsnFeDL4x2dHRT0VR5U2H5ZgEcm95ZDWcMA5JShroXjHOejmAD/fg8+H+OvUnVXz2g==",
"license": "MIT",
"dependencies": {
"cwise-compiler": "^1.1.2",
"ndarray": "^1.0.13"
}
},
"node_modules/ndarray/node_modules/is-buffer": {
"version": "1.1.6",
"resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz",
"integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==",
"license": "MIT"
},
"node_modules/negotiator": {
"version": "0.6.3",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz",
@ -20492,6 +20747,14 @@
"integrity": "sha512-eh0GgfEkpnoWDq+VY8OyvYhFEzBk6jIYbRKdIlyTiAXIVJ8PyBaKb0rp7oDtoddbdoHWhq8wwr+XZ81F1rpNdA==",
"license": "MIT"
},
"node_modules/node-bitmap": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/node-bitmap/-/node-bitmap-0.0.1.tgz",
"integrity": "sha512-Jx5lPaaLdIaOsj2mVLWMWulXF6GQVdyLvNSxmiYCvZ8Ma2hfKX0POoR2kgKOqz+oFsRreq0yYZjQ2wjE9VNzCA==",
"engines": {
"node": ">=v0.6.5"
}
},
"node_modules/node-fetch": {
"version": "2.7.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz",
@ -20643,6 +20906,15 @@
"integrity": "sha512-a5ERWK1kh38ExDEfoO6qUHJb32rd7aYmPHuyCu3Fta/cnICvYmgd2uhuKXvPD+PXB+gCEYYEaQdIRAjCOwAKNA==",
"license": "MIT"
},
"node_modules/oauth-sign": {
"version": "0.9.0",
"resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
"integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==",
"license": "Apache-2.0",
"engines": {
"node": "*"
}
},
"node_modules/object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
@ -20793,6 +21065,12 @@
"node": "^10.13.0 || >=12.0.0"
}
},
"node_modules/omggif": {
"version": "1.0.10",
"resolved": "https://registry.npmjs.org/omggif/-/omggif-1.0.10.tgz",
"integrity": "sha512-LMJTtvgc/nugXj0Vcrrs68Mn2D1r0zf630VNtqtpI1FEO7e+O9FP4gqs9AcnBaSEeoHIPm28u6qgPR0oyEpGSw==",
"license": "MIT"
},
"node_modules/on-finished": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
@ -21148,6 +21426,21 @@
"node": ">=6"
}
},
"node_modules/parse-data-uri": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/parse-data-uri/-/parse-data-uri-0.2.0.tgz",
"integrity": "sha512-uOtts8NqDcaCt1rIsO3VFDRsAfgE4c6osG4d9z3l4dCBlxYFzni6Di/oNU270SDrjkfZuUvLZx1rxMyqh46Y9w==",
"license": "ISC",
"dependencies": {
"data-uri-to-buffer": "0.0.3"
}
},
"node_modules/parse-data-uri/node_modules/data-uri-to-buffer": {
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-0.0.3.tgz",
"integrity": "sha512-Cp+jOa8QJef5nXS5hU7M1DWzXPEIoVR3kbV0dQuVGwROZg8bGf1DcCnkmajBTnvghTtSNMUdRrPjgaT6ZQucbw==",
"license": "MIT"
},
"node_modules/parse-entities": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/parse-entities/-/parse-entities-4.0.1.tgz",
@ -21345,6 +21638,12 @@
"through2": "^2.0.3"
}
},
"node_modules/performance-now": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
"integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==",
"license": "MIT"
},
"node_modules/periscopic": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/periscopic/-/periscopic-3.1.0.tgz",
@ -21448,6 +21747,15 @@
"pathe": "^1.1.2"
}
},
"node_modules/pngjs": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/pngjs/-/pngjs-3.4.0.tgz",
"integrity": "sha512-NCrCHhWmnQklfH4MtJMRjZ2a8c80qXeMlQMv2uVp9ISJMTt562SbGd6n2oq0PaPgKm7Z6pL9E2UlLIhC+SHL3w==",
"license": "MIT",
"engines": {
"node": ">=4.0.0"
}
},
"node_modules/possible-typed-array-names": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz",
@ -22602,6 +22910,84 @@
"url": "https://opencollective.com/unified"
}
},
"node_modules/request": {
"version": "2.88.2",
"resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz",
"integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==",
"deprecated": "request has been deprecated, see https://github.com/request/request/issues/3142",
"license": "Apache-2.0",
"dependencies": {
"aws-sign2": "~0.7.0",
"aws4": "^1.8.0",
"caseless": "~0.12.0",
"combined-stream": "~1.0.6",
"extend": "~3.0.2",
"forever-agent": "~0.6.1",
"form-data": "~2.3.2",
"har-validator": "~5.1.3",
"http-signature": "~1.2.0",
"is-typedarray": "~1.0.0",
"isstream": "~0.1.2",
"json-stringify-safe": "~5.0.1",
"mime-types": "~2.1.19",
"oauth-sign": "~0.9.0",
"performance-now": "^2.1.0",
"qs": "~6.5.2",
"safe-buffer": "^5.1.2",
"tough-cookie": "~2.5.0",
"tunnel-agent": "^0.6.0",
"uuid": "^3.3.2"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/request/node_modules/form-data": {
"version": "2.3.3",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
"integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
"license": "MIT",
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.6",
"mime-types": "^2.1.12"
},
"engines": {
"node": ">= 0.12"
}
},
"node_modules/request/node_modules/qs": {
"version": "6.5.3",
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz",
"integrity": "sha512-qxXIEh4pCGfHICj1mAJQ2/2XVZkjCDTcEgfoSQxc/fYivUZxTkk7L3bDBJSoNrEzXI17oUO5Dp07ktqE5KzczA==",
"license": "BSD-3-Clause",
"engines": {
"node": ">=0.6"
}
},
"node_modules/request/node_modules/tough-cookie": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
"integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
"license": "BSD-3-Clause",
"dependencies": {
"psl": "^1.1.28",
"punycode": "^2.1.1"
},
"engines": {
"node": ">=0.8"
}
},
"node_modules/request/node_modules/uuid": {
"version": "3.4.0",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==",
"deprecated": "Please upgrade to version 7 or higher. Older versions may use Math.random() in certain circumstances, which is known to be problematic. See https://v8.dev/blog/math-random for details.",
"license": "MIT",
"bin": {
"uuid": "bin/uuid"
}
},
"node_modules/require-directory": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
@ -23522,6 +23908,37 @@
"license": "BSD-3-Clause",
"peer": true
},
"node_modules/sshpk": {
"version": "1.18.0",
"resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.18.0.tgz",
"integrity": "sha512-2p2KJZTSqQ/I3+HX42EpYOa2l3f8Erv8MWKsy2I9uf4wA7yFIkXRffYdsx86y6z4vHtV8u7g+pPlr8/4ouAxsQ==",
"license": "MIT",
"dependencies": {
"asn1": "~0.2.3",
"assert-plus": "^1.0.0",
"bcrypt-pbkdf": "^1.0.0",
"dashdash": "^1.12.0",
"ecc-jsbn": "~0.1.1",
"getpass": "^0.1.1",
"jsbn": "~0.1.0",
"safer-buffer": "^2.0.2",
"tweetnacl": "~0.14.0"
},
"bin": {
"sshpk-conv": "bin/sshpk-conv",
"sshpk-sign": "bin/sshpk-sign",
"sshpk-verify": "bin/sshpk-verify"
},
"engines": {
"node": ">=0.10.0"
}
},
"node_modules/sshpk/node_modules/jsbn": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
"integrity": "sha512-UVU9dibq2JcFWxQPA6KCqj5O42VOmAY3zQUfEKxU0KpTGXwNoCjkX1e13eHNvw/xPynt6pU0rZ1htjWTNTSXsg==",
"license": "MIT"
},
"node_modules/ssri": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/ssri/-/ssri-8.0.1.tgz",
@ -24450,8 +24867,7 @@
"version": "2.3.8",
"resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
"integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==",
"license": "MIT",
"peer": true
"license": "MIT"
},
"node_modules/through2": {
"version": "2.0.5",
@ -24780,6 +25196,24 @@
"integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==",
"license": "0BSD"
},
"node_modules/tunnel-agent": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
"integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==",
"license": "Apache-2.0",
"dependencies": {
"safe-buffer": "^5.0.1"
},
"engines": {
"node": "*"
}
},
"node_modules/tweetnacl": {
"version": "0.14.5",
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
"integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==",
"license": "Unlicense"
},
"node_modules/type-check": {
"version": "0.4.0",
"resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
@ -25026,6 +25460,12 @@
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/uniq": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/uniq/-/uniq-1.0.1.tgz",
"integrity": "sha512-Gw+zz50YNKPDKXs+9d+aKAjVwpjNwqzvNpLigIruT4HA9lMZNdMqs9x07kKHB/L9WRzqp4+DlTU5s4wG2esdoA==",
"license": "MIT"
},
"node_modules/unique-filename": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-1.1.1.tgz",
@ -25432,6 +25872,26 @@
"node": ">= 14"
}
},
"node_modules/verror": {
"version": "1.10.0",
"resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
"integrity": "sha512-ZZKSmDAEFOijERBLkmYfJ+vmk3w+7hOLYDNkRCuRuMJGEmqYNCNLyBBFwWKVMhfwaEF3WOd0Zlw86U/WC/+nYw==",
"engines": [
"node >=0.6.0"
],
"license": "MIT",
"dependencies": {
"assert-plus": "^1.0.0",
"core-util-is": "1.0.2",
"extsprintf": "^1.2.0"
}
},
"node_modules/verror/node_modules/core-util-is": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
"integrity": "sha512-3lqz5YjWTYnW6dlDa5TLaTCcShfar1e40rmcJVwCBJC6mWlFuj0eCHIElmG1g5kyuJ/GD+8Wn4FFCcz4gJPfaQ==",
"license": "MIT"
},
"node_modules/vfile": {
"version": "5.3.7",
"resolved": "https://registry.npmjs.org/vfile/-/vfile-5.3.7.tgz",

View File

@ -26,6 +26,7 @@
"@types/react-redux": "^7.1.23",
"axios": "^1.7.2",
"bcrypt": "^5.1.1",
"colorthief": "^2.4.0",
"concurrently": "^7.6.0",
"cookie": "^0.6.0",
"cors": "^2.8.5",

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 117 KiB

File diff suppressed because one or more lines are too long

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 864 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 232 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 380 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 219 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 112 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 99 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 143 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

View File

@ -904,6 +904,12 @@ export const BadgeCheckSolid: React.FC<IconProps> = ({ className, onClick }) =>
</svg>;
return Icon;
}
export const BadgePercenrageSolid: React.FC<IconProps> = ({ className, onClick }) => {
const Icon = <svg onClick={(e) => onClick && onClick(e)} className={`${className}`} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
<path d="M256 0c36.8 0 68.8 20.7 84.9 51.1C373.8 41 411 49 437 75s34 63.3 23.9 96.1C491.3 187.2 512 219.2 512 256s-20.7 68.8-51.1 84.9C471 373.8 463 411 437 437s-63.3 34-96.1 23.9C324.8 491.3 292.8 512 256 512s-68.8-20.7-84.9-51.1C138.2 471 101 463 75 437s-34-63.3-23.9-96.1C20.7 324.8 0 292.8 0 256s20.7-68.8 51.1-84.9C41 138.2 49 101 75 75s63.3-34 96.1-23.9C187.2 20.7 219.2 0 256 0zM192 224c17.7 0 32-14.3 32-32s-14.3-32-32-32s-32 14.3-32 32s14.3 32 32 32zm160 96c0-17.7-14.3-32-32-32s-32 14.3-32 32s14.3 32 32 32s32-14.3 32-32zM337 209c9.4-9.4 9.4-24.6 0-33.9s-24.6-9.4-33.9 0L175 303c-9.4 9.4-9.4 24.6 0 33.9s24.6 9.4 33.9 0L337 209z" />
</svg>;
return Icon;
}
export const EngineLight: React.FC<IconProps> = ({ className, onClick }) => {
const Icon = <svg onClick={(e) => onClick && onClick(e)} className={`${className}`} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512">
<path d="M216 256C216 269.3 205.3 280 192 280C178.7 280 168 269.3 168 256C168 242.7 178.7 232 192 232C205.3 232 216 242.7 216 256zM264 256C264 242.7 274.7 232 288 232C301.3 232 312 242.7 312 256C312 269.3 301.3 280 288 280C274.7 280 264 269.3 264 256zM408 256C408 269.3 397.3 280 384 280C370.7 280 360 269.3 360 256C360 242.7 370.7 232 384 232C397.3 232 408 242.7 408 256zM416 80C416 88.84 408.8 96 400 96H320V128H396.6C409.3 128 421.6 131.7 432.1 138.7L483.5 173C501.3 184.9 512 204.9 512 226.3V384C512 419.3 483.3 448 448 448H254.8C235.3 448 216.9 439.2 204.8 423.1L172.8 384H144C117.5 384 96 362.5 96 336V272H32V352C32 360.8 24.84 368 16 368C7.164 368 0 360.8 0 352V160C0 151.2 7.164 144 16 144C24.84 144 32 151.2 32 160V240H96V176C96 149.5 117.5 128 144 128H288V96H208C199.2 96 192 88.84 192 80C192 71.16 199.2 64 208 64H400C408.8 64 416 71.16 416 80zM144 160C135.2 160 128 167.2 128 176V336C128 344.8 135.2 352 144 352H188.2L229.8 403.1C235.8 411.6 245 416 254.8 416H448C465.7 416 480 401.7 480 384V226.3C480 215.6 474.7 205.6 465.8 199.6L414.4 165.4C409.1 161.9 402.9 160 396.6 160H144zM544 224C544 206.3 558.3 192 576 192H608C625.7 192 640 206.3 640 224V416C640 433.7 625.7 448 608 448H576C558.3 448 544 433.7 544 416V224zM576 416H608V224H576V416z" />
@ -1816,4 +1822,28 @@ export const SeatAirlineSolid: React.FC<IconProps> = ({ className, onClick }) =>
</svg>;
return Icon;
}
export const ThumbTackSolid: React.FC<IconProps> = ({ className, onClick }) => {
const Icon = <svg onClick={(e) => onClick && onClick(e)} className={`${className}`} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 384 512">
<path xmlns="http://www.w3.org/2000/svg" d="M32 32C32 14.3 46.3 0 64 0H320c17.7 0 32 14.3 32 32s-14.3 32-32 32H290.5l11.4 148.2c36.7 19.9 65.7 53.2 79.5 94.7l1 3c3.3 9.8 1.6 20.5-4.4 28.8s-15.7 13.3-26 13.3H32c-10.3 0-19.9-4.9-26-13.3s-7.7-19.1-4.4-28.8l1-3c13.8-41.5 42.8-74.8 79.5-94.7L93.5 64H64C46.3 64 32 49.7 32 32zM160 384h64v96c0 17.7-14.3 32-32 32s-32-14.3-32-32V384z" />
</svg>;
return Icon;
}
export const BullHornSolid: React.FC<IconProps> = ({ className, onClick }) => {
const Icon = <svg onClick={(e) => onClick && onClick(e)} className={`${className}`} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
<path d="M480 32c0-12.9-7.8-24.6-19.8-29.6s-25.7-2.2-34.9 6.9L381.7 53c-48 48-113.1 75-181 75H192 160 64c-35.3 0-64 28.7-64 64v96c0 35.3 28.7 64 64 64l0 128c0 17.7 14.3 32 32 32h64c17.7 0 32-14.3 32-32V352l8.7 0c67.9 0 133 27 181 75l43.6 43.6c9.2 9.2 22.9 11.9 34.9 6.9s19.8-16.6 19.8-29.6V300.4c18.6-8.8 32-32.5 32-60.4s-13.4-51.6-32-60.4V32zm-64 76.7V240 371.3C357.2 317.8 280.5 288 200.7 288H192V192h8.7c79.8 0 156.5-29.8 215.3-83.3z" />
</svg>;
return Icon;
}
export const PartyHornSolid: React.FC<IconProps> = ({ className, onClick }) => {
const Icon = <svg onClick={(e) => onClick && onClick(e)} className={`${className}`} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
<path d="M503.5 42.3c10.1-8.6 11.4-23.7 2.8-33.8s-23.7-11.4-33.8-2.8l-10 8.5c-5.8 4.9-13.2 7.5-20.7 7.3c-42.5-1.1-78.3 31.7-80.8 74.2l-1.8 30c-1 16.6-15 29.4-31.6 29c-19.4-.5-38.2 6.1-53 18.6l-10 8.5c-10.1 8.6-11.4 23.7-2.8 33.8s23.7 11.4 33.8 2.8l10-8.5c5.8-4.9 13.2-7.5 20.7-7.3c42.5 1.1 78.3-31.7 80.8-74.2l1.8-30c1-16.6 15-29.4 31.6-29c19.4 .5 38.2-6.1 53-18.6l10-8.5zM232 0c-13.3 0-24 10.7-24 24c0 30.5-10.1 54.1-20.3 70.2c-5.1 8.1-10.2 14.2-13.8 18.2c-1.8 2-3.3 3.4-4.2 4.3c-.5 .4-.8 .7-1 .9l-.1 .1 0 0c-10.1 8.5-11.4 23.6-3 33.7c8.5 10.2 23.6 11.6 33.8 3.1L184 136c15.4 18.4 15.4 18.4 15.4 18.4l0 0 0 0 .1-.1 .2-.2 .7-.6c.5-.5 1.2-1.1 2.1-1.9c1.7-1.6 4-3.8 6.6-6.7c5.3-5.8 12.3-14.2 19.2-25.1C242.1 97.9 256 65.5 256 24c0-13.3-10.7-24-24-24zM96 32C96 14.3 81.7 0 64 0S32 14.3 32 32s14.3 32 32 32s32-14.3 32-32zM512 160c0-17.7-14.3-32-32-32s-32 14.3-32 32s14.3 32 32 32s32-14.3 32-32zM480 480c17.7 0 32-14.3 32-32s-14.3-32-32-32s-32 14.3-32 32s14.3 32 32 32zM395.5 342.1l.1-.1c.2-.2 .5-.6 .9-1.1c.9-1.1 2.3-2.8 4.3-4.8c4-4.2 10-9.7 17.8-15.1c15.6-10.7 37.9-20.2 66.8-17c13.2 1.5 25-8 26.5-21.2s-8-25-21.2-26.5c-43.1-4.8-76.8 9.7-99.2 25c-11.2 7.6-19.7 15.6-25.5 21.6c-2.9 3.1-5.2 5.7-6.7 7.6c-.8 1-1.4 1.8-1.9 2.4l-.6 .8-.2 .3-.1 .1 0 0 0 0c0 0 0 0 19.5 14l-19.5-13.9c-7.7 10.8-5.2 25.8 5.6 33.5c10.7 7.7 25.7 5.2 33.4-5.5l0 0zM121.2 192.7c-10.6 2.3-19.4 9.9-23.2 20L83.1 252.5 259.5 428.9 299.2 414c10.2-3.8 17.7-12.6 20-23.2s-1-21.7-8.6-29.4l-160-160c-7.7-7.7-18.8-11-29.4-8.6zM70.8 285.4L48.2 345.6 166.4 463.8l60.2-22.6L70.8 285.4zm62.7 190.7L35.9 378.5 2 468.8c-4.4 11.8-1.5 25 7.3 33.9s22.1 11.7 33.9 7.3l90.3-33.8z" />
</svg>;
return Icon;
}
export const PartyBellSolid: React.FC<IconProps> = ({ className, onClick }) => {
const Icon = <svg onClick={(e) => onClick && onClick(e)} className={`${className}`} xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512">
<path d="M224 0C100.3 0 0 100.3 0 224c0 35.3 8.2 68.9 22.8 98.7C32.3 341.9 51.7 352 70.7 352c14.5 0 28.3-5.7 38.5-16L336 109.2c10.2-10.2 16-24.1 16-38.5c0-19-10.1-38.4-29.3-47.8C292.9 8.2 259.3 0 224 0zM352 512c17.7 0 32-14.3 32-32s-14.3-32-32-32s-32 14.3-32 32s14.3 32 32 32zM448 64c0 17.7 14.3 32 32 32s32-14.3 32-32s-14.3-32-32-32s-32 14.3-32 32zM64 512c17.7 0 32-14.3 32-32s-14.3-32-32-32s-32 14.3-32 32s14.3 32 32 32zM490.6 255.9c13.2-1.5 22.7-13.3 21.2-26.5s-13.3-22.7-26.5-21.2c-28.9 3.2-51.2-6.3-66.8-17c-7.8-5.4-13.8-10.9-17.8-15.1c-2-2.1-3.4-3.8-4.3-4.8c-.4-.5-.7-.9-.9-1.1l-.1-.1 0 0c-7.7-10.7-22.6-13.1-33.4-5.5c-10.8 7.7-13.3 22.7-5.6 33.5L376 184c-19.5 13.9-19.5 14-19.5 14l0 0 0 0 .1 .1 .2 .3 .6 .8c.5 .6 1.1 1.4 1.9 2.4c1.6 1.9 3.8 4.6 6.7 7.6c5.8 6.1 14.3 14 25.5 21.6c22.4 15.3 56.1 29.8 99.2 25zM169.9 395.4l0 0 .1 .1c.2 .2 .6 .5 1.1 .9c1.1 .9 2.8 2.3 4.8 4.3c4.2 4 9.7 10 15.1 17.8c10.7 15.6 20.2 37.9 17 66.8c-1.5 13.2 8 25 21.2 26.5s25-8 26.5-21.2c4.8-43.1-9.7-76.8-25-99.2c-7.6-11.2-15.6-19.7-21.6-25.5c-3.1-2.9-5.7-5.2-7.6-6.7c-1-.8-1.8-1.4-2.4-1.9l-.8-.6-.3-.2-.1-.1 0 0 0 0c0 0 0 0-14 19.5l13.9-19.5c-10.8-7.7-25.8-5.2-33.5 5.6c-7.7 10.7-5.2 25.7 5.5 33.4zM469.7 503.5c8.6 10.1 23.7 11.4 33.8 2.8s11.4-23.7 2.8-33.8l-8.5-10c-4.9-5.8-7.5-13.2-7.3-20.7c1.1-42.5-31.7-78.3-74.2-80.8l-30-1.8c-16.6-1-29.4-15-29-31.6c.5-19.4-6.1-38.2-18.6-53l-8.5-10c-8.6-10.1-23.7-11.4-33.8-2.8s-11.4 23.7-2.8 33.8l8.5 10c4.9 5.8 7.5 13.2 7.3 20.7c-1.1 42.5 31.7 78.3 74.2 80.8l30 1.8c16.6 1 29.4 15 29 31.6c-.5 19.4 6.1 38.2 18.6 53l8.5 10z" />
</svg>;
return Icon;
}

View File

@ -1,8 +1,8 @@
import { ApolloClient, InMemoryCache } from "@apollo/client";
const cmsAddress = () => {
const serverAddress = process.env.NODE_ENV === 'production' ? `https://backend.flierland.ca` : `http://192.168.1.105:8055`;
// const serverAddress = process.env.NODE_ENV === 'production' ? `http://192.168.1.105:8055` : `http://192.168.1.105:8055`;
// const serverAddress = process.env.NODE_ENV === 'production' ? `https://backend.flierland.ca` : `http://192.168.1.105:8055`;
const serverAddress = process.env.NODE_ENV === 'production' ? `http://192.168.1.105:8055` : `http://192.168.1.105:8055`;
return serverAddress;
}
const defaultOptions = {

View File

@ -0,0 +1,49 @@
export const getDominantColor = (imageUrl: string): Promise<string> => {
return new Promise((resolve, reject) => {
const img = new Image();
img.crossOrigin = 'Anonymous';
img.src = imageUrl;
img.onload = function () {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
// Set canvas dimensions equal to the image's
canvas.width = img.width;
canvas.height = img.height;
ctx?.drawImage(img, 0, 0);
const imageData = ctx?.getImageData(0, 0, canvas.width, canvas.height);
const pixels = imageData?.data;
if (!pixels) {
reject('No pixels found');
return;
}
let r = 0, g = 0, b = 0, total = 0;
// Loop through pixels, calculating the average color
for (let i = 0; i < pixels.length; i += 4) {
r += pixels[i]; // Red
g += pixels[i + 1]; // Green
b += pixels[i + 2]; // Blue
total++;
}
// Get average color values
r = Math.floor(r / total);
g = Math.floor(g / total);
b = Math.floor(b / total);
// Convert RGB to Hex
const dominantHex = `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1).toUpperCase()}`;
resolve(dominantHex);
};
img.onerror = function () {
reject('Failed to load image');
};
});
};

View File

@ -67,22 +67,22 @@ export const iranianLandlineNumberValidationRegex = new RegExp("^\\d{11}$");
// Website general base path generator for both local and production
// 167.99.189.252 live server's ip address
export const basePath = () => {
const basePath = process.env.NODE_ENV === 'production' ? `https://flierland.ca` : `http://localhost:3000`;
// const basePath = process.env.NODE_ENV === 'production' ? `http://localhost:3000` : `http://localhost:3000`;
// const basePath = process.env.NODE_ENV === 'production' ? `https://flierland.ca` : `http://localhost:3000`;
const basePath = process.env.NODE_ENV === 'production' ? `http://localhost:3000` : `http://localhost:3000`;
return basePath;
}
// Website server address generator for both local and production
export const serverAddress = () => {
const serverAddress = process.env.NODE_ENV === 'production' ? `https://cdn.flierland.ca/assets/` : `http://192.168.1.105:8055/assets/`;
// const serverAddress = process.env.NODE_ENV === 'production' ? `http://192.168.1.105:8055/assets/` : `http://192.168.1.105:8055/assets/`;
// const serverAddress = process.env.NODE_ENV === 'production' ? `https://cdn.flierland.ca/assets/` : `http://192.168.1.105:8055/assets/`;
const serverAddress = process.env.NODE_ENV === 'production' ? `http://192.168.1.105:8055/assets/` : `http://192.168.1.105:8055/assets/`;
return serverAddress;
}
// Website server address generator for both local and production
export const cmsAddress = () => {
const serverAddress = process.env.NODE_ENV === 'production' ? `https://backend.flierland.ca` : `http://192.168.1.105:8055`;
// const serverAddress = process.env.NODE_ENV === 'production' ? `http://192.168.1.105:8055` : `http://192.168.1.105:8055`;
// const serverAddress = process.env.NODE_ENV === 'production' ? `https://backend.flierland.ca` : `http://192.168.1.105:8055`;
const serverAddress = process.env.NODE_ENV === 'production' ? `http://192.168.1.105:8055` : `http://192.168.1.105:8055`;
return serverAddress;
}
@ -92,19 +92,19 @@ export const goToDashboard = (isLoggedIn) => {
return urlAddress;
}
// Redirects user to dashboard based on logged in & locale
// Checks if the ad is unpaid
export const isAdUnpaid = (expDate, adCatId) => {
const today = new Date();
const isUnpaid = (!hasValue(expDate) || Date.parse(expDate) < Date.parse(today)) && adCatId === "27"; // The ID for the services ads parent category is "27"
return isUnpaid;
}
// Website server address generator for both local and production
// Admin access token
export const adminAccessToken = process.env.NODE_ENV === 'production' ? process.env.NEXT_PUBLIC_ADMIN_LIVE_STATIC_TOKEN : process.env.NEXT_PUBLIC_LOCAL_ADMIN_STATIC_TOKEN;
// Strips html tags from text
export const stripHtml = (text) => {
const strippedText = text.replace(/(<([^>]+)>)/gi, '').replace('&nbsp;', '');
const strippedText = text.replace(/(<([^>]+)>)/gi, '').replace('&nbsp;', '').replace(/&zwnj;/gi, ' ');
return strippedText;
}
// Strips html tags from text

View File

@ -45,7 +45,7 @@ const BillboardMap: React.FunctionComponent<BillboardMapProps> = ({ billboard })
className="block w-full h-64"
>
<div className={`${showMap ? "hidden" : "flex"} flex-col items-center justify-center w-full h-full space-y-8 rounded-lg lg:cursor-pointer bg-gray-50
[background-image:url('/pics/patterns/blizzard.png')] bg-repeat`} onClick={() => setShowMap(true)}>
[background-image:url('/pics/patterns/white-texture.png')] bg-repeat`} onClick={() => setShowMap(true)}>
<span className="inline-block text-center text-lg py-2 px-4 rounded-lg bg-[var(--brand-color)] text-white">{translate("billboard-contact-show-map")}</span>
{/* <img
src="/pics/billboard/pirate-map.jpg"

View File

@ -0,0 +1,408 @@
import React, { useEffect, useState } from "react"
import Link from "components/link/link"
import useTranslate from "services/translation/translation"
import { basePath, fetchPublicData, getLocaleTr, getSmartTime, hasValue, useGetRouter } from "services/general/general";
import { CalendarDaysSolid, CopySolid, EllipsisSolid, EmptyHeart, EyeSolid, FilledHeart, ImageSharpSolid, PercentageSolid, ReplySolid, ShareNodesSolid, SplitSolid, Telegram, Whatsapp, XMark, XTwitter } from "components/icons";
import Gallery from "components/gallery/gallery";
import Image from "components/image/image";
import InnerLoading from "components/loading/inner-loading";
import useSWR from "swr";
import { BillboardNewsItem } from "common/types/billboard";
import Parser from "components/parser/parser";
import Slider from "components/slider/slider";
import ClickOutside from "components/click-outside/click-outside";
import Popover from "components/popover/popover";
import { copyPageUrl, shareMore } from "services/social/social";
interface NewsDetailsProps {
billboardId: string;
billboardTitle: string;
brandColor: string;
}
interface GalleryItemProps {
title: string;
pic: {
id: string;
description: string;
filename_download: string;
width: number;
height: number;
};
className?: string;
index: number;
onClick: (i: number) => void;
}
interface LabelProps {
value: string;
className: string;
}
interface SocialMediaButtonProps {
icon: string;
text: string;
link: string;
bgColor: string;
color: string;
className?: string;
onClick?: () => void;
}
const SocialMediaButton = ({ icon, text, link, bgColor, color, className, onClick }: SocialMediaButtonProps) => {
const getIconClass = `h-6 fill-current rtl:ml-2 ltr:mr-2`;
const filterIcon = () => {
let finalIcon: any;
switch (icon) {
case "Whatsapp":
finalIcon = <Whatsapp className={getIconClass} />
break;
case "Telegram":
finalIcon = <Telegram className={getIconClass} />
break;
case "XTwitter":
finalIcon = <XTwitter className={getIconClass} />
break;
case "EllipsisSolid":
finalIcon = <EllipsisSolid className={getIconClass} />
break;
default:
break;
}
return finalIcon;
}
return onClick ?
<div className={`reactive-button flex flex-col items-center hover:brightness-105 ${className}`} onClick={onClick}>
<div style={{ backgroundColor: bgColor, color: color }} className={`flex items-center justify-center w-full p-2 rounded-lg overflow-hidden shadow-inner`}>
{filterIcon()}
<span className="block text-sm text-current capitalize">{text}</span>
</div>
</div>
:
<a className={`reactive-button flex flex-col items-center hover:brightness-105 ${className}`} target={"blank"} href={link}>
<div style={{ backgroundColor: bgColor, color: color }} className={`flex items-center justify-center w-full p-2 rounded-lg overflow-hidden shadow-inner`}>
{filterIcon()}
<span className="block text-sm text-current capitalize">{text}</span>
</div>
</a>
}
const Label: React.FunctionComponent<LabelProps> = ({ value, className }) => {
return <div className={`inline-block absolute top-0 -translate-y-1/2 rtl:right-[1.5rem] ltr:left-[1.5rem] px-4 py-[2px] rounded z-10 text-white text-xs font-normal uppercase ${className}`}>
{value}
</div>
}
const GalleryItem: React.FunctionComponent<GalleryItemProps> = ({ title, pic, index, className, onClick }) => {
// variables
const translate = useTranslate();
return <div className="block h-full lg:cursor-pointer" onClick={() => onClick(index)}>
{pic ?
<Image
src={`${pic.id}/${pic.filename_download}`}
alt={translate("pic-of") + title}
width={pic.width}
height={pic.height}
quality={75}
ar={[pic.width / pic.height, pic.width / pic.height, pic.width / pic.height, pic.width / pic.height]}
imageSizes={[450, 900, 900, 900]}
priority={true}
noPreload={true}
fetchPriority="low"
className={`w-full rounded-lg !object-cover sm:!object-contain`}
figureClass="rounded-lg w-full max-h-80 sm:max-h-[450px] select-none [&>div]:!bg-transparent lg:cursor-pointer"
/>
:
<span className="block size-full rounded-full bg-gray-50">
<ImageSharpSolid className="inline-block absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 size-7 xl:size-9 fill-gray-200" />
</span>
}
</div>
}
const NewsDetails: React.FunctionComponent<NewsDetailsProps> = ({ billboardId, billboardTitle, brandColor }) => {
// states
const [copyUrlText, setCopyUrltext] = useState("copy-success");
const [copyUrlPopoverOpen, setCopyUrlPopoverOpen] = useState(false);
const [isWindowReady, setIsWindowReady] = useState(false);
const [canShare, setCanShare] = useState(true);
const [shareOpen, setShareOpen] = useState(false);
const [showGallery, setShowGallery] = useState(false);
const [galleryIndex, setGalleryIndex] = useState(0);
// variables
const translate = useTranslate();
const { locale, query, asPath, router } = useGetRouter();
const newsDetailsQuery = `
billboard_news
(
filter: {
billboards: { Billboards_id: { id: { _eq: "${billboardId}" }}},
news_id: { _eq: "${query.news}" }
private: { _eq: false },
status: { _eq: "published" }
},
),
{
id
status
date_created
news_id
translations {
languages_code {
code
}
title
content
cta
cta_link
}
billboards {
Billboards_id {
id
}
}
category {
billboard_news_categories_id {
id
cat_id
translations {
languages_code {
code
}
name
}
has_children
parent {
related_billboard_news_categories_id {
id
cat_id
translations {
languages_code {
code
}
name
}
has_children
}
}
}
}
gallery {
directus_files_id {
id
description
filename_download
width
height
}
}
video_link
features
col_span
pic_only
private
pinned
likes
seen_by
}
`
const { data: newsDetails, error: newsDetailsError } = useSWR(query.news ? ['', newsDetailsQuery] : null, ([path, url]) => fetchPublicData(path, url));
newsDetailsError && console.log(newsDetailsError);
const newsData: BillboardNewsItem = newsDetails?.data.billboard_news[0];
const pageUrl = encodeURI(basePath() + (locale === "en" ? "/en" : "") + asPath);
const shareData = {
title: newsData ? getLocaleTr(newsData, locale).title : billboardTitle,
url: pageUrl
}
const picWidth = (x:any) => x.gallery[0]?.directus_files_id.width;
const picHeight = (x:any) => x.gallery[0]?.directus_files_id.height;
// console.log(newsData);
// methods
const onBack = () => {
router.back();
}
const copy = () => {
if (isWindowReady) {
copyPageUrl(setCopyUrltext, locale, asPath);
setCopyUrlPopoverOpen(true);
}
}
const handleShareMore = () => {
if (canShare) {
shareMore(shareData);
}
}
const openGallery = (index: number) => {
setGalleryIndex(index);
setShowGallery(true);
}
const closeGallery = (i: number) => {
setShowGallery(false);
setGalleryIndex(i);
}
// useEffects
return newsDetails ?
<>
<div className="flex items-center justify-between w-full py-2 lg:py-3 px-4 bg-white border-b border-gray-200/75">
<span className="inlin-block py-2 px-4 rounded-lg bg-[var(--light-brand-color)] text-xs lg:text-sm text-[var(--brand-color)]">
<SplitSolid className="inline-block size-3 lg:size-3 fill-current rtl:ml-2 ltr:mr-2 lg:rtl:ml-2 lg:ltr:mr-2" />
{getLocaleTr((newsData as any).category[0].billboard_news_categories_id, locale).name}
</span>
<ReplySolid className="reactive-button inline-block size-9 lg:size-11 ltr:rotate-180 fill-current p-2 lg:p-3 rounded-full bg-[var(--light-brand-color)] text-[var(--brand-color)]" onClick={onBack} />
</div>
{/* gallery */}
<Gallery
open={showGallery}
items={newsData.gallery}
index={galleryIndex}
title={getLocaleTr(newsData, locale).title}
onClose={(i) => closeGallery(i)}
id="product-gallery"
/>
<div className="block bg-white rounded-b-lg p-4 border-b border-gray-200/75 mt-1">
{newsData.gallery.length < 2 ?
<div className="block w-full bg-white rounded-lg lg:cursor-pointer" onClick={() => openGallery(0)}>
{newsData.gallery[0] ?
<Image
src={`${newsData.gallery[0].directus_files_id.id}/${newsData.gallery[0].directus_files_id.filename_download}`}
alt={translate("pic-of") + getLocaleTr(newsData, locale).title}
width={newsData.gallery[0].directus_files_id.width}
height={newsData.gallery[0].directus_files_id.height}
quality={75}
ar={[picWidth(newsData) / picHeight(newsData), picWidth(newsData) / picHeight(newsData), picWidth(newsData) / picHeight(newsData), picWidth(newsData) / picHeight(newsData)]}
imageSizes={[450, 900, 900, 900]}
priority={true}
noPreload={true}
fetchPriority="low"
className={`w-auto rounded-lg !object-contain`}
figureClass="rounded-lg w-full max-h-[450px] select-none [&>div]:!bg-transparent"
/>
:
<span className="block size-full rounded-full bg-gray-50">
<ImageSharpSolid className="inline-block absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 size-7 xl:size-9 fill-gray-200" />
</span>
}
</div>
:
<Slider
space={2}
slidesToShow={[1, 1, 1, 1, 1, 1]}
dots={true}
dragFree={false}
buttons={false}
slides={newsData.gallery}
startIndex={0}
align="center"
direction={locale === "fa" ? "rtl" : "ltr"}
loop={false}
autoplay={false}
childComponent={(x: any, i) => <GalleryItem key={i} title={getLocaleTr(newsData, locale).title} pic={x.directus_files_id} index={i} onClick={openGallery} />}
sliderClass={`block w-full [rtl:direction:rtl] mx-auto bg-white`}
viewPortClass="!p-0 bg-white"
containerClass=""
slideClass=""
controlsContainerClass="mt-8 mb-4"
/>
}
</div>
<div className={`block w-full relative px-4 lg:px-8 rounded-lg bg-white mt-1 border-b border-gray-200/75
${newsData.features?.includes("celebrate") ? "[background-image:url('/pics/patterns/memphis-colorful.webp')] bg-repeat bg-blend-multiply py-8" : "py-2"}
`}>
<div className="block px-4 lg:px-6 pt-2 pb-4 rounded-lg bg-white/85">
{/* labels */}
{newsData.features?.includes("celebrate") && <Label value={translate("billboard-news-label-celebrate")} className="bg-[var(--brand-color)]" />}
{newsData.features?.includes("hot") && <Label value={translate("billboard-news-label-hot")} className="bg-primary" />}
{newsData.features?.includes("limited") && <Label value={translate("billboard-news-label-limited")} className="bg-violet-600" />}
{/* header */}
<header className="block w-full pb-3 lg:py-4 lg:mb-2">
<h2 className="block text-lg/9 lg:text-2xl lg:text-center text-secondary-light pt-2">{getLocaleTr(newsData, locale).title}</h2>
<div className="flex items-center justify-between w-full border-b border-dashed border-gray-200/75 px-1 pt-6 lg:pt-12 pb-2">
{/* date */}
<span className="flex items-center text-sm text-secondary-light">
<CalendarDaysSolid className="inline-block size-4 fill-cool-gray rtl:ml-2 ltr:mr-2 -mt-[2px]" />
{getSmartTime(newsData.date_created)}
</span>
{/* seen & likes */}
<div className={`flex items-center space-x-3 rtl:space-x-reverse`}>
<div className="flex items-center space-x-[6px] rtl:space-x-reverse">
<EyeSolid className="inline-block size-4 fill-cool-gray" />
<span className="text-sm font-semibold text-secondary-light">{newsData.seen_by}</span>
</div>
<div className="flex items-center space-x-[6px] rtl:space-x-reverse">
<FilledHeart className="inline-block size-3 fill-cool-gray" />
<span className="text-sm font-semibold text-secondary-light">{newsData.likes}</span>
</div>
</div>
</div>
</header>
{/* content */}
<Parser limit limitLength={800} text={getLocaleTr(newsData, locale).content} className={`[&_*]:!text-sm/7 [&_*]:lg:!text-sm/7 !min-h-0 [&_*]:!text-start`} />
{getLocaleTr(newsData, locale).cta_link &&
<Link
href={`${getLocaleTr(newsData, locale).cta_link}`}
className="reactive-button block mx-auto w-max py-3 px-6 rounded-xl bg-[var(--brand-color)] text-white text-sm lg:text-base font-semibold mt-6 mb-4 select-none uppercase"
>
{getLocaleTr(newsData, locale).cta}
</Link>
}
{/* footer */}
<footer className="flex items-center w-full px-8 mt-6 mb-4 pt-4 lg:pt-8 border-t border-dashed">
{/* sharing */}
<ClickOutside wrapperClass="billboard-sharing" handleClass="billboard-sharing-handle" className={`!p-0 !shadow-none`} onClickOutside={() => setShareOpen(false)}>
<div className={`${shareOpen ? "visible -translate-y-4" : "invisible"} select-none block p-2 transition-transform duration-500 [transition-timing-function:cubic-bezier(0.2,1,0.3,1)] bottom-20 lg:bottom-28 bg-market-input w-full sm:w-96 absolute shadow-lg left-1/2 -translate-x-1/2 rounded-lg z-20`}>
<div className="flex flex-col items-center rounded-lg bg-white p-6">
<div className="flex items-center justify-between w-full">
<span className="text-base xl:text-base font-semibold block w-full text-secondary-light uppercase">{translate("article-share-title")}</span>
<XMark className="reactive-button inline-block capitalize shrink-0 bg-market-input fill-market-title-light/50 hover:fill-market-title-light w-8 h-8 p-1 rounded-lg" onClick={() => setShareOpen(false)} />
</div>
<div className="flex flex-col items-center w-full">
<div className="grid grid-cols-2 gap-2 w-full py-7">
<SocialMediaButton icon="Whatsapp" bgColor="#128C7E" color="#fff" text={translate("article-share-whatsapp")} link={`https://wa.me/?text=${pageUrl}`} />
<SocialMediaButton icon="Telegram" bgColor="#2AABEE" color="#fff" text={translate("article-share-telegram")} link={`https://t.me/share/url?url=${pageUrl}`} />
<SocialMediaButton icon="XTwitter" bgColor="#14171A" color="#fff" text={translate("article-share-x")} link={`https://twitter.com/intent/tweet?text=${billboardTitle}&url=${pageUrl}`} />
<SocialMediaButton icon="EllipsisSolid" bgColor="#9e3379" color="#fff" text={translate("other")} link={`#`} onClick={handleShareMore} />
</div>
<div className="block w-full relative">
<span className="block text-sm text-secondary-light mb-3 font-semibold capitalize">{translate("article-share-copy")}</span>
<div className="reactive-button flex items-center justify-between w-full rounded-lg bg-market-input px-4 py-2 shadow-inner text-market-title-light/50 hover:text-market-title-light" onClick={copy}>
<CopySolid className="inline-block capitalize shrink-0 fill-current h-[18px] rtl:ml-4 ltr:mr-4" />
<span className="text-sm text-current [direction:ltr] text-ellipsis overflow-hidden w-full whitespace-nowrap">{pageUrl}</span>
</div>
<Popover position="top" content={translate(copyUrlText)} open={copyUrlPopoverOpen} onClose={(e) => setCopyUrlPopoverOpen(e)} className="top-0" />
</div>
</div>
</div>
</div>
</ClickOutside>
<div className="flex items-center space-x-2 rtl:space-x-reverse mx-auto">
<EmptyHeart className="inline-block p-2 size-9 sm:size-10 bg-market-input fill-secondary-light rounded-lg md:rounded-lg lg:cursor-pointer hover:fill-[var(--brand-color)]" />
<ShareNodesSolid className="billboard-sharing-handle inline-block rounded-lg md:rounded-lg bg-market-input hover:bg-[var(--light-brand-color)] p-2 size-9 sm:size-10 fill-current text-secondary-light hover:text-[var(--brand-color)] lg:cursor-pointer" onClick={() => setShareOpen(true)} />
</div>
</footer>
</div>
</div>
</>
:
<InnerLoading loadingText={""} width={"200"} height={"100"} color={brandColor} />
}
export default NewsDetails

View File

@ -0,0 +1,121 @@
import React, { useEffect, useState } from "react"
import Link from "components/link/link"
import useTranslate from "services/translation/translation"
import { getLocaleTr, getSmartTime, serverAddress, stripHtml, url, useGetRouter } from 'services/general/general'
import { BadgePercenrageSolid, BellSolid, BullHornSolid, EmptyHeart, EyeSolid, FilledHeart, FireFlameSolid, ImageSharpSolid, PartyHornSolid, PercentageSolid, PlusSolid, ThumbTackSolid } from "components/icons"
import { BillboardNewsItem, BillboardProduct } from "common/types/billboard"
import Image from "components/image/image"
import { getDominantColor } from "services/general/dominant-color"
interface PicOnlyNewsProps {
item: BillboardNewsItem;
billboardId: string;
billboardTitle: string;
}
interface LabelProps {
value: string;
className: string;
}
const Label: React.FunctionComponent<LabelProps> = ({ value, className }) => {
return <div className={`inline-block absolute top-0 -translate-y-full rtl:right-5 ltr:left-5 px-4 py-[2px] rounded-t z-10 text-white text-xs font-normal uppercase ${className}`}>
{value}
</div>
}
const PicOnlyNews: React.FunctionComponent<PicOnlyNewsProps> = ({ item, billboardId, billboardTitle }) => {
// states
const [dominantColor, setDominantColor] = useState<string>('#f3f4f6');
// variables
const translate = useTranslate();
const { locale } = useGetRouter();
const pic = item.gallery[0]?.directus_files_id;
const itemTitle = item.translations.filter((x: any) => x.languages_code.code.slice(0, 2) === locale)[0].title;
const itemContent = item.translations.filter((x: any) => x.languages_code.code.slice(0, 2) === locale)[0].content;
// effects
useEffect(() => {
const fetchDominantColor = async () => {
try {
const color = await getDominantColor(`${serverAddress()}${pic.id}/${pic.filename_download}`);
setDominantColor(color);
} catch (error) {
console.error('Error fetching dominant color:', error);
}
};
fetchDominantColor();
}, [item]);
// console.log(dominantColor);
return (
<Link
href={`/billboard/${billboardId}/${url(billboardTitle)}?news=${item.news_id}#updates`}
shallow
scroll={false}
target={"_self"}
style={{ "--col-span": `span ${item.col_span} / span ${item.col_span}`, "--dominant-color": dominantColor } as React.CSSProperties}
className={`max-lg:reactive-button flex w-full max-h-[420px] relative bg-white shrink-0 border-b p-2 lg:p-1 border-gray-100 lg:rounded-lg [grid-column:var(--col-span)] [grid-row:var(--row-span)]
lg:cursor-pointer
${item.features?.includes("celebrate") ? "!bg-[var(--brand-color)]" : ""}
${item.features?.includes("hot") ? "!bg-primary" : ""}
${item.features?.includes("limited") ? "!bg-violet-600" : ""}
[&_.pic-only-overlay]:hover:lg:block`}
>
{item.pinned && <ThumbTackSolid className="inline-block absolute z-10 -top-4 rtl:left-4 ltr:right-4 size-5 rounded rtl:-rotate-12 ltr:rotate-12 bg-white p-1 fill-secondary-light" />}
{item.features?.includes("celebrate") && <Label value={translate("billboard-news-label-celebrate")} className="bg-[var(--brand-color)]" />}
{item.features?.includes("hot") && <Label value={translate("billboard-news-label-hot")} className="bg-primary" />}
{item.features?.includes("limited") && <Label value={translate("billboard-news-label-limited")} className="bg-violet-600" />}
<div
className={`flex w-full relative bg-white/75 rounded-lg
${item.features?.includes("celebrate") ? "!bg-[var(--brand-color)]" : ""}
${item.features?.includes("hot") ? "!bg-primary" : ""}
${item.features?.includes("limited") ? "!bg-violet-50/75" : ""}
`}>
<div className={`block w-full h-auto relative rounded-lg border-0 border-[var(--dominant-color)]
${item.features?.includes("celebrate") ? "!border-[var(--brand-color)]" : ""}
${item.features?.includes("hot") ? "!border-primary" : ""}
${item.features?.includes("limited") ? "!border-violet-600" : ""}
`}>
{pic ?
<Image
src={`${pic.id}/${pic.filename_download}`}
alt={translate("pic-of") + itemTitle}
width={pic.width}
height={pic.height}
quality={75}
ar={[pic.width / pic.height, pic.width / pic.height, pic.width / pic.height, pic.width / pic.height]}
imageSizes={[450, 900, 900, 900]}
priority={true}
noPreload={true}
fetchPriority="low"
className={`rounded-md object-contain`}
figureClass="rounded-md w-full select-none [&>div]:!bg-transparent"
/>
:
<span className="block size-full rounded-lg lg:rounded-md">
<BellSolid className="inline-block absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 size-16 xl:size-24 fill-gray-200" />
</span>
}
</div>
</div>
<div className={`pic-only-overlay hidden lg:hidden w-full h-full p-1 absolute top-0 left-0 rounded-lg`}>
<div className={`flex items-center justify-center w-full h-full bg-black/50 rounded-lg`}>
<span className={`inline-block text-base py-3 px-4 rounded-xl bg-[var(--dominant-color)] text-white lg:cursor-pointer
${item.features?.includes("celebrate") ? "!bg-[var(--brand-color)]" : ""}
${item.features?.includes("hot") ? "!bg-primary" : ""}
${item.features?.includes("limited") ? "!bg-violet-600" : ""}
`}>
<PartyHornSolid className="inline-block size-5 rounded fill-current rtl:ml-3 ltr:mr-3" />
{translate("see-details")}
</span>
</div>
</div>
</Link>
)
}
export default PicOnlyNews

View File

@ -0,0 +1,127 @@
import React, { useEffect, useState } from "react"
import Link from "components/link/link"
import useTranslate from "services/translation/translation"
import { getLocaleTr, getSmartTime, serverAddress, stripHtml, url, useGetRouter } from 'services/general/general'
import { BellSolid, EmptyHeart, EyeSolid, FilledHeart, ImageSharpSolid, PercentageSolid, PlusSolid, ThumbTackSolid } from "components/icons"
import { BillboardNewsItem, BillboardProduct } from "common/types/billboard"
import Image from "components/image/image"
import { getDominantColor } from "services/general/dominant-color"
interface RegularNewsProps {
item: BillboardNewsItem;
billboardId: string;
billboardTitle: string;
}
interface LabelProps {
value: string;
className: string;
}
const Label: React.FunctionComponent<LabelProps> = ({ value, className }) => {
return <div className={`inline-block absolute top-0 -translate-y-1/2 rtl:right-[7.5rem] ltr:left-[7.5rem] px-4 py-[2px] rounded z-10 text-white text-xs font-normal uppercase ${className}`}>
{value}
</div>
}
const RegularNews: React.FunctionComponent<RegularNewsProps> = ({ item, billboardId, billboardTitle }) => {
// states
const [dominantColor, setDominantColor] = useState<string>('#f3f4f6');
// variables
const translate = useTranslate();
const { locale } = useGetRouter();
const pic = item.gallery[0]?.directus_files_id;
const itemTitle = item.translations.filter((x: any) => x.languages_code.code.slice(0, 2) === locale)[0].title;
const itemContent = item.translations.filter((x: any) => x.languages_code.code.slice(0, 2) === locale)[0].content;
// effects
useEffect(() => {
const fetchDominantColor = async () => {
try {
const color = await getDominantColor(`${serverAddress()}${pic.id}/${pic.filename_download}`);
setDominantColor(color);
} catch (error) {
console.error('Error fetching dominant color:', error);
}
};
fetchDominantColor();
}, [item]);
return (
<Link
href={`/billboard/${billboardId}/${url(billboardTitle)}?news=${item.news_id}#updates`}
shallow
scroll={false}
target={"_self"}
className={`max-lg:reactive-button flex w-full relative bg-white shrink-0 border-b px-2 py-2 border-gray-100 lg:rounded-lg col-span-2 ${item.pinned ? "md:col-span-2" : "md:col-span-1"}
lg:cursor-pointer lg:hover:shadow-lg [&_.button]:hover:bg-[var(--brand-color)] [&_.button]:hover:text-white [&_.button]:hover:ring-[var(--brand-color)]
${item.features?.includes("celebrate") ? "[background-image:url('/pics/patterns/memphis-colorful.webp')] bg-contain bg-repeat bg-blend-multiply" : ""}
${item.features?.includes("hot") ? "!bg-error-bg" : ""}
${item.features?.includes("limited") ? "!bg-violet-50" : ""}
`}
>
{item.pinned && <ThumbTackSolid className="inline-block absolute -top-2 rtl:right-4 ltr:left-4 z-10 rtl:rotate-12 ltr:-rotate-12 size-5 rounded bg-white p-1 fill-secondary-light" />}
{item.features?.includes("celebrate") && <Label value={translate("billboard-news-label-celebrate")} className="bg-[var(--brand-color)]" />}
{item.features?.includes("hot") && <Label value={translate("billboard-news-label-hot")} className="bg-primary" />}
{item.features?.includes("limited") && <Label value={translate("billboard-news-label-limited")} className="bg-violet-600" />}
<div
style={{ "--dominant-color": dominantColor } as React.CSSProperties}
className={`flex w-full relative bg-white/75 px-1 py-1 rounded-lg
${item.features?.includes("celebrate") ? "!bg-white/75" : ""}
${item.features?.includes("hot") ? "!bg-error-bg/75" : ""}
${item.features?.includes("limited") ? "!bg-violet-50/75" : ""}
`}>
<div className={`block size-20 sm:size-28 relative shrink-0 mx-2 mt-2 border-[4px] rounded-full border-gray-100
${item.features?.includes("celebrate") ? "!border-[var(--brand-color)]" : ""}
${item.features?.includes("hot") ? "!border-primary" : ""}
${item.features?.includes("limited") ? "!border-violet-600" : ""}
`}>
{pic ?
<Image
src={`${pic.id}/${pic.filename_download}`}
alt={translate("pic-of") + itemTitle}
width={pic.width}
height={pic.height}
quality={75}
ar={[1 / 1, 1 / 1, 1 / 1, 1 / 1]}
imageSizes={[200, 250, 250, 250]}
priority={true}
noPreload={true}
fetchPriority="low"
className={`rounded-full object-cover`}
figureClass="rounded-full w-full select-none [&>div]:!bg-transparent"
/>
:
<span className="block size-full rounded-full bg-gray-50">
<BellSolid className="inline-block absolute top-1/2 left-1/2 -translate-x-1/2 -translate-y-1/2 size-7 xl:size-9 fill-gray-200" />
</span>
}
</div>
<div className="block w-full px-3 sm:px-4">
<h2 className={`text-sm/7 font-extrabold sm:text-lg text-secondary-light line-clamp-1 sm:line-clamp-1 mb-1 sm:mb-2 uppercase shrink-0`}>{itemTitle}</h2>
<p className="text-xs/6 sm:text-xs/6 first-letter:capitalize line-clamp-2 text-gray-500">{stripHtml(itemContent)}</p>
<div className="flex items-center justify-between w-full pt-4">
{/* seen & likes */}
<div className={`flex items-center space-x-2 rtl:space-x-reverse py-1 px-2 rounded-md bg-gray-100 ${item.features?.includes("hot") ? "bg-white" : ""} `}>
<div className="flex items-center space-x-1 rtl:space-x-reverse">
<FilledHeart className="inline-block size-[12px] fill-cool-gray" />
<span className="text-xs font-semibold text-secondary-light">{item.likes}</span>
</div>
<div className="flex items-center space-x-1 rtl:space-x-reverse">
<EyeSolid className="inline-block size-[14px] fill-cool-gray" />
<span className="text-xs font-semibold text-secondary-light">{item.seen_by}</span>
</div>
</div>
{/* news date & time */}
<span className="inline-block rtl:ml-2 ltr:mr-5 text-xs font-semibold text-secondary-light">{getSmartTime(item.date_created)}</span>
</div>
</div>
</div>
</Link>
)
}
export default RegularNews

View File

@ -0,0 +1,283 @@
import React, { useEffect, useState } from "react"
import Link from "components/link/link"
import useTranslate from "services/translation/translation"
import { fetchPublicData, getLocaleTr, hasValue, useGetRouter } from "services/general/general";
import { ChevronLeftSolid, ChevronRightSolid, MagnifyingGlass, XMark } from "components/icons";
import useSWR from "swr";
import { BillboardNewsCategory, BillboardNewsItem } from "common/types/billboard";
import RegularNews from "./cards/regular-news";
import InnerLoading from "components/loading/inner-loading";
import PicOnlyNews from "./cards/pic-only";
import Input from "components/input/text";
import Select from "components/select/select";
import Pagination from "../products/pagination";
interface BillboardNewsProps {
billboardId: string;
billboardTitle: string;
}
const BillboardNews: React.FunctionComponent<BillboardNewsProps> = ({ billboardId, billboardTitle }) => {
const translate = useTranslate();
// states
const [activeCat, setActiveCat] = useState(-1);
const [currentActiveCat, setCurrentActiveCat] = useState(1);
const [currentPage, setCurrentPage] = useState(1);
const [searchValue, setSearchValue] = useState("");
const [searchVisible, setSearchVisible] = useState(false);
const [newsFetched, setNewsFetched] = useState(false);
// variables
const { locale } = useGetRouter();
const itemsPerPage = 12;
const publicNewsQuery = `
billboard_news_aggregated
(
filter: {
billboards: { Billboards_id: { id: { _eq: "${billboardId}" }}},
translations: {
languages_code: { code: { _eq: "${locale === 'fa' ? 'fa-IR' : 'en-US'}" }},
},
private: { _eq: false },
status: { _eq: "published" }
},
sort: ["sort", "date_created"],
)
{
countDistinct {
id
}
}
billboard_news
(
filter: {
billboards: { Billboards_id: { id: { _eq: "${billboardId}" }}},
translations: {
languages_code: { code: { _eq: "${locale === 'fa' ? 'fa-IR' : 'en-US'}" }},
${hasValue(searchValue) ? `title: { _icontains: "${searchValue}" }` : ""}
},
_or: [
{
category: {
billboard_news_categories_id: {
parent: {
related_billboard_news_categories_id: {
cat_id: ${currentActiveCat === -1 ? `{ _neq: ${-1} }` : `{ _eq: ${currentActiveCat} }`}
}
}
}
}
},
{
category: {
billboard_news_categories_id: {
cat_id: ${currentActiveCat === -1 ? `{ _neq: ${-1} }` : `{ _eq: ${currentActiveCat} }`}
}
}
}
],
private: { _eq: false },
status: { _eq: "published" }
},
sort: ["sort", "-date_created"],
limit: ${itemsPerPage},
page: ${currentPage}
),
{
id
status
date_created
news_id
translations {
languages_code {
code
}
title
content
}
billboards {
Billboards_id {
id
}
}
category {
billboard_news_categories_id {
id
cat_id
translations {
languages_code {
code
}
name
}
has_children
parent {
related_billboard_news_categories_id {
id
cat_id
translations {
languages_code {
code
}
name
}
has_children
}
}
}
}
gallery {
directus_files_id {
id
description
filename_download
width
height
}
}
video_link
features
col_span
pic_only
private
pinned
likes
seen_by
}
`
const newsCategoriesQuery = `
billboard_news_categories
(
filter: {
related_billboard: { Billboards_id: { id: { _eq: "${billboardId}" }}},
status: { _eq: "published" }
},
sort: ["sort", "date_created"],
),
{
id
cat_id
translations {
languages_code {
code
}
name
}
has_children
}
`
const { data: publicNews, error: publicNewsError } = useSWR(['', publicNewsQuery], ([path, url]) => fetchPublicData(path, url));
const { data: newsCategories, error: newsCategoriesError } = useSWR(['', newsCategoriesQuery], ([path, url]) => fetchPublicData(path, url));
publicNewsError && console.log(publicNewsError);
newsCategoriesError && console.log(newsCategoriesError);
const publicNewsList: BillboardNewsItem[] = publicNews?.data.billboard_news;
const publicNewsTotalCount: number = publicNews?.data.billboard_news_aggregated[0].countDistinct.id;
const newsCategoriesList: BillboardNewsCategory[] = newsCategories?.data.billboard_news_categories;
const sortedNewsList = publicNews ? [ // news priority order
...publicNewsList.filter(x => x.pic_only && x.pinned).sort((a, b) => (Date.parse(b.date_created) - Date.parse(a.date_created))), // pinned pic-only
...publicNewsList.filter(x => x.pic_only && !x.pinned).sort((a, b) => (Date.parse(b.date_created) - Date.parse(a.date_created))), // regular pic-only
...publicNewsList.filter(x => x.pinned && !x.pic_only).sort((a, b) => (Date.parse(b.date_created) - Date.parse(a.date_created))), // pinned regular
...publicNewsList.filter(x => !x.pinned && !x.pic_only).sort((a, b) => (Date.parse(b.date_created) - Date.parse(a.date_created))) // regular
] : [];
// methods
const handleproductSearch = (text: string) => {
setSearchValue(text);
}
const handleCategorySelection = (cat: string) => {
setCurrentActiveCat(Number(newsCategoriesList.filter(x => getLocaleTr(x, locale).name === cat)[0].id));
}
// useEffects
useEffect(() => {
!newsFetched && publicNews && setNewsFetched(true);
}, [publicNews, newsFetched])
return (
<div className={``}>
{newsFetched ?
<>
<div className="block w-full bg-white border-b border-gray-200/75 mb-6 rounded-b-lg">
<div className="flex flex-col items-center w-full space-y-4 md:space-y-6 pt-6 pb-4 md:pt-8 md:pb-6">
<span className="block text-xl md:text-2xl font-extrabold text-secondary-light uppercase">{translate("billboard-news-intro-title")}</span>
<p className="block w-full max-md:px-4 md:w-[480px] mx-auto text-sm/7 md:text-sm/7 text-center first-letter:capitalize">"{translate("billboard-news-intro-text")}"</p>
</div>
<div className="flex items-center justify-between w-[calc(100vw_-_2rem)] lg:w-[calc(100%_-_2rem)] mx-4 pt-4 pb-4">
{/* filters */}
<div className={`${searchVisible ? "hidden" : "flex"} md:flex shrink-0 items-center space-x-1 rtl:space-x-reverse rounded-lg`}>
{newsCategories && <Select
id="news-cats-list"
label={translate("billboard-news-category-list-title")}
value={getLocaleTr(newsCategoriesList.filter(x => Number(x.cat_id) === currentActiveCat)[0], locale).name}
options={newsCategoriesList.map(x => getLocaleTr(x, locale).name)}
onChange={handleCategorySelection}
labelClass="text-sm px-1 font-semibold text-secondary-light capitalize"
className="inline-block px-2 rtl:mr-2 ltr:ml-2 rounded-lg bg-white capitalize text-sm"
wrapperClass="bg-gray-100/75 rounded-xl py-2 px-2"
/>}
</div>
{/* products search */}
<div className={`flex items-center ${searchVisible ? "max-md:w-full" : ""}`}>
<Input
type="search"
value={searchValue}
placeholder={translate("billboard-news-search-placeholder")}
stripeHtml
onInput={handleproductSearch}
className={`${searchVisible ? "block" : "hidden"} md:block w-full py-3 px-4 text-sm placeholder:text-xs placeholder:capitalize outline-none rounded-xl bg-gray-100/75`}
wrapperClass="w-full md:w-64"
/>
{searchVisible ?
<XMark className="inline-block md:hidden shrink-0 size-11 lg:size-3 fill-secondary-light p-3 rounded-xl bg-gray-100/75 rtl:mr-2 ltr:ml-2 lg:rtl:mr-2 lg:ltr:ml-2" onClick={() => setSearchVisible(false)} />
:
<MagnifyingGlass className="inline-block md:hidden shrink-0 size-11 lg:size-3 fill-secondary-light p-3 rounded-xl bg-gray-100/75 rtl:mr-2 ltr:ml-2 lg:rtl:mr-2 lg:ltr:ml-2" onClick={() => setSearchVisible(true)} />
}
</div>
</div>
</div>
{sortedNewsList.length > 0 ?
<>
<div className="grid grid-cols-2 lg:grid-cols-2 w-full gap-2 lg:gap-2 pb-4 mb-6 border-b border-dashed border-gray-200">
{sortedNewsList.filter(x => x.pic_only).map(x => (
<PicOnlyNews key={x.id} item={x} billboardId={billboardId} billboardTitle={billboardTitle} />
))}
</div>
<div className="grid grid-cols-2 lg:grid-cols-2 w-full gap-2 lg:gap-4 pb-4">
{sortedNewsList.filter(x => !x.pic_only).map(x => (
<RegularNews key={x.id} item={x} billboardId={billboardId} billboardTitle={billboardTitle} />
))}
</div>
</>
:
<div className="flex justify-center items-center">
<p className="block w-full text-center text-base/7 lg:text-base/9 text-secondary-light">" {translate("billboard-no-news-result")} "</p>
</div>
}
<Pagination
itemsCount={publicNewsTotalCount ?? 1}
activePage={currentPage}
pageSize={itemsPerPage}
onSelect={setCurrentPage}
wrapperClass="w-max pb-8 pt-2 lg:pt-8"
prevClass="!rounded-full"
nextClass="!rounded-full"
counterClass="!rounded-full"
/>
</>
:
<InnerLoading loadingText={""} width={"200"} height={"100"} />
}
</div>
)
}
export default BillboardNews

View File

@ -28,7 +28,7 @@ const ProductItem: React.FunctionComponent<ProductItemProps> = ({ item, billboar
return (
<Link
href={item.ad_item ? `/market/${item.ad_item.id}/${url(getLocaleTr(item.ad_item, locale).title)}` : `/billboard/${billboardId}/${url(billboardTitle)}?product=${item.id}#products`}
href={item.ad_item ? `/market/${item.ad_item.id}/${url(getLocaleTr(item.ad_item, locale).title)}` : `/billboard/${billboardId}/${url(billboardTitle)}?product=${item.product_id}#products`}
shallow
scroll={false}
target={item.ad_item ? "_blank" : "_self"}

View File

@ -30,7 +30,7 @@ const ReservationItem: React.FunctionComponent<ReservationItemProps> = ({ item,
return (
<Link
href={`/billboard/${billboardId}/${url(billboardTitle)}?product=${item.id}#products`}
href={`/billboard/${billboardId}/${url(billboardTitle)}?product=${item.product_id}#products`}
shallow
scroll={false}
target={item.ad_item ? "_blank" : "_self"}

View File

@ -8,11 +8,15 @@ interface PaginationProps {
itemsCount: number;
activePage: number;
pageSize: number;
wrapperClass?: string;
prevClass?: string;
nextClass?: string;
counterClass?: string;
onSelect: (page: number) => void;
}
const Pagination: React.FunctionComponent<PaginationProps> = ({ itemsCount, activePage = 1, pageSize = 12, onSelect }) => {
const Pagination: React.FunctionComponent<PaginationProps> = ({ itemsCount, activePage = 1, pageSize = 12, wrapperClass, prevClass, nextClass, counterClass, onSelect }) => {
const translate = useTranslate();
const { locale, query } = useGetRouter();
@ -49,14 +53,14 @@ const Pagination: React.FunctionComponent<PaginationProps> = ({ itemsCount, acti
}, [query.cat])
return (
<div className={`flex items-center mx-auto space-x-2 [direction:ltr] select-none`}>
<ChevronLeftSolid className="reactive-button inline-block shadow size-10 lg:size-10 fill-current lg:cursor-pointer rtl:ml-2 ltr:mr-2 lg:rtl:ml-2 lg:ltr:mr-2 p-3 bg-white text-secondary-light hover:text-[var(--brand-color)] rounded-lg" onClick={handlePrev} />
<div className="flex items-center justify-center w-16 space-x-1 py-2 px-2 rounded-lg bg-white shadow">
<div className={`flex items-center mx-auto space-x-2 [direction:ltr] select-none ${wrapperClass}`}>
<ChevronLeftSolid className={`reactive-button inline-block shadow size-10 lg:size-10 fill-current lg:cursor-pointer rtl:ml-2 ltr:mr-2 lg:rtl:ml-2 lg:ltr:mr-2 p-3 bg-white text-secondary-light hover:text-[var(--brand-color)] rounded-lg ${prevClass}`} onClick={handlePrev} />
<div className={`flex items-center justify-center w-16 space-x-1 py-2 px-2 rounded-lg bg-white shadow ${counterClass}`}>
<span className="inline-block text-base font-semibold text-[var(--brand-color)]">{`${active}`}</span>
<span className="inline-block text-base font-semibold text-gray-300">/</span>
<span className="inline-block text-base font-semibold text-gray-300">{`${numberOfPages}`}</span>
</div>
<ChevronRightSolid className="reactive-button inline-block shadow size-10 lg:size-10 fill-current lg:cursor-pointer rtl:ml-2 ltr:mr-2 lg:rtl:ml-2 lg:ltr:mr-2 p-3 bg-white text-secondary-light hover:text-[var(--brand-color)] rounded-lg" onClick={handleNext} />
<ChevronRightSolid className={`reactive-button inline-block shadow size-10 lg:size-10 fill-current lg:cursor-pointer rtl:ml-2 ltr:mr-2 lg:rtl:ml-2 lg:ltr:mr-2 p-3 bg-white text-secondary-light hover:text-[var(--brand-color)] rounded-lg ${nextClass}`} onClick={handleNext} />
</div>
)
}

View File

@ -211,6 +211,7 @@ const BillboardProducts: React.FunctionComponent<BillboardProductsProps> = ({ bi
{
id
date_created
product_id
type
product_card_type
translations {

View File

@ -4,12 +4,13 @@ import Footer from "../footer/footer"
import { useAppSelector, useAppDispatch } from 'common/redux/hooks';
import { setUserData, userData, userIsDataReady } from 'common/redux/slices/user'
import useSWR from "swr"
import useSWRImmutable from "swr"
import { userObject } from "services/user/user"
import Cookies from "../cookies/cookies"
import { adminAccessToken, adminFetchPrivateData, hasValue } from "services/general/general";
import { adminAccessToken, adminFetchPrivateData, fetchPublicData, hasValue } from "services/general/general";
import { getDictionary, setDictionary, setMenu } from "common/redux/slices/global";
import { useAuth } from "services/user/AuthContext";
import { useCachedGlobalData } from "services/general/global-data";
import { globalDataQuery, useCachedGlobalData } from "services/general/global-data";
interface LayoutProps {
children: React.ReactNode;
@ -39,8 +40,8 @@ const Layout = ({ children, fullScreen = false, bgColor = "#fff", trData, header
`
const { data: messagesData, error: messageFetchError } = useSWR(isUserDataAvailable ? [adminAccessToken, messagesDataQuery, ""] : null, ([token, query, route]) => adminFetchPrivateData(route, token, query));
const { data: globalData, error: globalDataError } = useCachedGlobalData();
// const { data: globalData, error: globalDataError } = useCachedGlobalData() : useSWRImmutable([globalDataQuery, ""], ([query, route]) => fetchPublicData(route, query));
// const { data: globalData, error: globalDataError } = useCachedGlobalData();
const { data: globalData, error: globalDataError } = process.env.NODE_ENV === 'production' ? useCachedGlobalData() : useSWRImmutable([globalDataQuery, ""], ([query, route]) => fetchPublicData(route, query));
// const { data: globalData, error: tDataError } = useSWRImmutable(!isDictionaryDataAvailable ? `${basePath()}/api/data/g-data` : null, fetcher);
// methods

View File

@ -159,6 +159,7 @@ export type BillboardProduct = {
id: string;
status: "draft" | "published" | "archived";
date_created: string;
product_id: number;
type: "reservation" | "purchasable" | "external" | "gallery" | "ad";
product_card_type: "realestate" | "vehicle" | "shop" | "event" | "reservation";
translations: {
@ -477,4 +478,65 @@ export type BillboardReservationSessionData = {
discount_price: number;
session_duration: number;
gap: number;
}
export type BillboardNewsCategory = {
id: string;
cat_id: number;
translations: {
languages_code: {
code: string;
}
name: string;
}
has_children: boolean;
parent: {
id: string;
related_billboard_news_categories_id: {
id: string;
translations: {
languages_code: {
code: string;
}
name: string;
}[]
has_children: boolean;
}
}[]
}
type AcceptedNewsFeatures = "celebrate" | "hot" | "limited";
export type BillboardNewsItem = {
id: number;
date_created: string;
news_id: number;
translations: {
languages_code: {
code: string;
};
title: string;
content: string;
cta: string;
cta_link: string;
}[];
billboards: Billboard[];
category: BillboardNewsCategory[];
gallery: {
directus_files_id: {
id: string;
description: string;
filename_download: string;
width: number;
height: number;
}
}[];
video_link: string;
features: AcceptedNewsFeatures[]
col_span: number;
pic_only: boolean;
private: boolean;
pinned: boolean;
likes: number;
seen_by: number;
}

View File

@ -28,6 +28,8 @@ import Breadcrumb from "components/breadcrumb/breadcrumb";
import { useAppSelector } from "common/redux/hooks";
import { getGlobalMenu } from "common/redux/slices/global";
import TopNav from "common/templates/billboard/page/top-nav";
import BillboardNews from "common/templates/billboard/page/news/news";
import NewsDetails from "common/templates/billboard/page/news-page/news-details";
const BillboardContact = dynamic(() => import("common/templates/billboard/page/contact"), { ssr: false })
@ -242,7 +244,14 @@ const BillboardPage = ({ data }: any) => {
}
</Tab>
<Tab id="updates" index={3} title={translate('billboard-tabs-updates')} icon={<BellSolid className={tabsIconClass} />}>
{router.asPath.includes("?news=") ?
query.news ?
<NewsDetails billboardId={billboard.id} billboardTitle={billboardTitle} brandColor={billboard.brand_color} />
:
<InnerLoading loadingText={""} width={"200"} height={"100"} />
:
<BillboardNews billboardId={billboard.id} billboardTitle={billboardTitle} />
}
</Tab>
</Tabs>
</div>
@ -339,6 +348,7 @@ export async function getStaticProps({ locale, params }: any) {
id
status
date_created
product_id
type
product_card_type
translations {

View File

@ -9,7 +9,7 @@
"./common/services/*"
]
},
"target": "es5",
"target": "esnext",
"lib": [
"dom",
"dom.iterable",