From e551b9b7ed6f8441f928c00d60d8cb5c4acff502 Mon Sep 17 00:00:00 2001 From: Ran Shamay Date: Thu, 16 Nov 2023 14:43:46 +0200 Subject: [PATCH 1/4] added rtl support --- Dockerfile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Dockerfile b/Dockerfile index 456d717..0952b87 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,7 +13,7 @@ FROM node:lts AS client WORKDIR /app COPY client/package.json client/package-lock.json . - +COPY client/src/components/semantic.rtl.min.css . RUN npm install npm@latest --global \ && npm install pnpm --global \ && pnpm install --prod @@ -37,6 +37,8 @@ COPY --from=server-dependencies --chown=node:node /app/node_modules node_modules COPY --from=client --chown=node:node /app/build public COPY --from=client --chown=node:node /app/build/index.html views/index.ejs +COPY --from=client --chown=node:node /app/semantic.rtl.min.css public/static/css/semantic.rtl.min.css + VOLUME /app/public/user-avatars VOLUME /app/public/project-background-images From 6c5619ec9eb5017c40b3afa4cb35813b23423bd0 Mon Sep 17 00:00:00 2001 From: Ran Shamay Date: Thu, 16 Nov 2023 14:45:07 +0200 Subject: [PATCH 2/4] use 3000 port --- Dockerfile | 2 +- charts/planka/README.md | 2 +- charts/planka/values.yaml | 4 ++-- client/src/constants/Config.js | 2 +- docker-compose.yml | 2 +- server/.env.sample | 2 +- 6 files changed, 7 insertions(+), 7 deletions(-) diff --git a/Dockerfile b/Dockerfile index 0952b87..93f0459 100644 --- a/Dockerfile +++ b/Dockerfile @@ -44,6 +44,6 @@ VOLUME /app/public/user-avatars VOLUME /app/public/project-background-images VOLUME /app/private/attachments -EXPOSE 1337 +EXPOSE 3000 CMD ["./start.sh"] diff --git a/charts/planka/README.md b/charts/planka/README.md index eebdb79..504388a 100644 --- a/charts/planka/README.md +++ b/charts/planka/README.md @@ -29,7 +29,7 @@ helm install planka . --set secretkey=$SECRETKEY To access Planka you can port forward using the following command: ```bash -kubectl port-forward $POD_NAME 3000:1337 +kubectl port-forward $POD_NAME 3000:3000 ``` ### Accessing Externally diff --git a/charts/planka/values.yaml b/charts/planka/values.yaml index 7d6e319..7c65796 100644 --- a/charts/planka/values.yaml +++ b/charts/planka/values.yaml @@ -45,7 +45,7 @@ securityContext: {} service: type: ClusterIP - port: 1337 + port: 3000 ingress: enabled: false @@ -106,6 +106,6 @@ persistence: enabled: false # existingClaim: netbox-data # storageClass: "-" - + accessMode: ReadWriteOnce size: 10Gi diff --git a/client/src/constants/Config.js b/client/src/constants/Config.js index 50128e0..4bc0493 100755 --- a/client/src/constants/Config.js +++ b/client/src/constants/Config.js @@ -5,7 +5,7 @@ const BASE_PATH = BASE_URL.replace(/^.*\/\/[^/]*(.*)[^?#]*.*$/, '$1'); const SERVER_BASE_URL = process.env.REACT_APP_SERVER_BASE_URL || - (process.env.NODE_ENV === 'production' ? BASE_URL : 'http://localhost:1337'); + (process.env.NODE_ENV === 'production' ? BASE_URL : 'http://localhost:3000'); const SERVER_HOST_NAME = SERVER_BASE_URL.replace(/^(.*\/\/[^/?#]*).*$/, '$1'); diff --git a/docker-compose.yml b/docker-compose.yml index c77790d..cf1be0b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -17,7 +17,7 @@ services: - project-background-images:/app/public/project-background-images - attachments:/app/private/attachments ports: - - 3000:1337 + - 3000:3000 environment: - BASE_URL=http://localhost:3000 - TRUST_PROXY=0 diff --git a/server/.env.sample b/server/.env.sample index 36de198..c4d1e78 100644 --- a/server/.env.sample +++ b/server/.env.sample @@ -1,6 +1,6 @@ ## Required -BASE_URL=http://localhost:1337 +BASE_URL=http://localhost:3000 DATABASE_URL=postgresql://postgres@localhost/planka SECRET_KEY=notsecretkey From cc59687b7130edee78c9e2934f36ebfe622d9140 Mon Sep 17 00:00:00 2001 From: Ran Shamay Date: Thu, 16 Nov 2023 15:38:12 +0200 Subject: [PATCH 3/4] align docker --- Dockerfile | 4 +- client/config-overrides.js | 13 ++ client/package-lock.json | 193 ++++++++++++++++-- client/package.json | 55 ++--- client/src/components/CardModal/CardModal.jsx | 3 + 5 files changed, 227 insertions(+), 41 deletions(-) diff --git a/Dockerfile b/Dockerfile index 93f0459..b8adecb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -13,7 +13,6 @@ FROM node:lts AS client WORKDIR /app COPY client/package.json client/package-lock.json . -COPY client/src/components/semantic.rtl.min.css . RUN npm install npm@latest --global \ && npm install pnpm --global \ && pnpm install --prod @@ -36,8 +35,9 @@ RUN mv .env.sample .env COPY --from=server-dependencies --chown=node:node /app/node_modules node_modules COPY --from=client --chown=node:node /app/build public +COPY --from=client --chown=node:node /app client + COPY --from=client --chown=node:node /app/build/index.html views/index.ejs -COPY --from=client --chown=node:node /app/semantic.rtl.min.css public/static/css/semantic.rtl.min.css VOLUME /app/public/user-avatars diff --git a/client/config-overrides.js b/client/config-overrides.js index 2ea9da5..172deef 100644 --- a/client/config-overrides.js +++ b/client/config-overrides.js @@ -1,5 +1,6 @@ const fs = require('fs'); const path = require('path'); +const CopyWebpackPlugin = require('copy-webpack-plugin'); const BASE_URL_PLACEHOLDER = 'BASE_URL_PLACEHOLDER'; @@ -39,6 +40,17 @@ const replaceBaseUrl = (compiler) => { }; module.exports = function override(config, env) { + config.plugins.push( + new CopyWebpackPlugin({ + patterns: [ + { + from: 'src/components/semantic.rtl.min.css', + to: 'static/css', + }, + ], + }), + ); + if (env === 'production') { const plugins = config.plugins.map((plugin) => { if (plugin.constructor.name === 'InterpolateHtmlPlugin') { @@ -48,6 +60,7 @@ module.exports = function override(config, env) { } return plugin; }); + return { ...config, output: { ...config.output, publicPath: BASE_URL_PLACEHOLDER }, diff --git a/client/package-lock.json b/client/package-lock.json index 8e3729d..cd99c9f 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -8,6 +8,7 @@ "dependencies": { "@juggle/resize-observer": "^3.4.0", "classnames": "^2.3.2", + "copy-webpack-plugin": "^11.0.0", "date-fns": "^2.29.3", "dequal": "^2.0.3", "easymde": "^2.18.0", @@ -7127,6 +7128,107 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, + "node_modules/copy-webpack-plugin": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-11.0.0.tgz", + "integrity": "sha512-fX2MWpamkW0hZxMEg0+mYnA40LTosOSa5TqZ9GYIBzyJa9C3QUaMPSE2xAi/buNr8u89SfD9wHSQVBzrRa/SOQ==", + "dependencies": { + "fast-glob": "^3.2.11", + "glob-parent": "^6.0.1", + "globby": "^13.1.1", + "normalize-path": "^3.0.0", + "schema-utils": "^4.0.0", + "serialize-javascript": "^6.0.0" + }, + "engines": { + "node": ">= 14.15.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^5.1.0" + } + }, + "node_modules/copy-webpack-plugin/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/copy-webpack-plugin/node_modules/ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "dependencies": { + "fast-deep-equal": "^3.1.3" + }, + "peerDependencies": { + "ajv": "^8.8.2" + } + }, + "node_modules/copy-webpack-plugin/node_modules/globby": { + "version": "13.2.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-13.2.2.tgz", + "integrity": "sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==", + "dependencies": { + "dir-glob": "^3.0.1", + "fast-glob": "^3.3.0", + "ignore": "^5.2.4", + "merge2": "^1.4.1", + "slash": "^4.0.0" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/copy-webpack-plugin/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "node_modules/copy-webpack-plugin/node_modules/schema-utils": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", + "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", + "dependencies": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + }, + "engines": { + "node": ">= 12.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/copy-webpack-plugin/node_modules/slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/core-js": { "version": "3.26.1", "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.26.1.tgz", @@ -9353,9 +9455,9 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "node_modules/fast-glob": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", - "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", @@ -10757,9 +10859,9 @@ } }, "node_modules/ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", + "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==", "engines": { "node": ">= 4" } @@ -28767,6 +28869,73 @@ "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", "integrity": "sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==" }, + "copy-webpack-plugin": { + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-11.0.0.tgz", + "integrity": "sha512-fX2MWpamkW0hZxMEg0+mYnA40LTosOSa5TqZ9GYIBzyJa9C3QUaMPSE2xAi/buNr8u89SfD9wHSQVBzrRa/SOQ==", + "requires": { + "fast-glob": "^3.2.11", + "glob-parent": "^6.0.1", + "globby": "^13.1.1", + "normalize-path": "^3.0.0", + "schema-utils": "^4.0.0", + "serialize-javascript": "^6.0.0" + }, + "dependencies": { + "ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "requires": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + } + }, + "ajv-keywords": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", + "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", + "requires": { + "fast-deep-equal": "^3.1.3" + } + }, + "globby": { + "version": "13.2.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-13.2.2.tgz", + "integrity": "sha512-Y1zNGV+pzQdh7H39l9zgB4PJqjRNqydvdYCDG4HFXM4XuvSaQQlEc91IU1yALL8gUTDomgBAfz3XJdmUS+oo0w==", + "requires": { + "dir-glob": "^3.0.1", + "fast-glob": "^3.3.0", + "ignore": "^5.2.4", + "merge2": "^1.4.1", + "slash": "^4.0.0" + } + }, + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, + "schema-utils": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.2.0.tgz", + "integrity": "sha512-L0jRsrPpjdckP3oPug3/VxNKt2trR8TcabrM6FOAAlvC/9Phcmm+cuAgTlxBqdBR1WJx7Naj9WHw+aOmheSVbw==", + "requires": { + "@types/json-schema": "^7.0.9", + "ajv": "^8.9.0", + "ajv-formats": "^2.1.1", + "ajv-keywords": "^5.1.0" + } + }, + "slash": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", + "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==" + } + } + }, "core-js": { "version": "3.26.1", "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.26.1.tgz", @@ -30387,9 +30556,9 @@ "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "fast-glob": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", - "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "requires": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", @@ -31420,9 +31589,9 @@ } }, "ignore": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.0.tgz", - "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==" + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", + "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==" }, "immer": { "version": "9.0.16", diff --git a/client/package.json b/client/package.json index b57a788..ee94636 100755 --- a/client/package.json +++ b/client/package.json @@ -55,62 +55,63 @@ "dependencies": { "@juggle/resize-observer": "^3.4.0", "classnames": "^2.3.2", - "date-fns": "^2.29.3", + "copy-webpack-plugin": "^11.0.0", + "date-fns": "^2.30.0", "dequal": "^2.0.3", "easymde": "^2.18.0", "history": "^5.3.0", - "i18next": "^22.0.6", - "i18next-browser-languagedetector": "^7.0.1", + "i18next": "^22.5.1", + "i18next-browser-languagedetector": "^7.2.0", "initials": "^3.1.2", - "js-cookie": "^3.0.1", + "js-cookie": "^3.0.5", "jwt-decode": "^3.1.2", "lodash": "^4.17.21", "node-sass": "^8.0.0", - "oidc-client-ts": "^2.3.0", - "photoswipe": "^5.3.3", + "oidc-client-ts": "^2.4.0", + "photoswipe": "^5.4.2", "prop-types": "^15.8.1", "react": "^18.2.0", "react-app-rewired": "^2.2.1", "react-beautiful-dnd": "^13.1.1", - "react-datepicker": "^4.8.0", + "react-datepicker": "^4.21.0", "react-dom": "^18.2.0", "react-dropzone": "^14.2.3", - "react-i18next": "^12.0.0", + "react-i18next": "^12.3.1", "react-input-mask": "^2.0.4", - "react-markdown": "^8.0.3", - "react-oidc-context": "^2.2.2", - "react-photoswipe-gallery": "^2.2.2", - "react-redux": "^8.0.5", - "react-router-dom": "^6.4.3", + "react-markdown": "^8.0.7", + "react-oidc-context": "^2.3.1", + "react-photoswipe-gallery": "^2.2.7", + "react-redux": "^8.1.3", + "react-router-dom": "^6.18.0", "react-scripts": "5.0.1", "react-simplemde-editor": "^5.2.0", - "react-textarea-autosize": "^8.4.0", - "redux": "^4.2.0", + "react-textarea-autosize": "^8.5.3", + "redux": "^4.2.1", "redux-logger": "^3.0.6", "redux-orm": "^0.16.2", - "redux-saga": "^1.2.1", - "remark-breaks": "^3.0.2", + "redux-saga": "^1.2.3", + "remark-breaks": "^3.0.3", "remark-gfm": "^3.0.1", - "reselect": "^4.1.7", + "reselect": "^4.1.8", "sails.io.js": "^1.2.1", "semantic-ui-css": "^2.5.0", - "semantic-ui-react": "^2.1.3", + "semantic-ui-react": "^2.1.4", "socket.io-client": "^2.5.0", - "validator": "^13.7.0", - "whatwg-fetch": "^3.6.2", + "validator": "^13.11.0", + "whatwg-fetch": "^3.6.19", "zxcvbn": "^4.4.2" }, "devDependencies": { - "@testing-library/jest-dom": "^5.16.5", + "@testing-library/jest-dom": "^5.17.0", "@testing-library/react": "^13.4.0", "@testing-library/user-event": "^13.5.0", "babel-preset-airbnb": "^5.0.0", - "chai": "^4.3.7", - "eslint": "^8.28.0", + "chai": "^4.3.10", + "eslint": "^8.53.0", "eslint-config-airbnb": "^19.0.4", - "eslint-plugin-import": "^2.26.0", - "eslint-plugin-jsx-a11y": "^6.6.1", - "eslint-plugin-react": "^7.31.11", + "eslint-plugin-import": "^2.29.0", + "eslint-plugin-jsx-a11y": "^6.8.0", + "eslint-plugin-react": "^7.33.2", "eslint-plugin-react-hooks": "^4.6.0", "react-test-renderer": "^18.2.0" } diff --git a/client/src/components/CardModal/CardModal.jsx b/client/src/components/CardModal/CardModal.jsx index d72ccca..65e9245 100755 --- a/client/src/components/CardModal/CardModal.jsx +++ b/client/src/components/CardModal/CardModal.jsx @@ -219,6 +219,7 @@ const CardModal = React.memo( >