diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000000..0b4af7b428 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,6 @@ +templates +settings +web +.drone.yml +install.sh +uninstall.sh \ No newline at end of file diff --git a/server/README.md b/server/README.md index ea5b674043..192b25b462 100644 --- a/server/README.md +++ b/server/README.md @@ -1,8 +1,576 @@ # Server HollaEx Kit has a built in server based on express + swagger and provides API and Websocket communication for HollaEx Web or any other clients. -API +The main 3rd-party applications being used throughout the Server are: +- [HollaEx Tools Library](https://github.com/bitholla/hollaex-tools-lib) +- [HollaEx Node Library](https://github.com/bitholla/hollaex-node-lib#readme) +- [Express v4.16.2](https://expressjs.com/en/api.html) +- [SwaggerUI v2.0.0](https://swagger.io/docs/specification/2-0/basic-structure/) +- [Sequelize v4.37.7](https://sequelize.org/v4/) -Websocket +To edit your own HollaEx Kit Server, you should be at least familiar with the applications above. + +## API + +All folders and files related to the RESTful API are in the `api` directory. The structure of this directory is: +- controllers +- helpers +- swagger + +### Controllers + +The files in the controllers folder hold all the functions that the endpoints connect to. The files are separated by the action that endpoints are responsible for: +- `admin.js` +- `deposit.js` +- `notification.js` +- `order.js` +- `otp.js` +- `public.js` +- `tier.js` +- `trade.js` +- `user.js` +- `withdrawal.js` + +### Helpers + +The files in the helpers folder hold all the helper functions used by the controller files above. Currently it is empty, but feel free to add any helper functions you need when customizing your own HollaEx Kit. + +### Swagger + +This folder holds the `swagger.yaml` file that contains all the endpoints for the server's RESTful API. + +### Other Relevant Directories + +- db + - Contains the database functions, migration files, models, seeders, triggers, and redis clients. + - Database used is `postgreSQL` +- mail + - This directory holds everything relevant to emails. + - The `strings` directory holds all the strings that contain email messages. They are separated by langauge e.g. `en.js`, `fa.js`, etc. + - The `templates` directory holds all the email templates. They are separated by the emails being send e.g. `welcome.js`, `deposit.js`, etc. + - The library used for emails is `Nodemailer`. +- config + - This directory holds all the configurations for different functionalities e.g. the database, redis, and logger. + +## Websocket + +All websocket related files are contained in the `ws` directory. The HollaEx Kit websocket uses the following libraries: +- [HollaEx Node Library](https://github.com/bitholla/hollaex-node-lib#readme) - Used to connect to the HollaEx Network Websocket +- [ws](https://www.npmjs.com/package/ws) - Used to create the websocket server +- [ws-heartbeat](https://www.npmjs.com/package/ws-heartbeat) - Used to maintain websocket connection to the Network and also to disconnect any clients that don't send a message within 60 seconds + +### Structure +- `channel.js` - Manages all the websocket channels that are connected to the Kit +- `hub.js` - Manages the Kit's connection to the HollaEx Network +- `index.js` - Handles all connections made to the Kit websocket +- `publicData.js` - Holds public orderbook and trade data received from the HollaEx Network +- `server.js` - The Kit websocket server is created here +- `sub.js` - Handles messages received from subscribers to the Kit Websocket +- `chat` - This directory holds all files related to the chat functionality of the Kit + +### How it works +The HollaEx Kit websocket acts as a proxy between clients and the HollaEx Network. All it does is forward data received from the Network to connected users. Because of this nature, users will not be able to connect to your Kit's websocket if your Kit is not connected to the HollaEx Network's websocket. If the Kit's connection to the HollaEx Network disconnects, all connections to the Kit will be dropped. The hub websocket will reconnect automatically. + +All clients connected to the Kit will need to send a message at least once per minute or their connection will be dropped from the Kit. + +For an easier way to connect to the websocket, use the [HollaEx Node Library](https://github.com/bitholla/hollaex-node-lib/blob/2.0/NETWORK_README.md#websocket). + +### Usage + +#### Connecting +Connect to the websocket through the endpoint `(API_URL)/stream`. + +```javascript +const WebSocket = require('ws'); +const API_URL = 'https://api.hollaex.com' + +// Public connection +const publicSocket = new WebSocket(`${API_URL}/stream`); +``` + +#### Authentication +For private events, you will need to authenticate your connection. You can do this in two ways: +- By including query parameters `authorization` for Bearer Tokens or `api-key`, `api-signature`, and `api-expires` for Hmac keys during connection. +- By sending an authorization message after connection. + +Javascript Example: + +```javascript +const WebSocket = require('ws'); +const API_URL = 'https://api.hollaex.com' + +// Bearer Token +const privateSocket = new WebSocket(`${API_URL}/stream?authorization=${BEARER_TOKEN}`); + +// HMAC Authentication +const privateSocket = new WebSocket(`${API_URL}/stream?api-key=${API_KEY}&api-signature=${API_SIGNATURE}&api-expires=${API_EXPIRES}`); + +// Bearer Token +privateSocket.send( + JSON.stringify({ + op: 'auth', + args: [{ + authorization: BEARER_TOKEN + }] + }) +); + +// HMAC Authentication +privateSocket.send( + JSON.stringify({ + op: 'auth', + args: [{ + 'api-key': API_KEY, + 'api-signature': API_SIGNATURE, + 'api-expires': API_EXPIRES + }] + }) +); +``` + +#### Subscribing +There are four events you can subscribe to: +- orderbook +- trade +- wallet (private) +- order (private) + +To subscribe to an event, send a message with an `op` and `args` in a stringified object. You must authentication your connection for private events. For public events, you can also pass a symbol to subscribe to specific symbols. + +Javascript example: +``` javascript +publicSocket.on('open', () => { + publicSocket.send( + JSON.stringify({ + op: 'subscribe', + args: ['orderbook', 'trade:xht-usdt'] + }) + ); +}); + +privateSocket.on('open', () => { + privateSocket.send( + JSON.stringify({ + op: 'subscribe', + args: ['wallet', 'order'] + }) + ); +}); +``` + +#### Unsubscribing +You can unsubscribe by following the same format as subscription + +Javascript example: +``` javascript +publicSocket.send( + JSON.stringify({ + op: 'unsubscribe', + args: ['orderbook', 'trade:xht-usdt'] + }) +); + +privateSocket.send( + JSON.stringify({ + op: 'unsubscribe', + args: ['wallet', 'order'] + }) +); +``` + +#### Receiving Data +Once subscribed, you will receive data from events when the websocket `message` event triggers. + +Javascript example: +``` javascript +socket.on('message', (data) => { + data = JSON.parse(data); + console.log(data); +}; +``` + +These are exapmles of data responses from the server. + +- **orderbook**: Updates related to the user's private information are as follows: + + ```json + { + "topic": "orderbook", + "action": "partial", + "symbol": "xht-usdt", + "data": { + "bids": [ + [0.1, 0.1], + ... + ], + "asks": [ + [1, 1], + ... + ], + "timestamp": "2020-12-15T06:45:27.766Z" + }, + "time": 1608015328 + } + ``` + +- **trade**: Updates related to the user's private information are as follows: + + ```json + { + "topic": "trade", + "action": "partial", + "symbol": "xht-usdt", + "data": [ + { + "size": 0.012, + "price": 300, + "side": "buy", + "timestamp": "2020-12-15T07:25:28.887Z" + }, + ... + ], + "time": 1608015328 + } + ``` + +- **wallet**: Updates related to the user's private information are as follows: + + ```json + { + "topic": "wallet", + "action": "partial", + "user_id": 1, + "data": { + "usdt_balance": 1, + "usdt_available": 1, + "xht_balance": 1, + "xht_available": 1, + "xmr_balance": 1, + "xmr_available": 1, + "btc_balance": 1, + "btc_available": 1, + "eth_balance": 1, + "eth_available": 1, + ..., + "updated_at": "2020-12-15T08:41:24.048Z" + }, + "time": 1608021684 + } + ``` + +- **order**: Websocket messages relating the the user's orders. + - The `status` of the order can be `new`, `pfilled`, `filled`, and `canceled`. + - The `action` of the data determines what caused it to happen. All three are explained below: + + - `partial`: All previous and current orders. Is the first order data received when connecting. Max: 50. Descending order. + + ```json + { + "topic": "order", + "action": "partial", + "user_id": 1, + "data": [ + { + "id": "7d3d9545-b7e6-4e7f-84a0-a39efa4cb173", + "side": "buy", + "symbol": "xht-usdt", + "type": "limit", + "size": 0.1, + "filled": 0, + "price": 1, + "stop": null, + "status": "new", + "fee": 0, + "fee_coin": "xht", + "meta": {}, + "fee_structure": { + "maker": 0.1, + "taker": 0.1 + }, + "created_at": "2020-11-30T07:45:43.819Z", + "created_by": 1 + }, + ... + ], + "time": 1608022610 + } + ``` + + - `insert`: When user's order is added. The status of the order can be either `new`, `pfilled`, or `filled`. + + ```json + { + "topic": "order", + "action": "insert", + "user_id": 1, + "symbol": "xht-usdt", + "data": [ + { + "id": "7d3d9545-b7e6-4e7f-84a0-a39efa4cb173", + "side": "buy", + "symbol": "xht-usdt", + "type": "limit", + "size": 0.1, + "filled": 0, + "price": 1, + "stop": null, + "status": "new", + "fee": 0, + "fee_coin": "xht", + "meta": {}, + "fee_structure": { + "maker": 0.1, + "taker": 0.1 + }, + "created_at": "2020-11-30T07:45:43.819Z", + "updated_at": "2020-12-15T08:56:45.066Z", + "created_by": 1 + }, + ... + ], + "time": 1608022610 + } + ``` + + - `update`: When user's order status is updated. Status can be `pfilled`, `filled`, and `canceled`. + + ```json + { + "topic": "order", + "action": "insert", + "user_id": 1, + "symbol": "xht-usdt", + "data": [ + { + "id": "7d3d9545-b7e6-4e7f-84a0-a39efa4cb173", + "side": "buy", + "symbol": "xht-usdt", + "type": "limit", + "size": 0.1, + "filled": 0, + "price": 1, + "stop": null, + "status": "new", + "fee": 0, + "fee_coin": "xht", + "meta": {}, + "fee_structure": { + "maker": 0.1, + "taker": 0.1 + }, + "created_at": "2020-11-30T07:45:43.819Z", + "updated_at": "2020-12-15T08:56:45.066Z", + "created_by": 1 + }, + ... + ], + "time": 1608022610 + } + ``` + +## Plugins + +The Kit plugins are handled within one file, `plugins.js`. The main libraries being used are: +- [eval v0.1.4](https://github.com/pierrec/node-eval) - Used to execute plugin scripts +- [Express v4.16.2](https://expressjs.com/en/api.html) + +### How it works + +The plugins are designed to be contained within their own process. They can be installed, updated, and uninstalled on the fly. Manipulating any plugin will not affect the actual API server. Plugins are stored in the database in the `Plugin` table. + +#### Plugin structure + +```json +{ + "version": 1, // Plugin version + "name": "string", // Name of plugin, must be unique + "author": "string", // Author of plugin + "bio": "string", // Plugin bio + "enabled": true, // Plugin enabled status + "description": "string", // Plugin description + "documentation": "string", // Plugin documentation. Will be in markdown format + "logo": "string", // URL of plugin logo + "icon": "string", // URL of plugin icon + "url": "string", // URL of plugin (where it can be found) + "meta": {}, // Variables that will be unique for each instance of this plugin + "prescript": { // Scripts to run before installing plugin + "run": "string", + "install": [] + }, + "postscript": { // Scripts to run before uninstalling plugin + "run": "string" + }, + "script": "string", // Plugin script to run after installation + "admin_view": "string", // Client code for admin panel + "web_view": "string" // Client code for web view +} +``` + +#### Meta +The meta object is for any unique values your plugin requires. For example, if you are building a plugin using AWS S3, you will need a way to input your AWS credentials. You can use the meta object to store this values so they will be accessable in your plugin. + +```javascript +{ + "meta": { + "key": "key", + "secret": "secret" + } +} +``` + +Meta values are stored in the script as `meta`. Our `meta` values can be accessed as follows: +```javascript +const { key, secret } = meta; +``` + +#### Libraries + +- The HollaEx Plugins can be developed using the installed libraries below. They are accessable in the script as the value in the parentheses. + + - HollaEx Tools Library (`toolsLib`) + - Express (`app`) + - Lodash (`lodash`) + - Express Validator (`expressValidator`) + - Winston logger `loggerPlugin` + - Multer (`multer`) + +If your plugin requires other libraries, add the `npm` library name to the `install` array in `prescript`. +```javascript +{ + "prescript": { + "install": ['npm-library'] + } +} +``` + +Installed libraries are stored in the script as `installedLibraries`. +```javascript +const npmLibrary = installedLibraries['npm-library']; +``` + + +#### Script +Scripts must be written in ES6+ becuase they will be minified before installation. If this minification fails, the installation will fail as well. The script is what runs after you install and enable the plugin. All endpoints created must start with the path `/plugins`. +```javascript +app.get('/plugins/say-hi', (req, res) => { + res.send('hi'); +}); +``` + +### Endpoints + +#### `GET /plugins` + - Will return paginated data of plugins installed on Kit + - Query parameters: + - `limit` + - `page` + - `search` - Get plugins with names that match this value +#### `POST /plugins` + - Installs a new plugin into the kit + - Only admins can access this + - Returns the newly created plugin and restarts plugin server if plugin is enabled + - Body values: + ```jsx + { + name: 'string', + script: 'string', + version: 'int', + description: 'string', + author: 'string', + enabled: 'boolean', + bio: 'string', //optional + documentation: 'string', //optional + icon: 'string', //optional + url: 'string', //optional + logo: 'string', //optional + admin_view: 'string', //optional + web_view: 'string', //optional + prescript: { //optional + install: 'array', + run: 'string' + }, + postscript: { //optional + run: 'string' + }, + meta: { //optional + ...any values + } + } + ``` +#### `PUT /plugins` + - Updates an existing plugin to a new version + - Returns the updated plugin and restarts plugin server if plugin in enabled + - Only admin can access this endpoint + - Body values: + ```jsx + { + name: 'string', + version: 'int', //Version must be different from the one already installed + script: 'string', + description: 'string', //optional + author: 'string', //optional + bio: 'string', //optional + documentation: 'string', //optional + icon: 'string', //optional + url: 'string', //optional + logo: 'string', //optional + admin_view: 'string', //optional + web_view: 'string', //optional + prescript: { //optional + install: 'array', + run: 'string' + }, + postscript: { //optional + run: 'string' + }, + meta: { //optional + ...any values + } + } + ``` +#### `DELETE /plugins` + - Uninstalls a plugin from the Kit and restarts the plugin server + - Returns Success message + - Only admins can access this + - Query parameters: + - `name` - name of the plugin to uninstall +#### `GET /plugins/enable` + - Will enable a plugin if disabled + - Only admin can access this endpoint + - Returns a success message and restarts plugin server + - Query parameters: + - `name` - name of plugin to enable +#### `GET /plugins/disable` + - Will disable a plugin if enabled + - Only admin can access this endpoint + - Returns a success message and restarts plugin server + - Query parameters: + - `name` - name of plugin to disable +#### `GET /plugins/script` + - Will return `name`, `version`, `script`, `prescript`, `postscript`, and `admin_view` for plugin + - Only admin can access this endpoint + - Query parameters: + - `name` - name of plugin +#### `GET /plugins/meta` + - Will return the meta values for the plugin + - Only admin can access this endpoint + - Returns `name`, `version`, and `meta` for plugin + - Query parameters: + - `name` - name of plugin +#### `PUT /plugins/meta` + - Update the meta object for a plugin. This meta object stores values used within a plugin + - Only admin can access this endpoint + - Returns a success message and restarts the plugin server + - Body values: + ```jsx + { + name: 'string', + meta: { + ...any values + // will only update values that are both in the original plugin meta + // and that are passed within this object. The meta object passed + // through this endpoint doesn't completely override the existing meta + } + } + ``` -Plugins diff --git a/server/api/swagger/swagger.yaml b/server/api/swagger/swagger.yaml index 0e6177991e..6ce3468a62 100644 --- a/server/api/swagger/swagger.yaml +++ b/server/api/swagger/swagger.yaml @@ -1,6 +1,6 @@ swagger: "2.0" info: - version: "2.0.4" + version: "2.0.5" title: HollaEx Kit host: api.hollaex.com basePath: /v2 @@ -955,7 +955,7 @@ paths: description: This will send a confirmation email to the email address on record, if activate OTP, it will require OTP as well. tags: - User - - Withdraw + - Withdrawal parameters: - name: data description: withdrawal information @@ -985,7 +985,7 @@ paths: description: confirm a withdrawal request tags: - User - - Withdraw + - Withdrawal parameters: - name: data description: Tansfer information diff --git a/server/app.js b/server/app.js index c68670dfc1..3fcd52ce33 100644 --- a/server/app.js +++ b/server/app.js @@ -2,12 +2,16 @@ const { createServer } = require('http'); var SwaggerExpress = require('swagger-express-mw'); +const swaggerUi = require('swagger-ui-express'); const morgan = require('morgan'); +var YAML = require('yamljs'); +var swaggerDoc = YAML.load('./api/swagger/swagger.yaml'); const { logEntryRequest, stream, logger } = require('./config/logger'); const { domainMiddleware, helmetMiddleware } = require('./config/middleware'); var app = require('express')(); const toolsLib = require('hollaex-tools-lib'); const { checkStatus } = require('./init'); +const { API_HOST, CUSTOM_CSS } = require('./constants'); checkStatus(); // listen through pubsub for configuration/init @@ -36,6 +40,33 @@ var config = { } }; +swaggerDoc.host = API_HOST; +if (process.env.NODE_ENV === 'production') { + swaggerDoc.schemes = ['https']; + Object.entries(swaggerDoc.paths).forEach(([path, pathContent], index) => { + Object.keys(pathContent).forEach((method) => { + if (method.indexOf('swagger') === -1) { + if (pathContent[method].hasOwnProperty('tags')) { + const tags = pathContent[method].tags; + const index = tags.findIndex((value) => value === 'Admin' || value === 'Notification'); + if (index > -1) { + delete pathContent[method]; + } + } + } + }); + }); +} + +var options = { + customCss: CUSTOM_CSS, + customSiteTitle: 'HollaEx Kit API Explorer', + customfavIcon: + 'https://rm-content.s3.amazonaws.com/5aead825bb456c005e2322dd/upload-c116da40-ebd2-11e8-9c84-e32cc39c32d1_57.png' +}; + +app.use('/docs', swaggerUi.serve, swaggerUi.setup(swaggerDoc, options)); + SwaggerExpress.create(config, function(err, swaggerExpress) { if (err) { throw err; } diff --git a/server/constants.js b/server/constants.js index 233a566846..38dac31ec4 100644 --- a/server/constants.js +++ b/server/constants.js @@ -54,10 +54,10 @@ subscriber.on('message', (channel, message) => { if (data.secrets) updateSecrets(data.secrets); break; case 'freezeUser': - updateFrozenUser(data, 'add'); + updateFrozenUser('add', data); break; case 'unfreezeUser': - updateFrozenUser(data, 'remove'); + updateFrozenUser('remove', data); break; case 'stop': resetAllConfig(); @@ -438,4 +438,29 @@ exports.VERIFY_STATUS = { REJECTED: 2, COMPLETED: 3 }; -// PLUGIN CONSTANTS END ------------------------------ to be moved \ No newline at end of file +// PLUGIN CONSTANTS END ------------------------------ to be moved + +exports.CUSTOM_CSS = ` + .topbar-wrapper img { + content:url('https://s3.ap-northeast-2.amazonaws.com/public-holla-images/bitholla/bitholla-white--01.png'); + height: 2rem; + } + .swagger-ui .opblock.opblock-get .opblock-summary-method { + background: blue; + } + .swagger-ui .btn.execute { + background-color: blue; + color: #fff; + border-color: blue; + } + .swagger-ui .btn.authorize { + color: blue; + border-color: blue; + } + .swagger-ui .btn.authorize svg { + fill: blue; + } + .models { + display: none !important; + } +`; diff --git a/server/db/seeders/20190825120350-add-status.js b/server/db/seeders/20190825120350-add-status.js index b2f452ca9e..a3f0f29a53 100644 --- a/server/db/seeders/20190825120350-add-status.js +++ b/server/db/seeders/20190825120350-add-status.js @@ -9,7 +9,6 @@ const { EMAILS_TIMEZONE, VALID_LANGUAGES, NEW_USER_DEFAULT_LANGUAGE, - SENDER_EMAIL, DEFAULT_THEME, NEW_USER_IS_ACTIVATED, SMTP_SERVER, @@ -19,7 +18,6 @@ const { SEND_EMAIL_TO_SUPPORT, ALLOWED_DOMAINS, CAPTCHA_SECRET_KEY, - ADMIN_EMAIL, CAPTCHA_SITE_KEY, ADMIN_WHITELIST_IP, ACTIVATION_CODE, @@ -106,8 +104,8 @@ module.exports = { emails: { timezone: EMAILS_TIMEZONE || '', send_email_to_support: (SEND_EMAIL_TO_SUPPORT && SEND_EMAIL_TO_SUPPORT === 'true') || false, - sender: SENDER_EMAIL || '', - audit: ADMIN_EMAIL || '' + sender: '', + audit: '' }, captcha: { secret_key: CAPTCHA_SECRET_KEY diff --git a/server/init.js b/server/init.js index d8d63acd9a..6404b6fe45 100644 --- a/server/init.js +++ b/server/init.js @@ -126,6 +126,9 @@ const checkStatus = () => { created_at: exchange.created_at, expiry: exchange.expiry, collateral_level: exchange.collateral_level, + type: exchange.type, + plan: exchange.plan, + period: exchange.period, status: true, initialized: status.initialized }; diff --git a/server/mail/strings/en.js b/server/mail/strings/en.js index 8ac6c86d39..036808893d 100644 --- a/server/mail/strings/en.js +++ b/server/mail/strings/en.js @@ -54,7 +54,7 @@ const WELCOME = { BODY: { 1: () => `Thank you for signing up to ${API_NAME()}.`, 2: (account, deposit) => ` - To being trading, you must first deposit cryptocurrency or fund money to your account. + To begin trading, you must first deposit cryptocurrency or fund money to your account. Please go to your ${account} and visit the ${deposit} page.`, 3: 'account', 4: 'deposit', diff --git a/server/package.json b/server/package.json index 67b37ac3b5..a003eace50 100644 --- a/server/package.json +++ b/server/package.json @@ -1,5 +1,5 @@ { - "version": "2.0.4", + "version": "2.0.5", "private": false, "description": "HollaEx Kit", "keywords": [ @@ -57,6 +57,7 @@ "request-promise": "4.2.2", "sequelize": "4.37.7", "swagger-express-mw": "0.1.0", + "swagger-ui-express": "4.0.7", "uglify-es": "3.3.9", "uuid": "3.2.1", "validator": "9.4.1", diff --git a/server/tools/dbs/checkConfig.js b/server/tools/dbs/checkConfig.js index 4ae7171bef..32e0d679e6 100644 --- a/server/tools/dbs/checkConfig.js +++ b/server/tools/dbs/checkConfig.js @@ -60,8 +60,8 @@ Status.findOne() emails: { timezone: existingSecrets.emails ? (existingSecrets.emails.timzeone || process.env.EMAILS_TIMEZONE || '') : (process.env.EMAILS_TIMEZONE || ''), send_email_to_support: existingSecrets.emails ? (existingSecrets.emails.send_email_to_support || (process.env.SEND_EMAIL_TO_SUPPORT && process.env.SEND_EMAIL_TO_SUPPORT === 'true') || false) : ((process.env.SEND_EMAIL_TO_SUPPORT && process.env.SEND_EMAIL_TO_SUPPORT === 'true') || false), - sender: existingSecrets.emails ? (existingSecrets.emails.sender || process.env.SENDER_EMAIL || '') : (process.env.SENDER_EMAIL || ''), - audit: existingSecrets.audit ? (existingSecrets.emails.audit || process.env.ADMIN_EMAIL || '') : (process.env.ADMIN_EMAIL || '') + sender: existingSecrets.emails ? (existingSecrets.emails.sender || '') : '', + audit: existingSecrets.emails ? (existingSecrets.emails.audit || '') : '' }, captcha: { secret_key: existingSecrets.captcha ? (existingSecrets.captcha.secret_key || process.env.CAPTCHA_SECRET_KEY) : process.env.CAPTCHA_SECRET_KEY @@ -70,7 +70,7 @@ Status.findOne() server: existingSecrets.smtp ? (existingSecrets.smtp.server || process.env.SMTP_SERVER || '') : (process.env.SMTP_SERVER || ''), port: existingSecrets.smtp ? (existingSecrets.smtp.port || process.env.SMTP_PORT || 587) : (process.env.SMTP_PORT || 587), user: existingSecrets.smtp ? (existingSecrets.smtp.user || process.env.SMTP_USER) : process.env.SMTP_USER, - password: existingSecrets.smtp ? (existingSecrets.smtp.password || process.env.SMTP_PASSWORD) : (process.env.SMTP_PASSWORD) + password: existingSecrets.smtp ? (existingSecrets.smtp.password || process.env.SMTP_PASSWORD) : process.env.SMTP_PASSWORD } }; diff --git a/server/tools/dbs/setConfig.js b/server/tools/dbs/setConfig.js index 215a2f0968..06aee02ca1 100644 --- a/server/tools/dbs/setConfig.js +++ b/server/tools/dbs/setConfig.js @@ -11,7 +11,6 @@ const { EMAILS_TIMEZONE, VALID_LANGUAGES, NEW_USER_DEFAULT_LANGUAGE, - SENDER_EMAIL, DEFAULT_THEME, NEW_USER_IS_ACTIVATED, SMTP_SERVER, @@ -21,7 +20,6 @@ const { SEND_EMAIL_TO_SUPPORT, ALLOWED_DOMAINS, CAPTCHA_SECRET_KEY, - ADMIN_EMAIL, CAPTCHA_SITE_KEY, ADMIN_WHITELIST_IP, NATIVE_CURRENCY @@ -78,8 +76,8 @@ const secrets = { emails: { timezone: EMAILS_TIMEZONE || '', send_email_to_support: (SEND_EMAIL_TO_SUPPORT && SEND_EMAIL_TO_SUPPORT === 'true') || false, - sender: SENDER_EMAIL || '', - audit: ADMIN_EMAIL || '' + sender: '', + audit: '' }, captcha: { secret_key: CAPTCHA_SECRET_KEY diff --git a/settings/configmap b/settings/configmap index 789ce6a754..8c8d50fd53 100644 --- a/settings/configmap +++ b/settings/configmap @@ -27,11 +27,11 @@ HOLLAEX_CONFIGMAP_API_HOST=http://localhost HOLLAEX_CONFIGMAP_USER_LEVEL_NUMBER=4 -HOLLAEX_CONFIGMAP_ADMIN_EMAIL=admin@bitholla.com +HOLLAEX_CONFIGMAP_ADMIN_EMAIL= HOLLAEX_CONFIGMAP_KYC_EMAIL= HOLLAEX_CONFIGMAP_SUPPORT_EMAIL=support@bitholla.com HOLLAEX_CONFIGMAP_SUPERVISOR_EMAIL= -HOLLAEX_CONFIGMAP_SENDER_EMAIL=$HOLLAEX_CONFIGMAP_SUPPORT_EMAIL +HOLLAEX_CONFIGMAP_SENDER_EMAIL= HOLLAEX_CONFIGMAP_NEW_USER_IS_ACTIVATED=true @@ -118,24 +118,24 @@ ENVIRONMENT_USER_HOLLAEX_CORE_IMAGE_VERSION=$ENVIRONMENT_DOCKER_IMAGE_VERSION ENVIRONMENT_USER_HOLLAEX_WEB_IMAGE_REGISTRY=bitholla/my-hollaex-web ENVIRONMENT_USER_HOLLAEX_WEB_IMAGE_VERSION=latest -ENVIRONMENT_KUBERNETES_API_CPU_LIMITS=200m +ENVIRONMENT_KUBERNETES_API_CPU_LIMITS=100m ENVIRONMENT_KUBERNETES_API_MEMORY_LIMITS=512Mi -ENVIRONMENT_KUBERNETES_API_CPU_REQUESTS=10m +ENVIRONMENT_KUBERNETES_API_CPU_REQUESTS=50m ENVIRONMENT_KUBERNETES_API_MEMORY_REQUESTS=512Mi -ENVIRONMENT_KUBERNETES_STREAM_CPU_LIMITS=200m +ENVIRONMENT_KUBERNETES_STREAM_CPU_LIMITS=100m ENVIRONMENT_KUBERNETES_STREAM_MEMORY_LIMITS=256Mi -ENVIRONMENT_KUBERNETES_STREAM_CPU_REQUESTS=10m -ENVIRONMENT_KUBERNETES_STREAM_MEMORY_REQUESTS=128Mi +ENVIRONMENT_KUBERNETES_STREAM_CPU_REQUESTS=50m +ENVIRONMENT_KUBERNETES_STREAM_MEMORY_REQUESTS=256Mi ENVIRONMENT_KUBERNETES_POSTGRESQL_CPU_LIMITS=100m -ENVIRONMENT_KUBERNETES_POSTGRESQL_MEMORY_LIMITS=200Mi -ENVIRONMENT_KUBERNETES_POSTGRESQL_CPU_REQUESTS=10m +ENVIRONMENT_KUBERNETES_POSTGRESQL_MEMORY_LIMITS=100Mi +ENVIRONMENT_KUBERNETES_POSTGRESQL_CPU_REQUESTS=100m ENVIRONMENT_KUBERNETES_POSTGRESQL_MEMORY_REQUESTS=100Mi ENVIRONMENT_KUBERNETES_REDIS_CPU_LIMITS=100m -ENVIRONMENT_KUBERNETES_REDIS_MEMORY_LIMITS=200Mi -ENVIRONMENT_KUBERNETES_REDIS_CPU_REQUESTS=10m +ENVIRONMENT_KUBERNETES_REDIS_MEMORY_LIMITS=100Mi +ENVIRONMENT_KUBERNETES_REDIS_CPU_REQUESTS=100m ENVIRONMENT_KUBERNETES_REDIS_MEMORY_REQUESTS=100Mi ENVIRONMENT_KUBERNETES_S3_BACKUP_CRONJOB_RULE='0 1 * * *' diff --git a/version b/version index 26e33797ac..b9d2bdfd65 100644 --- a/version +++ b/version @@ -1 +1 @@ -2.0.4 \ No newline at end of file +2.0.5 \ No newline at end of file diff --git a/web/package.json b/web/package.json index 08d4cc18ba..2900f4a9d7 100644 --- a/web/package.json +++ b/web/package.json @@ -1,6 +1,6 @@ { "name": "hollaex-kit", - "version": "2.0.4", + "version": "2.0.5", "private": true, "dependencies": { "@ant-design/compatible": "1.0.5", diff --git a/web/public/assets/icons/xlm-icon.svg b/web/public/assets/icons/xlm-icon.svg index e48fd929ab..c43b57ebcf 100644 --- a/web/public/assets/icons/xlm-icon.svg +++ b/web/public/assets/icons/xlm-icon.svg @@ -2,14 +2,10 @@ - - - - - - + + diff --git a/web/public/assets/images/terms_of_service_and_privacy_policy-tooltip.png b/web/public/assets/images/terms_of_service_and_privacy_policy-tooltip.png new file mode 100644 index 0000000000..136cff08bc Binary files /dev/null and b/web/public/assets/images/terms_of_service_and_privacy_policy-tooltip.png differ diff --git a/web/public/assets/images/user-email-unverified.svg b/web/public/assets/images/user-email-unverified.svg new file mode 100644 index 0000000000..003c7416df --- /dev/null +++ b/web/public/assets/images/user-email-unverified.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + diff --git a/web/public/assets/images/user-email-verified.svg b/web/public/assets/images/user-email-verified.svg new file mode 100644 index 0000000000..0ba007fe78 --- /dev/null +++ b/web/public/assets/images/user-email-verified.svg @@ -0,0 +1,17 @@ + + + + + + + + + + + + + diff --git a/web/public/assets/images/user-email.svg b/web/public/assets/images/user-email.svg new file mode 100644 index 0000000000..c2ca44d0e0 --- /dev/null +++ b/web/public/assets/images/user-email.svg @@ -0,0 +1,9 @@ + + + + + diff --git a/web/public/favicon.ico b/web/public/favicon.ico index 18e346d5ef..950555a3df 100644 Binary files a/web/public/favicon.ico and b/web/public/favicon.ico differ diff --git a/web/src/actions/walletActions.js b/web/src/actions/walletActions.js index f98bb89721..0ab984835b 100644 --- a/web/src/actions/walletActions.js +++ b/web/src/actions/walletActions.js @@ -30,6 +30,7 @@ export const ACTION_KEYS = { const ENDPOINTS = { TRADES: '/user/trades', + ORDERS: '/orders', DEPOSITS: '/user/deposits', WITHDRAWALS: '/user/withdrawals', DEPOSIT_BANK: '/user/deposit/bank', @@ -211,7 +212,7 @@ export const getOrdersHistory = ({ return (dispatch) => { dispatch({ type: ACTION_KEYS.ORDER_HISTORY_PENDING, payload: { page } }); axios - .get(`${ENDPOINTS.TRADES}?${query}`) + .get(`${ENDPOINTS.ORDERS}?${query}`) .then((body) => { dispatch({ type: ACTION_KEYS.ORDER_HISTORY_FULFILLED, @@ -239,6 +240,9 @@ export const downloadUserTrades = (key) => { format: 'csv', }); let path = ENDPOINTS.TRADES; + if (key === 'orders') { + path = ENDPOINTS.ORDERS; + } if (key === 'deposit') { path = ENDPOINTS.DEPOSITS; } else if (key === 'withdrawal') { diff --git a/web/src/components/AdminForm/hoc.js b/web/src/components/AdminForm/hoc.js index b66f3ef998..f8deb3ddcc 100644 --- a/web/src/components/AdminForm/hoc.js +++ b/web/src/components/AdminForm/hoc.js @@ -17,6 +17,7 @@ const Form = (name, className = '', allowPristine = false) => { small, buttonClass = '', submitOnKeyDown = false, + disableAllFields = false, }) => { return (
{ } }} > - {fields && renderFields(fields)} + {fields && renderFields(fields, disableAllFields)} {error && (
{error} @@ -40,6 +41,7 @@ const Form = (name, className = '', allowPristine = false) => { type={buttonType ? buttonType : 'primary'} onClick={handleSubmit(onSubmit)} disabled={ + disableAllFields || (allowPristine ? false : fields && pristine) || submitting || !valid || diff --git a/web/src/components/AdminForm/utils.js b/web/src/components/AdminForm/utils.js index 1322b01ed8..a580bf1a23 100644 --- a/web/src/components/AdminForm/utils.js +++ b/web/src/components/AdminForm/utils.js @@ -13,7 +13,7 @@ import { FileField } from './FileField'; import CaptchaField from './captchaField'; import Editor from './Editor'; -const renderFields = (fields) => { +const renderFields = (fields, disableAllFields) => { return ( {Object.keys(fields).map((key) => { @@ -23,6 +23,7 @@ const renderFields = (fields) => { validate: field.validate || [], name: key, key, + ...(disableAllFields ? { disabled: true } : {}), }; let component; diff --git a/web/src/components/AppBar/ThemeSwitcher.js b/web/src/components/AppBar/ThemeSwitcher.js index 15e907cc73..94982e5e09 100644 --- a/web/src/components/AppBar/ThemeSwitcher.js +++ b/web/src/components/AppBar/ThemeSwitcher.js @@ -36,7 +36,8 @@ const ThemeSwitcher = ({ selected, options = [], toggle, icons: ICONS }) => { } >
@@ -57,8 +58,7 @@ const ThemeSwitcher = ({ selected, options = [], toggle, icons: ICONS }) => { } > diff --git a/web/src/components/AppFooter/index.js b/web/src/components/AppFooter/index.js index afb6586b53..9e1c6de52d 100644 --- a/web/src/components/AppFooter/index.js +++ b/web/src/components/AppFooter/index.js @@ -1,11 +1,11 @@ import React from 'react'; import classnames from 'classnames'; import { isMobile } from 'react-device-detect'; -import STRINGS from '../../config/localizedStrings'; // import { PUBLIC_URL } from '../../config/constants'; import withConfig from 'components/ConfigProvider/withConfig'; import Image from 'components/Image'; import withEdit from 'components/EditProvider/withEdit'; +import STRINGS from 'config/localizedStrings'; const generateSectionsText = (links = {}, ICONS) => { let sectionsText = Object.keys(links) @@ -41,6 +41,7 @@ const AppFooter = ({ className, theme, constants = { description: '' }, + constants: { links = {} }, icons: ICONS, isEditMode, }) => { @@ -161,9 +162,49 @@ const AppFooter = ({ -
-
-
{STRINGS['FOOTER.FOOTER_COPYRIGHT']}
+
+
+ {!links.hide_referral_badge && ( + + {links.referral_label || 'Powered by bitHolla'} + + )} +
+
+ + | + +
+
); diff --git a/web/src/components/CheckTitle/index.js b/web/src/components/CheckTitle/index.js index b987a4c824..7d6e274fbc 100644 --- a/web/src/components/CheckTitle/index.js +++ b/web/src/components/CheckTitle/index.js @@ -5,7 +5,7 @@ import Image from 'components/Image'; import { EditWrapper } from 'components'; import { FLEX_CENTER_CLASSES } from '../../config/constants'; -import ICONS from 'config/icons'; +import { STATIC_ICONS as ICONS } from 'config/icons'; export const renderStatusIcon = (statusCode = -1, className = '') => { switch (statusCode) { diff --git a/web/src/components/ConfigProvider/index.js b/web/src/components/ConfigProvider/index.js index 209efb5aee..7caa9f6eb1 100644 --- a/web/src/components/ConfigProvider/index.js +++ b/web/src/components/ConfigProvider/index.js @@ -12,7 +12,7 @@ class ConfigProvider extends Component { constructor(props) { super(props); const { initialConfig } = this.props; - const { icons = {}, color = {}, defaults = {}, coin_keys } = { + const { icons = {}, color = {}, defaults = {}, coin_icons } = { ...initialConfig, }; @@ -24,7 +24,7 @@ class ConfigProvider extends Component { icons: generateAllIcons( calculatedThemes, addDefaultLogo(defaultLogo, icons), - coin_keys + coin_icons ), color: calculatedThemes, themeOptions, diff --git a/web/src/components/Form/TradeFormFields/Slider.js b/web/src/components/Form/TradeFormFields/Slider.js index 4e10bd5bba..29c0deeb25 100644 --- a/web/src/components/Form/TradeFormFields/Slider.js +++ b/web/src/components/Form/TradeFormFields/Slider.js @@ -5,17 +5,37 @@ import { Slider } from 'antd'; const marks = { 0: {}, - 25: {}, - 50: {}, - 75: {}, - 100: {}, + 25: { + label: '25%', + style: { + transform: 'translateX(-66%)', + }, + }, + 50: { + label: '50%', + style: { + transform: 'translateX(-66%)', + }, + }, + 75: { + label: '75%', + style: { + transform: 'translateX(-66%)', + }, + }, + 100: { + label: '100%', + style: { + transform: 'translateX(-66%)', + }, + }, }; const SizeSlider = (props) => { const { onClick } = props; return ( -
+
{Daily deposit max amount}: 1 -> {50,000,000} 2 -> {T} 3 -> {(Want to increase your daily limit?)} + FULL_MESSAGE_LIMIT: '{0}: {1} {2} {3}', // 0 -> {Daily deposit max amount}: 1 -> {50,000,000} 2 -> {T} 3 -> {(Want to increase your daily limit?)} }, LOGIN: { LOGIN_TO: 'Bei {0} anmelden', - CANT_LOGIN: "Sie können sich nicht anmelden?", - NO_ACCOUNT: "Sie haben noch kein Konto?", + CANT_LOGIN: 'Sie können sich nicht anmelden?', + NO_ACCOUNT: 'Sie haben noch kein Konto?', CREATE_ACCOUNT: 'Erstellen Sie hier ein Konto', - HELP: 'Hilfe' + HELP: 'Hilfe', }, FORM_FIELDS: { EMAIL_LABEL: 'E-Mail', @@ -199,7 +200,7 @@ export default { PASSWORD_LABEL: 'Passwort', PASSWORD_PLACEHOLDER: 'Geben Sie Ihr Passwort ein', PASSWORD_REPEAT_LABEL: 'Geben Sie Ihr Passwort erneut ein', - PASSWORD_REPEAT_PLACEHOLDER: 'Geben Sie Ihr Passwort erneut ein' + PASSWORD_REPEAT_PLACEHOLDER: 'Geben Sie Ihr Passwort erneut ein', }, VALIDATIONS: { OTP_LOGIN: 'OTP-Code zur Anmeldung bereitstellen', @@ -219,11 +220,12 @@ export default { MIN_VALUE: 'Wert muss {0} oder höher sein.', MAX_VALUE: 'Wert muss {0} oder niedriger sein.', INSUFFICIENT_BALANCE: 'Unzureichende Balance', - PASSWORDS_DONT_MATCH: "Passwort stimmt nicht überein", + PASSWORDS_DONT_MATCH: 'Passwort stimmt nicht überein', USER_EXIST: 'E-Mail wurde bereits registriert', - ACCEPT_TERMS: 'Sie haben den Nutzungsbedingungen und der Datenschutzrichtlinie nicht zugestimmt', + ACCEPT_TERMS: + 'Sie haben den Nutzungsbedingungen und der Datenschutzrichtlinie nicht zugestimmt', STEP: 'Ungültiger Wert, Schritt ist {0}', - ONLY_NUMBERS: 'Wert kann nur Zahlen enthalten' + ONLY_NUMBERS: 'Wert kann nur Zahlen enthalten', }, LEGAL: { PRIVACY_POLICY: { @@ -237,8 +239,8 @@ export default { 'Die Website und das Urheberrecht an allen Texten, Grafiken, Bildern, Software und allen anderen Materialien auf der Website sind Eigentum von bitHolla, einschließlich aller Marken und anderer geistiger Eigentumsrechte in Bezug auf die Materialien und den Service auf der Website. Die Materialien auf dieser Website dürfen nur für den persönlichen Gebrauch und für nicht-kommerzielle Zwecke verwendet werden.', 'Sie dürfen Auszüge der Website nur für den oben genannten Zweck auf einem Computerbildschirm anzeigen oder ausdrucken, vorausgesetzt, dass Sie alle Urheberrechts- und sonstigen Eigentumshinweise sowie alle bitHolla-Marken oder -Logos beibehalten, wie sie auf dem ersten Ausdruck oder Download angezeigt werden, ohne sie zu ändern, hinzuzufügen oder zu löschen. Sofern nicht ausdrücklich angegeben, dürfen Sie ohne die vorherige schriftliche Genehmigung von bitHolla keine Materialien der Website verändern, modifizieren, reproduzieren, verteilen oder in einem anderen kommerziellen Kontext verwenden.', 'Sie erkennen an, dass bitHolla und das bitHolla-Logo Warenzeichen von bitHolla Inc. sind. Sie dürfen diese Warenzeichen ohne Änderungen auf dem von dieser Website heruntergeladenen Material in dem oben genehmigten Umfang wiedergeben, aber Sie dürfen sie nicht anderweitig verwenden, kopieren, anpassen oder löschen.', - 'Sie dürfen unter keinen Umständen irgendwelche Rechte über oder in Bezug auf die Website erhalten (außer den Rechten zur Nutzung der Website gemäß diesen Bedingungen und anderen Bedingungen, die einen bestimmten Dienst oder Abschnitt der Website regeln) oder sich als Inhaber solcher Rechte über oder in Bezug auf die Website ausgeben.' - ] + 'Sie dürfen unter keinen Umständen irgendwelche Rechte über oder in Bezug auf die Website erhalten (außer den Rechten zur Nutzung der Website gemäß diesen Bedingungen und anderen Bedingungen, die einen bestimmten Dienst oder Abschnitt der Website regeln) oder sich als Inhaber solcher Rechte über oder in Bezug auf die Website ausgeben.', + ], }, GENERAL_TERMS: { TITLE: 'Allgemeine Geschäftsbedingungen', @@ -251,15 +253,15 @@ export default { 'Die Website und das Urheberrecht an allen Texten, Grafiken, Bildern, Software und allen anderen Materialien auf der Website sind Eigentum von bitHolla, einschließlich aller Marken und anderer geistiger Eigentumsrechte in Bezug auf die Materialien und den Service auf der Website. Die Materialien auf dieser Website dürfen nur für den persönlichen Gebrauch und für nicht-kommerzielle Zwecke verwendet werden.', 'Sie dürfen Auszüge der Website nur für den oben genannten Zweck auf einem Computerbildschirm anzeigen oder ausdrucken, vorausgesetzt, dass Sie alle Urheberrechts- und sonstigen Eigentumshinweise sowie alle bitHolla-Marken oder -Logos beibehalten, wie sie auf dem ersten Ausdruck oder Download angezeigt werden, ohne sie zu ändern, hinzuzufügen oder zu löschen. Sofern nicht ausdrücklich angegeben, dürfen Sie ohne die vorherige schriftliche Genehmigung von bitHolla keine Materialien der Website verändern, modifizieren, reproduzieren, verteilen oder in einem anderen kommerziellen Kontext verwenden.', 'Sie erkennen an, dass bitHolla und das bitHolla-Logo Warenzeichen von bitHolla Inc. sind. Sie dürfen diese Warenzeichen ohne Änderungen auf dem von dieser Website heruntergeladenen Material in dem oben genehmigten Umfang wiedergeben, aber Sie dürfen sie nicht anderweitig verwenden, kopieren, anpassen oder löschen.', - 'Sie dürfen unter keinen Umständen irgendwelche Rechte über oder in Bezug auf die Website erhalten (außer den Rechten zur Nutzung der Website gemäß diesen Bedingungen und anderen Bedingungen, die einen bestimmten Dienst oder Abschnitt der Website regeln) oder sich als Inhaber solcher Rechte über oder in Bezug auf die Website ausgeben.' - ] - } + 'Sie dürfen unter keinen Umständen irgendwelche Rechte über oder in Bezug auf die Website erhalten (außer den Rechten zur Nutzung der Website gemäß diesen Bedingungen und anderen Bedingungen, die einen bestimmten Dienst oder Abschnitt der Website regeln) oder sich als Inhaber solcher Rechte über oder in Bezug auf die Website ausgeben.', + ], + }, }, NOTIFICATIONS: { BUTTONS: { OKAY: 'Okay', START_TRADING: 'Handel beginnen', - SEE_HISTORY: 'siehe Verlauf' + SEE_HISTORY: 'siehe Verlauf', }, DEPOSITS: { TITLE_RECEIVED: '{0} Einzahlung erhalten', @@ -269,28 +271,29 @@ export default { INFORMATION_PENDING_1: 'Ihre {0} benötigen 1 Bestätigungen, bevor Sie mit dem Handel beginnen können.', INFORMATION_PENDING_2: - 'Dies kann 10-30 Minuten dauern. Wir senden eine E-Mail, sobald Ihre {0} in der Blockchain bestätigt wurde.' - } + 'Dies kann 10-30 Minuten dauern. Wir senden eine E-Mail, sobald Ihre {0} in der Blockchain bestätigt wurde.', + }, }, REFERRAL_SUCCESS: { TITLE: 'Anfrage gesendet', - BUTTON_TEXT: 'Okay' + BUTTON_TEXT: 'Okay', }, OTP_FORM: { - OTP_FORM_TITLE: 'Geben Sie Ihren Authentifizierungscode ein, um fortzufahren', + OTP_FORM_TITLE: + 'Geben Sie Ihren Authentifizierungscode ein, um fortzufahren', OTP_LABEL: 'OTP-Code', OTP_PLACEHOLDER: 'Geben Sie den Authentifizierungscode ein', OTP_TITLE: 'Authentifizierungscode', OTP_HELP: 'Hilfe', OTP_BUTTON: 'einreichen', - ERROR_INVALID: 'Ungültiger OTP-Code' + ERROR_INVALID: 'Ungültiger OTP-Code', }, QUICK_TRADE_COMPONENT: { TITLE: 'Quick', TOTAL_COST: 'Gesamtkosten', BUTTON: 'Überprüfung {0} Auftrag', INPUT: '{0} bis {1}', - TRADE_TITLE: '{0} {1}' // quick buy + TRADE_TITLE: '{0} {1}', // quick buy }, PREVIOUS_PAGE: 'vorherige Seite', NEXT_PAGE: 'nächste Seite', @@ -302,61 +305,62 @@ export default { ORDERS_PLURAL: 'orders', ORDERS_SINGULAR: 'order', HOLD_ORDERS: - 'Sie haben {0} offen {1}, was zu einem Halt von {2} {3} auf Ihrem {4} Saldo führt' + 'Sie haben {0} offen {1}, was zu einem Halt von {2} {3} auf Ihrem {4} Saldo führt', }, REQUEST_RESET_PASSWORD: { TITLE: 'Konto-Wiederherstellung', SUBTITLE: `Stellen Sie Ihr Konto unten wieder her`, SUPPORT: 'Kontaktieren Sie den Kundendienst.', - BUTTON: 'Wiederherstellungslink senden' + BUTTON: 'Wiederherstellungslink senden', }, REQUEST_RESET_PASSWORD_SUCCESS: { TITLE: 'Passwortrücksetzung gesendet', TEXT: - 'Wenn für die E-Mail-Adresse ein Konto existiert, wurde eine E-Mail mit Anweisungen zum Zurücksetzen an diese Adresse gesendet. Bitte prüfen Sie Ihre E-Mail und klicken Sie auf den Link, um das Zurücksetzen des Passworts abzuschließen.' + 'Wenn für die E-Mail-Adresse ein Konto existiert, wurde eine E-Mail mit Anweisungen zum Zurücksetzen an diese Adresse gesendet. Bitte prüfen Sie Ihre E-Mail und klicken Sie auf den Link, um das Zurücksetzen des Passworts abzuschließen.', }, RESET_PASSWORD: { TITLE: 'Neues Passwort festlegen', SUBTITLE: 'Neues Passwort festlegen', - BUTTON: 'Neues Passwort festlegen' + BUTTON: 'Neues Passwort festlegen', }, RESET_PASSWORD_SUCCESS: { TEXT_1: 'Sie haben erfolgreich ein neues Passwort eingerichtet.', - TEXT_2: 'Klicken Sie unten auf Login, um fortzufahren.' + TEXT_2: 'Klicken Sie unten auf Login, um fortzufahren.', }, SIGN_UP: { SIGNUP_TO: 'Registrieren Sie sich bei {0}', - NO_EMAIL: "Haben Sie die E-Mail nicht erhalten?", + NO_EMAIL: 'Haben Sie die E-Mail nicht erhalten?', REQUEST_EMAIL: 'Hier ein weitere E-Mail anfordern', HAVE_ACCOUNT: 'Sie haben bereits ein Konto?', GOTO_LOGIN: 'Gehen Sie zur Anmeldeseite', AFFILIATION_CODE: 'Empfehlungscode (optional)', - AFFILIATION_CODE_PLACEHOLDER:'Geben Sie Ihren Empfehlungscode ein', + AFFILIATION_CODE_PLACEHOLDER: 'Geben Sie Ihren Empfehlungscode ein', TERMS: { terms: 'Allgemeine Bedingungen', policy: 'Datenschutzrichtlinie', - text: 'Ich habe die {0} und {1} gelesen und stimme ihnen zu.' - } + text: 'Ich habe die {0} und {1} gelesen und stimme ihnen zu.', + }, }, VERIFICATION_TEXTS: { TITLE: 'E-Mail gesendet', - TEXT_1: 'Prüfen Sie Ihre E-Mail und klicken Sie auf den Link, um Ihre E-Mail zu verifizieren.', + TEXT_1: + 'Prüfen Sie Ihre E-Mail und klicken Sie auf den Link, um Ihre E-Mail zu verifizieren.', TEXT_2: - 'If you have not received any email verification and you have checked your junk mail then you can try clicking resend below.' + 'If you have not received any email verification and you have checked your junk mail then you can try clicking resend below.', }, VERIFICATION_EMAIL_REQUEST: { TITLE: 'E-Mail-Anfrage erneut senden', - BUTTON: 'E-Mail anfordern' + BUTTON: 'E-Mail anfordern', }, VERIFICATION_EMAIL_REQUEST_SUCCESS: { TITLE: 'E-Mail erneut senden', TEXT_1: - 'Wenn Sie nach einigen Minuten immer noch keine E-Mail-Bestätigung erhalten haben, kontaktieren Sie uns bitte unten.' + 'Wenn Sie nach einigen Minuten immer noch keine E-Mail-Bestätigung erhalten haben, kontaktieren Sie uns bitte unten.', }, VERIFICATION_EMAIL: { INVALID_UUID: 'Ungültiger Code', - TEXT_1: "Sie haben Ihre E-Mail erfolgreich verifiziert.", - TEXT_2: 'Sie können nun zur Anmeldung fortfahren.' + TEXT_1: 'Sie haben Ihre E-Mail erfolgreich verifiziert.', + TEXT_2: 'Sie können nun zur Anmeldung fortfahren.', }, USER_VERIFICATION: { INFO_TXT: @@ -449,14 +453,15 @@ export default { GENDER_PLACEHOLDER: 'Geben Sie an, welches Geschlecht Sie haben', GENDER_OPTIONS: { MAN: 'Männlich', - WOMAN: 'Weiblich' + WOMAN: 'Weiblich', }, NATIONALITY_LABEL: 'Nationalität', NATIONALITY_PLACEHOLDER: 'Geben Sie ein, welche Nationalität in Ihrem Ausweisdokument steht', DOB_LABEL: 'Geburtsdatum', COUNTRY_LABEL: 'Land Ihres Wohnsitzes', - COUNTRY_PLACEHOLDER: 'Wählen Sie das Land aus, in dem Sie sich derzeit befinden.', + COUNTRY_PLACEHOLDER: + 'Wählen Sie das Land aus, in dem Sie sich derzeit befinden.', CITY_LABEL: 'Stadt', CITY_PLACEHOLDER: 'Geben Sie die Stadt ein, in der Sie wohnen', ADDRESS_LABEL: 'Adresse', @@ -464,14 +469,15 @@ export default { POSTAL_CODE_LABEL: 'Postleitzahl', POSTAL_CODE_PLACEHOLDER: 'Geben Sie Ihre Postleitzahl ein', PHONE_CODE_LABEL: 'Land', - PHONE_CODE_PLACEHOLDER: 'Wählen Sie das Land, in dem Ihr Telefon angemeldet ist', + PHONE_CODE_PLACEHOLDER: + 'Wählen Sie das Land, in dem Ihr Telefon angemeldet ist', PHONE_CODE_DISPLAY: '({0}) {1}', // 0 -> (+21) 1 -> South Korea PHONE_NUMBER_LABEL: 'Telefonnummer', PHONE_NUMBER_PLACEHOLDER: 'Geben Sie Ihre Telefonnummer ein', CONNECTING_LOADING: 'Verbinden', SMS_SEND: 'SMS senden', SMS_CODE_LABEL: 'SMS-Code', - SMS_CODE_PLACEHOLDER: 'Geben Sie Ihren SMS-Code ein' + SMS_CODE_PLACEHOLDER: 'Geben Sie Ihren SMS-Code ein', }, INFORMATION: { TEXT: @@ -483,27 +489,29 @@ export default { PHONE_VERIFICATION_TXT_1: 'Erhalten Sie Echtzeit-Updates für Ein- und Auszahlungen, indem Sie Ihre Handynummer mitteilen.', PHONE_VERIFICATION_TXT_2: - 'Beweisen Sie außerdem Ihre Identität und Adresse, indem Sie Ihre LAN-Telefonnummer angeben (optional).' - } + 'Beweisen Sie außerdem Ihre Identität und Adresse, indem Sie Ihre LAN-Telefonnummer angeben (optional).', + }, }, ID_DOCUMENTS_FORM: { VALIDATIONS: { ID_TYPE: 'Bitte wählen Sie eine Art von Ausweisdokument', ID_NUMBER: 'Bitte geben Sie Ihre Dokumentennummer ein', - ISSUED_DATE: 'Bitte wählen Sie das Datum, an dem Ihr Dokument ausgestellt wurde', + ISSUED_DATE: + 'Bitte wählen Sie das Datum, an dem Ihr Dokument ausgestellt wurde', EXPIRATION_DATE: 'Bitte wählen Sie das Datum, an dem Ihr Dokument abläuft', FRONT: 'Bitte laden Sie eine Kopie Ihres Reisepasses hoch', PROOF_OF_RESIDENCY: 'Bitte laden Sie eine Kopie des Dokuments hoch, das Ihren aktuellen Wohnsitz belegt', - SELFIE_PHOTO_ID: 'Bitte laden Sie ein Selfie mit Reisepass und Notiz hoch' + SELFIE_PHOTO_ID: + 'Bitte laden Sie ein Selfie mit Reisepass und Notiz hoch', }, FORM_FIELDS: { TYPE_LABEL: 'Dokument-ID Typ', TYPE_PLACEHOLDER: 'Art des Ausweises wählen', TYPE_OPTIONS: { ID: 'ID', - PASSPORT: 'Reisepass' + PASSPORT: 'Reisepass', }, ID_NUMBER_LABEL: 'Reisepassnummer', ID_NUMBER_PLACEHOLDER: 'Geben Sie Ihre Reisepassnummer ein', @@ -519,10 +527,11 @@ export default { PASSPORT_LABEL: 'Reisepass-Dokument', PASSPORT_PLACEHOLDER: 'Fügen Sie eine Kopie Ihres Passdokuments hinzu', POR_LABEL: 'Dokument zum Nachweis Ihrer Adresse', - POR_PLACEHOLDER: 'Fügen Sie eine Kopie eines Dokuments hinzu, das Ihre Adresse belegt', + POR_PLACEHOLDER: + 'Fügen Sie eine Kopie eines Dokuments hinzu, das Ihre Adresse belegt', SELFIE_PHOTO_ID_LABEL: 'Ihr Selfie mit Reisepass und Notiz', SELFIE_PHOTO_ID_PLACEHOLDER: - 'Fügen Sie eine Kopie Ihres Selfies mit Reisepass und Notiz hinzu' + 'Fügen Sie eine Kopie Ihres Selfies mit Reisepass und Notiz hinzu', }, INFORMATION: { IDENTITY_DOCUMENT: 'Ausweisdokument', @@ -531,14 +540,16 @@ export default { TITLE: 'Bitte stellen Sie sicher, dass Ihre eingereichten Dokumente:', LIST_ITEM_1: 'HOHE QUALITÄT (Farbbilder, 300dpi Auflösung oder höher) sind.', - LIST_ITEM_2: 'VOLLSTÄNDIG SICHTBAR (Wasserzeichen sind erlaubt) sind.', - LIST_ITEM_3: 'GÜLTIG, mit deutlich sichtbarem Gültigkeitsdatum, sind.', + LIST_ITEM_2: + 'VOLLSTÄNDIG SICHTBAR (Wasserzeichen sind erlaubt) sind.', + LIST_ITEM_3: + 'GÜLTIG, mit deutlich sichtbarem Gültigkeitsdatum, sind.', WARNING_1: 'Es wird nur ein gültiger Reisepass akzeptiert; Fotos in hoher Qualität oder gescannte Bilder dieser Dokumente sind akzeptabel:', WARNING_2: 'Stellen Sie sicher, dass Sie Ihre eigenen Dokumente hochladen. Jede Verwendung von falschen oder gefälschten Dokumenten hat rechtliche Konsequenzen und führt zur sofortigen Sperrung Ihres Kontos.', WARNING_3: - 'Reichen Sie bitte nicht den Reisepass als Wohnsitznachweis ein.' + 'Reichen Sie bitte nicht den Reisepass als Wohnsitznachweis ein.', }, POR: { SECTION_1_TEXT_1: @@ -556,7 +567,7 @@ export default { SECTION_2_LIST_ITEM_3: 'Ein von der Regierung ausgestelltes Dokument (Steuererklärung, Wohnsitzbescheinigung usw.).', WARNING: - 'Wir können die Adresse auf Ihrem eingereichten Ausweisdokument nicht als gültigen Wohnsitznachweis akzeptieren.' + 'Wir können die Adresse auf Ihrem eingereichten Ausweisdokument nicht als gültigen Wohnsitznachweis akzeptieren.', }, SELFIE: { TITLE: 'Selfie mit Reisepass und Notiz', @@ -566,12 +577,12 @@ export default { INSTRUCTION_1: 'Ihr Gesicht muss deutlich sichtbar sein', INSTRUCTION_2: 'Reisepass muss deutlich lesbar sein', INSTRUCTION_3: 'Schreiben Sie den Namen des Exchange.', - INSTRUCTION_4: "Schreiben Sie das heutige Datum", + INSTRUCTION_4: 'Schreiben Sie das heutige Datum', INSTRUCTION_5: 'Unterschreiben Sie die Notiz.', WARNING: - 'Selfie mit einem anderen Reisepass mit hochgeladenem Inhalt wird abgelehnt' - } - } + 'Selfie mit einem anderen Reisepass mit hochgeladenem Inhalt wird abgelehnt', + }, + }, }, BANK_ACCOUNT_FORM: { VALIDATIONS: { @@ -581,7 +592,7 @@ export default { 'Ihre Bankkontonummer sollte weniger als 50 Ziffern haben', ACCOUNT_NUMBER_MAX_LENGTH: 'Ihre Bankkontonummer hat ein Limit von 50 Zeichen', - CARD_NUMBER: 'Ihre Kartennummer hat ein falsches Format' + CARD_NUMBER: 'Ihre Kartennummer hat ein falsches Format', }, FORM_FIELDS: { BANK_NAME_LABEL: 'Bank Name', @@ -592,20 +603,22 @@ export default { ACCOUNT_OWNER_PLACEHOLDER: 'Type the name as on your bank account', CARD_NUMBER_LABEL: 'Bankkartennummer', CARD_NUMBER_PLACEHOLDER: - 'Geben Sie die 16-stellige Nummer ein, die sich auf der Vorderseite Ihrer Bankkarte befindet' - } + 'Geben Sie die 16-stellige Nummer ein, die sich auf der Vorderseite Ihrer Bankkarte befindet', + }, }, WARNING: { - TEXT_1: 'Durch die Verifizierung Ihrer Identität können Sie Folgendes erhalten:', + TEXT_1: + 'Durch die Verifizierung Ihrer Identität können Sie Folgendes erhalten:', LIST_ITEM_1: 'Erhöhte Abhebungslimits', LIST_ITEM_2: 'Erhöhte Einzahlungslimits', - LIST_ITEM_3: 'Niedrigere Gebühren' - } + LIST_ITEM_3: 'Niedrigere Gebühren', + }, }, USER_SETTINGS: { TITLE_TEXT_1: 'Ändern Sie die Einstellungen Ihres Kontos. Von Schnittstelle, Benachrichtigungen, Benutzername und anderen Anpassungen.', - TITLE_TEXT_2: 'Wenn Sie die Einstellungen speichern, werden die Änderungen übernommen und gespeichert.', + TITLE_TEXT_2: + 'Wenn Sie die Einstellungen speichern, werden die Änderungen übernommen und gespeichert.', TITLE_NOTIFICATION: 'Benachrichtigung', TITLE_INTERFACE: 'Interface', TITLE_LANGUAGE: 'Sprache', @@ -627,30 +640,28 @@ export default { ORDER_PORTFOLIO_LABEL: 'Prozentualer Anteil des Portfolios:', NOTIFICATION_FORM: { TRADE_POPUPS: 'Handel Pop-ups', - POPUP_ORDER_CONFIRMATION: 'Vor dem Absenden von Bestellungen um Bestätigung bitten', - POPUP_ORDER_COMPLETED: 'Pop-up anzeigen, wenn die Bestellung abgeschlossen ist', + POPUP_ORDER_CONFIRMATION: + 'Vor dem Absenden von Bestellungen um Bestätigung bitten', + POPUP_ORDER_COMPLETED: + 'Pop-up anzeigen, wenn die Bestellung abgeschlossen ist', POPUP_ORDER_PARTIALLY_FILLED: - 'Pop-up anzeigen, wenn der Auftrag teilweise gefüllt ist' + 'Pop-up anzeigen, wenn der Auftrag teilweise gefüllt ist', }, - AUDIO_CUE_FORM: { // new + AUDIO_CUE_FORM: { + // new ALL_AUDIO: 'Alle Audio-Cues', PUBLIC_TRADE_AUDIO: 'Wenn ein öffentlicher Handel stattgefunden hat', - ORDERS_PARTIAL_AUDIO: - 'Wenn einer Ihrer Aufträge teilweise erfüllt ist', - ORDERS_PLACED_AUDIO: - 'Wenn eine Bestellung aufgegeben wird', - ORDERS_CANCELED_AUDIO: - 'Wenn ein Auftrag storniert wird', + ORDERS_PARTIAL_AUDIO: 'Wenn einer Ihrer Aufträge teilweise erfüllt ist', + ORDERS_PLACED_AUDIO: 'Wenn eine Bestellung aufgegeben wird', + ORDERS_CANCELED_AUDIO: 'Wenn ein Auftrag storniert wird', ORDERS_COMPLETED_AUDIO: 'Wenn einer Ihrer Aufträge vollständig gefüllt ist', CLICK_AMOUNTS_AUDIO: 'Wenn Sie Beträge und Preise im Auftragsbuch anklicken', - GET_QUICK_TRADE_AUDIO: - 'Wenn Sie ein Angebot für das Protrading einholen', + GET_QUICK_TRADE_AUDIO: 'Wenn Sie ein Angebot für das Protrading einholen', SUCCESS_QUICK_TRADE_AUDIO: 'Wenn ein erfolgreicher Quick-Trade stattfindet', - QUICK_TRADE_TIMEOUT_AUDIO: - 'Wenn Quick-Trade-Timeout ', + QUICK_TRADE_TIMEOUT_AUDIO: 'Wenn Quick-Trade-Timeout ', }, RISK_MANAGEMENT: { INFO_TEXT: @@ -660,8 +671,8 @@ export default { TOMAN_ASSET: 'Ungefährer Wert', ADJUST: '(PROZENTSATZ ANPASSEN)', ACTIVATE_RISK_MANAGMENT: 'Aktivieren Sie das Risikomanagement', - WARNING_POP_UP: 'Warn-Pop-ups' - } + WARNING_POP_UP: 'Warn-Pop-ups', + }, }, TRANSACTION_HISTORY: { TITLE: 'Verlauf', @@ -671,7 +682,7 @@ export default { TEXT_DOWNLOAD: 'DOWNLOAD-HISTORIE', TRADES: 'Trades', DEPOSITS: 'Einzahlungen', - WITHDRAWALS: 'Abhebungen' + WITHDRAWALS: 'Abhebungen', }, ACCOUNT_SECURITY: { OTP: { @@ -680,11 +691,11 @@ export default { OTP_DISABLED: 'BITTE SCHALTEN SIE DIE 2FA FUNKTION EIN', ENABLED_TEXTS: { TEXT_1: 'OTP bei der Anmeldung verlangen', - TEXT_2: 'OTP beim Abheben von Geldern verlangen' + TEXT_2: 'OTP beim Abheben von Geldern verlangen', }, DIALOG: { SUCCESS: 'Sie haben das OTP erfolgreich aktiviert', - REVOKE: 'Sie haben Ihr OTP erfolgreich widerrufen' + REVOKE: 'Sie haben Ihr OTP erfolgreich widerrufen', }, CONTENT: { TITLE: 'Aktivieren der Zwei-Faktor-Authentifizierung', @@ -700,55 +711,59 @@ export default { WARNING: 'Wir empfehlen Ihnen dringend, die 2-Faktor-Authentifizierung (2FA) einzurichten. Dadurch wird die Sicherheit Ihrer Gelder erheblich erhöht.', ENABLE: 'Aktivieren der Zwei-Faktoren-Authentifizierung', - DISABLE: 'Zwei-Faktor-Authentifizierung deaktivieren' + DISABLE: 'Zwei-Faktor-Authentifizierung deaktivieren', }, FORM: { - PLACEHOLDER: 'Geben Sie Ihr von Google Authenticator bereitgestelltes OTP ein.', - BUTTON: '2FA aktivieren' - } + PLACEHOLDER: + 'Geben Sie Ihr von Google Authenticator bereitgestelltes OTP ein.', + BUTTON: '2FA aktivieren', + }, }, CHANGE_PASSWORD: { TITLE: 'Passwort ändern', ACTIVE: 'AKTIV', DIALOG: { - SUCCESS: 'Sie haben Ihr Passwort erfolgreich geändert' + SUCCESS: 'Sie haben Ihr Passwort erfolgreich geändert', }, FORM: { BUTTON: 'Passwort ändern', CURRENT_PASSWORD: { label: 'Aktuelles Passwort', - placeholder: 'Geben Sie Ihr aktuelles Passwort ein' + placeholder: 'Geben Sie Ihr aktuelles Passwort ein', }, NEW_PASSWORD: { label: 'Neues Passwort', - placeholder: 'Geben Sie ein neues Passwort ein' + placeholder: 'Geben Sie ein neues Passwort ein', }, NEW_PASSWORD_REPEAT: { label: 'Neues Passwort bestätigen', - placeholder: 'Geben Sie Ihr neues Passwort erneut ein' - } - } - } + placeholder: 'Geben Sie Ihr neues Passwort erneut ein', + }, + }, + }, }, CURRENCY: 'Währung', TYPE: 'Typ', TYPES_VALUES: { market: 'Markt', - limit: 'Limit' + limit: 'Limit', }, TYPES: [ { value: 'market', label: 'Markt' }, // DO NOT CHANGE value, ONLY TRANSLATE label - { value: 'limit', label: 'Limit' } // DO NOT CHANGE value, ONLY TRANSLATE label + { value: 'limit', label: 'Limit' }, // DO NOT CHANGE value, ONLY TRANSLATE label ], SIDE: 'Seite', SIDES_VALUES: { buy: 'kaufen', - sell: 'verkaufen' + sell: 'verkaufen', }, - SIDES: [{ value: 'buy', label: 'kaufen' }, { value: 'sell', label: 'verkaufen' }], // DO NOT CHANGE value, ONLY TRANSLATE label + SIDES: [ + { value: 'buy', label: 'kaufen' }, + { value: 'sell', label: 'verkaufen' }, + ], // DO NOT CHANGE value, ONLY TRANSLATE label DEFAULT_TOGGLE_OPTIONS: [ { value: true, label: 'an' }, // DO NOT CHANGE value, ONLY TRANSLATE label - { value: false, label: 'aus' } // DO NOT CHANGE value, ONLY TRANSLATE label + { value: false, label: 'aus' }, // DO NOT CHANGE value, ONLY TRANSLATE label ], SIZE: 'Größe', PRICE: 'Preis', @@ -794,7 +809,7 @@ export default { h: 'Hoch', l: 'Tief', c: 'Schließen', - v: 'Volumen' + v: 'Volumen', }, QUICK_TRADE: 'Quick trade', PRO_TRADE: 'Pro trade', @@ -825,8 +840,7 @@ export default { WITHDRAWALS_FORM_AMOUNT_LABEL: '{0} abzuhebender Betrag', WITHDRAWALS_FORM_AMOUNT_PLACEHOLDER: 'Geben Sie den Betrag von {0} ein, den Sie abheben möchten', - WITHDRAWALS_FORM_FEE_COMMON_LABEL: - 'Transaktionsgebühr', + WITHDRAWALS_FORM_FEE_COMMON_LABEL: 'Transaktionsgebühr', WITHDRAWALS_FORM_FEE_FIAT_LABEL: 'Bankabhebungsgebühr', WITHDRAWALS_FORM_FEE_PLACEHOLDER: 'Geben Sie den Betrag von {0} ein, den Sie als Gebühr für die Transaktion verwenden möchten', @@ -852,15 +866,18 @@ export default { DEPOSIT_VERIFICATION_WARNING_INFORMATION: 'Dies ist die ID des Vorgangs: "{0}", bitte teilen Sie uns diese ID mit, damit wir Ihnen helfen können.', DEPOSIT_VERIFICATION_SUCCESS: 'Zahlung verifiziert', - DEPOSIT_VERIFICATION_ERROR: 'Es ist ein Fehler bei der Verifizierung der Einzahlung aufgetreten.', - DEPOSIT_VERIFICATION_ERROR_VERIFIED: 'Die Einzahlung wurde bereits verifiziert', + DEPOSIT_VERIFICATION_ERROR: + 'Es ist ein Fehler bei der Verifizierung der Einzahlung aufgetreten.', + DEPOSIT_VERIFICATION_ERROR_VERIFIED: + 'Die Einzahlung wurde bereits verifiziert', DEPOSIT_VERIFICATION_ERROR_STATUS: 'Ungültiger Status', DEPOSIT_VERIFICATION_ERROR_USER_MATCH: 'Die Karte, mit der Sie die Einzahlung vorgenommen haben, ist nicht dieselbe wie Ihre registrierte Karte. Daher wird Ihre Einzahlung abgelehnt und Ihr Geld wird in weniger als einer Stunde zurückerstattet.', QUOTE_MESSAGE: 'Sie gehen zu {0} {1} {2} für {3} {4}', QUOTE_BUTTON: 'Akzeptieren', QUOTE_REVIEW: 'Überprüfen', - QUOTE_COUNTDOWN_MESSAGE: 'Sie haben {0} Sekunden, um den Handel durchzuführen', + QUOTE_COUNTDOWN_MESSAGE: + 'Sie haben {0} Sekunden, um den Handel durchzuführen', QUOTE_EXPIRED_TOKEN: 'Der Quote-Token ist abgelaufen.', QUOTE_SUCCESS_REVIEW_TITLE: 'Quick Trade', QUOTE_SUCCESS_REVIEW_MESSAGE: 'Sie haben erfolgreich {0} {1} {2} für {3} {4}', // you have successfully buy 1 btc from x toman @@ -879,14 +896,17 @@ export default { BASE_MESSAGE_3: 'Täglicher Abhebungs-Maximalbetragt', BASE_INCREASE_LIMIT: 'Erhöhen Sie Ihr Tageslimit', CONFIRM_VIA_EMAIL: 'Bestätigen Sie per E-Mail', - CONFIRM_VIA_EMAIL_1: 'Wir haben Ihnen eine Bestätigungs-E-Mail zur Abhebung geschickt.', + CONFIRM_VIA_EMAIL_1: + 'Wir haben Ihnen eine Bestätigungs-E-Mail zur Abhebung geschickt.', CONFIRM_VIA_EMAIL_2: 'Um den Abhebungsvorgang abzuschließen, bestätigen Sie bitte', - CONFIRM_VIA_EMAIL_3: 'die Abhebung über Ihre E-Mail innerhalb von 5 Minuten.', - WITHDRAW_CONFIRM_SUCCESS_1: 'Ihre Abhebungsanfrage ist bestätigt. Sie wird in Kürze bearbeitet.', + CONFIRM_VIA_EMAIL_3: + 'die Abhebung über Ihre E-Mail innerhalb von 5 Minuten.', + WITHDRAW_CONFIRM_SUCCESS_1: + 'Ihre Abhebungsanfrage ist bestätigt. Sie wird in Kürze bearbeitet.', WITHDRAW_CONFIRM_SUCCESS_2: 'Um Ihren Abhebungsstatus zu sehen, besuchen Sie bitte Ihre Abhebungs-Historienseite', - GO_WITHDRAWAL_HISTORY: 'Zur Abhebungsverlaufsseite gehen' + GO_WITHDRAWAL_HISTORY: 'Zur Abhebungsverlaufsseite gehen', }, WALLET_BUTTON_BASE_DEPOSIT: 'einzahlen', WALLET_BUTTON_BASE_WITHDRAW: 'abheben', @@ -925,8 +945,7 @@ export default { LOGOUT_ERROR_TOKEN_EXPIRED: 'Token ist abgelaufen', LOGOUT_ERROR_LOGIN_AGAIN: 'Erneut anmelden', // ip doesnt match LOGOUT_ERROR_INVALID_TOKEN: 'Ungültiges Token', - LOGOUT_ERROR_INACTIVE: - 'Sie wurden abgemeldet, weil Sie inaktiv waren', + LOGOUT_ERROR_INACTIVE: 'Sie wurden abgemeldet, weil Sie inaktiv waren', ORDER_ENTRY_BUTTON: '{0} {1}', // 0 -> buy/sell 1 -> btc/.. QUICK_TRADE_OUT_OF_LIMITS: 'Auftragsgröße ist außerhalb der Grenzen', QUICK_TRADE_TOKEN_USED: 'Token wurde verwendet', @@ -971,12 +990,12 @@ export default { SETTINGS_ORDERPOPUP_LABEL: 'Pop-up zur Auftragsbestätigung anzeigen', SETTINGS_ORDERPOPUP_OPTIONS: [ { value: false, label: 'NEIN' }, // DO NOT CHANGE value, ONLY TRANSLATE label - { value: true, label: 'Ja' } // DO NOT CHANGE value, ONLY TRANSLATE label + { value: true, label: 'Ja' }, // DO NOT CHANGE value, ONLY TRANSLATE label ], SETTINGS_THEME_LABEL: 'User Interface Theme', // TODO set right labels // ToDo SETTINGS_THEME_OPTIONS: [ { value: 'white', label: 'Weiß' }, // DO NOT CHANGE value, ONLY TRANSLATE label - { value: 'dark', label: 'Dunkel' } // DO NOT CHANGE value, ONLY TRANSLATE label + { value: 'dark', label: 'Dunkel' }, // DO NOT CHANGE value, ONLY TRANSLATE label ], SETTING_BUTTON: 'speichern', STRING_WITH_PARENTHESIS: '{0} ({1})', @@ -996,7 +1015,7 @@ export default { ACTIVE: 'Aktiv', INACTIVE: 'Inaktiv', INVALID_LEVEL: - 'Sie müssen Ihre Verifizierungsstufe erhöhen, um Zugriff auf diese Funktion zu haben' // TODO + 'Sie müssen Ihre Verifizierungsstufe erhöhen, um Zugriff auf diese Funktion zu haben', // TODO }, DEVELOPERS_TOKENS_POPUP: { GENERATE_TITLE: 'API-Schlüssel generieren', @@ -1014,7 +1033,7 @@ export default { CREATED_TITLE: 'Kopieren Sie Ihren API-Schlüssel', CREATED_TEXT_1: 'Bitte kopieren Sie Ihren API-Schlüssel, da dieser in Zukunft nicht mehr erreichbar sein wird.', - CREATED_TEXT_2: 'Halten Sie Ihren Schlüssel privat.' + CREATED_TEXT_2: 'Halten Sie Ihren Schlüssel privat.', }, DEVELOPERS_TOKENS_TABLE: { NAME: 'Name', @@ -1023,7 +1042,7 @@ export default { CREATED: 'Erstelltes Datum', REVOKE: 'Widerrufen', REVOKED: 'Widerrufen', - REVOKE_TOOLTIP: 'Sie müssen 2FA aktivieren, um das Token zu widerrufen' // TODO + REVOKE_TOOLTIP: 'Sie müssen 2FA aktivieren, um das Token zu widerrufen', // TODO }, CHAT: { CHAT_TEXT: 'Chat', @@ -1035,11 +1054,12 @@ export default { CHAT_MESSAGE_BOX_PLACEHOLDER: 'Mitteilungen', SIGN_UP_CHAT: 'Zum Chatten anmelden', JOIN_CHAT: 'Benutzername für den Chat festlegen', - TROLLBOX: 'Trollbox ({0})' // new + TROLLBOX: 'Trollbox ({0})', // new }, INVALID_USERNAME: 'Der Benutzername muss zwischen 3 und 15 Zeichen lang sein. Enthält nur Kleinbuchstaben, Zahlen und Unterstrich', - USERNAME_TAKEN: 'Dieser Benutzername ist bereits vergeben. Bitte versuchen Sie einen anderen.', + USERNAME_TAKEN: + 'Dieser Benutzername ist bereits vergeben. Bitte versuchen Sie einen anderen.', USERNAME_LABEL: 'Benutzername (wird für den Chat verwendet)', USERNAME_PLACEHOLDER: 'Benutzername', TAB_USERNAME: 'Benutzername', @@ -1063,7 +1083,7 @@ export default { LABEL_PAIR_MAKER_FEE: '{0} Maker Gebühr', LABEL_PAIR_TAKER_FEE: '{0} Taker Gebühr', UNLIMITED: 'Unbegrenzt', - BLOCKED: '0' + BLOCKED: '0', }, WALLET_ADDRESS_TITLE: 'Geldbörse {0} generieren', WALLET_ADDRESS_GENERATE: 'Erzeugen', @@ -1119,7 +1139,8 @@ export default { VIEW_FEE_STRUCTURE: 'Gebührenstruktur und Limits anzeigen', UPGRADE_ACCOUNT: 'Konto aktualisieren', ACTIVE_2FA_SECURITY: 'Aktive 2FA-Sicherheit', - ACCOUNT_ASSETS_TXT_1: 'Angezeigt wird eine Übersicht über alle Ihre Assets.', + ACCOUNT_ASSETS_TXT_1: + 'Angezeigt wird eine Übersicht über alle Ihre Assets.', ACCOUNT_ASSETS_TXT_2: 'Das Halten einer großen Menge an Vermögenswerten berechtigt Sie zu einem Konto-Upgrade, das ein einzigartiges Abzeichen und niedrigere Handelsgebühren beinhaltet.', TRADING_VOLUME_TXT_1: @@ -1194,15 +1215,21 @@ export default { TRADE_OVER_BTC: 'Handel über {0} BTC Wert', // new XHT_IN_WALLET: '{0} XHT in der Geldbörse', // new REWARDS_BONUS: 'Belohnungen und Prämien', // new - COMPLETE_TASK_DESC: 'Erfüllen Sie Aufgaben und verdienen Sie Prämien im Wert von über 10.000 $.', // new + COMPLETE_TASK_DESC: + 'Erfüllen Sie Aufgaben und verdienen Sie Prämien im Wert von über 10.000 $.', // new TASKS: 'Aufgaben', // new - MAKE_FIRST_DEPOSIT: 'Machen Sie Ihre erste Einzahlung und erhalten Sie 1 XHT', // new - BUY_FIRST_XHT: 'Kaufen Sie Ihr ersten XHT und erhalten Sie einen Bonus von 5 XHT', // new + MAKE_FIRST_DEPOSIT: + 'Machen Sie Ihre erste Einzahlung und erhalten Sie 1 XHT', // new + BUY_FIRST_XHT: + 'Kaufen Sie Ihr ersten XHT und erhalten Sie einen Bonus von 5 XHT', // new COMPLETE_ACC_VERIFICATION: 'Schließen Sie die Kontoverifizierung ab und erhalten Sie einen 20 XHT-Bonus', // new - INVITE_USER: 'Laden Sie Benutzer ein und genießen Sie Provisionen aus deren Handel', // new - JOIN_HAP: 'Werden Sie Mitglied bei HAP und verdienen Sie 10% für jedes verkaufte HollaEx Kit', // new - EARN_RUNNING_EXCHANGE: 'Verdienen Sie passives Einkommen durch den Betrieb Ihrer eigenen Exchange.', // new + INVITE_USER: + 'Laden Sie Benutzer ein und genießen Sie Provisionen aus deren Handel', // new + JOIN_HAP: + 'Werden Sie Mitglied bei HAP und verdienen Sie 10% für jedes verkaufte HollaEx Kit', // new + EARN_RUNNING_EXCHANGE: + 'Verdienen Sie passives Einkommen durch den Betrieb Ihrer eigenen Exchange.', // new XHT_WAVE_AUCTION: 'XHT-Wellen-Auktionsdaten', // new XHT_WAVE_DESC_1: 'Die Verteilung des HollaEx-Tokens (XHT) erfolgt über eine Wave-Auktion.', // new @@ -1214,17 +1241,19 @@ export default { LEARN_MORE_WAVE_AUCTION: 'Erfahren Sie mehr über die Wave Auktion', // new WAVE_NUMBER: 'Wellennummer', // new DISCOUNT: '( {0}% Rabatt )', // new - MY_FEES_LIMITS: ' Meine Gebühren und Limits' // new + MY_FEES_LIMITS: ' Meine Gebühren und Limits', // new }, REFERRAL_LINK: { TITLE: 'Freunde einladen', // new INFO_TEXT: 'Empfehlen Sie Ihre Freunde, indem Sie diesen Link weitergeben und erhalten Sie Vorteile, wenn Sie andere Personen einladen.', - COPY_FIELD_LABEL: 'Teilen Sie den untenstehenden Link mit Freunden und verdienen Sie Provisionen:', // new + COPY_FIELD_LABEL: + 'Teilen Sie den untenstehenden Link mit Freunden und verdienen Sie Provisionen:', // new REFERRED_USER_COUT: 'Sie haben {0} Benutzer empfohlen.', // new COPY_LINK_BUTTON: 'EMPFEHLUNGSLINK KOPIEREN', // new XHT_TITLE: 'MEINE REFERRALS', // new - XHT_INFO_TEXT: 'Verdienen Sie Provisionen, indem Sie Ihre Freunde einladen.', // new + XHT_INFO_TEXT: + 'Verdienen Sie Provisionen, indem Sie Ihre Freunde einladen.', // new XHT_INFO_TEXT_1: 'Provisionen werden periodisch an Ihre Geldbörse gezahlt', // new APPLICATION_TXT: 'Um ein HollaEx-Kit-Vertriebspartner zu werden, füllen Sie bitte einen Antrag aus.', // new @@ -1232,7 +1261,7 @@ export default { PENDING_REFERRAL: 'Ausstehende Kommissionen:', // new EARN_REFERRAL: 'Verdiente Provisionen:', // new XHT_COUNT: '{0} XHT', // new - APPLY_BUTTON: 'ANWENDEN' // new + APPLY_BUTTON: 'ANWENDEN', // new }, STAKE_TOKEN: { TITLE: 'Staken von HollaEx Token', // new @@ -1242,7 +1271,7 @@ export default { 'Sie können Ihre HollaEx-Token auf ähnliche Weise sichern und XHT verdienen, die während der Wave-Auktion nicht verkauft werden.', // new INFO_TXT3: 'Gehen Sie einfach auf dash.bitholla.com und sichern Sie sich noch heute Ihren eigenen Exchange und verdienen Sie kostenlos XHT', // new - BUTTON_TXT: 'FINDEN SIE MEHR' // new + BUTTON_TXT: 'FINDEN SIE MEHR', // new }, TERMS_OF_SERVICES: { // new @@ -1257,14 +1286,16 @@ export default { 'Zahlen Sie Geld in Ihre Geldbörse ein, um HollaEx Token (XHT) zu erhalten', READ_FAG: 'Lesen Sie HollaEx FAQ hier: {0}', READ_DOCUMENTATION: 'Lesen Sie das HollaEx Whitepaper hier: {0}', - READ_WAVES: 'Regeln für die kommende öffentliche Wellenauktion im Dezember{0}', // new + READ_WAVES: + 'Regeln für die kommende öffentliche Wellenauktion im Dezember{0}', // new DOWNLOAD_BUY_XHT: 'Laden Sie die PDF-Datei herunter, um eine visuelle Schritt-für-Schritt-Anleitung zu sehen {0}', HOW_TO_BUY: 'wie man HollaEx Token (XHT) kauft', PUBLIC_SALES: 'Öffentliche Wellen-Auktion', // new CONTACT_US: 'Für weitere Informationen und bei Problemen können Sie uns gerne eine E-Mail an {0} senden.', - VISUAL_STEP: 'Sehen Sie eine visuelle Schritt-für-Schritt-Anleitung auf {0}', // new + VISUAL_STEP: + 'Sehen Sie eine visuelle Schritt-für-Schritt-Anleitung auf {0}', // new WARNING_TXT: 'Wir werden Ihre Anfrage prüfen und Ihnen weitere Anweisungen für den Zugang zur XIV Exchange per E-Mail zusenden.', // new WARNING_TXT1: @@ -1275,7 +1306,7 @@ export default { XHT_TITLE: 'XHT', //new XHT_TRADE_TXT_1: 'Anmelden, um Ihre letzten Trades zu sehen', //new XHT_TRADE_TXT_2: 'Sie können {0}, um Ihren letzten Handelsverlauf zu sehen', //new - LOGIN_HERE: 'hier anmelden' + LOGIN_HERE: 'hier anmelden', }, WAVES: { // new @@ -1283,7 +1314,7 @@ export default { NEXT_WAVE: 'Nächste Welle', WAVE_AMOUNT: 'Menge in Welle', FLOOR: 'Etage', - LAST_WAVE: 'Letzte Wellee' + LAST_WAVE: 'Letzte Wellee', }, TYPES_OF_POSTS: { // new @@ -1291,12 +1322,13 @@ export default { ANNOUNCEMEN: 'Ankündigung', SYSTEM_UPDATE: 'System-Update', LAST_WAVE: 'Letzte Welle', - ANNOUNCEMENT_TXT: 'Kostenloses XHT wird an alle Geldbörsen verteilt, die sich für diese Funktion qualifizieren.', + ANNOUNCEMENT_TXT: + 'Kostenloses XHT wird an alle Geldbörsen verteilt, die sich für diese Funktion qualifizieren.', SYSTEM_UPDATE_TIME: 'Zeit: 12:31 PM, 19. Dezember 2019 ', SYSTEM_UPDATE_DURATION: '1 Stunde', LAST_WAVE_AMOUNT: '100, 213 XHT', LAST_WAVE_REDISTRIBUTED: ' 11, 211', - LAST_WAVE_TIME: ' 12: 31 PM, 19. Dezember 2019' + LAST_WAVE_TIME: ' 12: 31 PM, 19. Dezember 2019', }, USER_LEVEL: 'User Level', // new LIMIT_AMOUNT: 'Grenzwertbetrag', // new @@ -1326,10 +1358,11 @@ export default { 'Verdienen Sie XHT mit dem HAP-Programm, indem Sie Ihre Freunde in den Exchange einladen. {0}.', DEFAULT_ANNOUNCEMENT: 'In diesem Bereich werden Ihre öffentlichen Ankündigungen für den Exchange angezeigt!', - ANNOUNCEMENT_TXT_2: 'Kostenloses XHT wird an alle Geldbörsen verteilt, die {0}.', + ANNOUNCEMENT_TXT_2: + 'Kostenloses XHT wird an alle Geldbörsen verteilt, die {0}.', LEARN_MORE: 'Mehr erfahren', - APPLY_TODAY: 'Heute bewerben' // new + APPLY_TODAY: 'Heute bewerben', // new }, OPEN_WALLET: 'Offener Geldbeutel', // new - AGO: 'ago' // new + AGO: 'ago', // new }; diff --git a/web/src/config/lang/en.js b/web/src/config/lang/en.js index 4890f96029..c9e45ba9e7 100644 --- a/web/src/config/lang/en.js +++ b/web/src/config/lang/en.js @@ -81,7 +81,8 @@ const nestedContent = { FOOTER: { FOOTER_LEGAL: ['Proudly made in Seoul, South Korea', 'bitHolla Inc.'], FOOTER_LANGUAGE_TEXT: 'LANGUAGE', - FOOTER_COPYRIGHT: 'Powered by bitHolla', + TERMS_OF_SERVICE: 'Terms of Service', + PRIVACY_POLICY: 'Privacy Policy', SECTIONS: { SECTION_1_TITLE: 'ABOUT', SECTION_1_LINK_1: 'About Us', @@ -438,6 +439,11 @@ const nestedContent = { DOCUMENT_VERIFIED_NOTE: 'Your documents are completed.', NOTE_FROM_VERIFICATION_DEPARTMENT: 'Note from the verification department', CODE_EXPIRES_IN: 'Code expires in', + EMAIL_VERIFICATION: 'Send verification email', + VERIFICATION_SENT: 'Verification Sent', + VERIFICATION_SENT_INFO: + 'Check your email and click the link to verify email.', + OKAY: 'Okay', USER_DOCUMENTATION_FORM: { FORM_FIELDS: { FIRST_NAME_LABEL: 'First name', @@ -813,7 +819,7 @@ const nestedContent = { ORDER_HISTORY: 'Order history', PUBLIC_SALES: 'public sales', // ToDo REMAINING: 'Remaining', - FULLFILLED: '{0} % Fullfilled', + FULLFILLED: '{0} % Filled', FILLED: 'Filled', // new LOWEST_PRICE: 'Lowest Price ({0})', // new PHASE: 'Phase', // new @@ -968,6 +974,7 @@ const nestedContent = { LOGOUT_ERROR_INACTIVE: 'You have been logged out because you have been inactive', ORDER_ENTRY_BUTTON: '{0} {1}', // 0 -> buy/sell 1 -> btc/.. + ORDER_ENTRY_ADVANCED: 'Advanced', QUICK_TRADE_OUT_OF_LIMITS: 'Order size is out of the limits', QUICK_TRADE_TOKEN_USED: 'Token has been used', QUICK_TRADE_QUOTE_EXPIRED: 'Quote has expired', diff --git a/web/src/config/lang/es.js b/web/src/config/lang/es.js index c5caf45f59..a29347a7e3 100644 --- a/web/src/config/lang/es.js +++ b/web/src/config/lang/es.js @@ -78,7 +78,6 @@ export default { FOOTER: { FOOTER_LEGAL: ['Proudly made in Seoul, South Korea', 'bitHolla Inc.'], FOOTER_LANGUAGE_TEXT: 'LANGUAGE', - FOOTER_COPYRIGHT: 'Powered by bitHolla', SECTIONS: { SECTION_1_TITLE: 'ABOUT', SECTION_1_LINK_1: 'About Us', @@ -680,7 +679,8 @@ export default { WITHDRAWALS: 'Retiros', }, ACCOUNT_SECURITY: { - TITLE_TEXT:'Adjust the security settings for your account. From Two-factor authentication, password, API keys and other security related functions.', + TITLE_TEXT: + 'Adjust the security settings for your account. From Two-factor authentication, password, API keys and other security related functions.', OTP: { TITLE: 'Autenticación de dos factores', OTP_ENABLED: 'habilitar otp', @@ -710,7 +710,8 @@ export default { DISABLE: 'Desactivar la autenticación de dos factores', SECRET_1: 'Enter yor secret key', SECRET_2: 'Please enter your secret key to confirm you wrote it down.', - SECRET_3: 'This secret key will help you recover your account if you lost access to your phone.', + SECRET_3: + 'This secret key will help you recover your account if you lost access to your phone.', INPUT_1: 'Secret Key', TITLE_2: 'Enter One-Time Password (OTP)', @@ -796,7 +797,7 @@ export default { ], // DO NOT CHANGE value, ONLY TRANSLATE label DEFAULT_TOGGLE_OPTIONS: [ { value: true, label: 'Sí' }, // DO NOT CHANGE value, ONLY TRANSLATE label - { value: false, label: 'No' } // DO NOT CHANGE value, ONLY TRANSLATE label + { value: false, label: 'No' }, // DO NOT CHANGE value, ONLY TRANSLATE label ], SIZE: 'Cantidad', PRICE: 'Precio', @@ -872,8 +873,7 @@ export default { WITHDRAWALS_FORM_AMOUNT_LABEL: 'Cantidad de {0} para retirar', WITHDRAWALS_FORM_AMOUNT_PLACEHOLDER: 'Type the amount of {0} you wish to withdraw', - WITHDRAWALS_FORM_FEE_COMMON_LABEL: - 'Tarifa de Transacción', + WITHDRAWALS_FORM_FEE_COMMON_LABEL: 'Tarifa de Transacción', WITHDRAWALS_FORM_FEE_FIAT_LABEL: 'Bank withdrawal fee', WITHDRAWALS_FORM_FEE_PLACEHOLDER: 'Type the amount of {0} you wish to use in the fee of the transaction', @@ -1023,7 +1023,7 @@ export default { SETTINGS_ORDERPOPUP_LABEL: 'Display order confirmation popup', SETTINGS_ORDERPOPUP_OPTIONS: [ { value: false, label: 'NO' }, // DO NOT CHANGE value, ONLY TRANSLATE label - { value: true, label: 'Si' } // DO NOT CHANGE value, ONLY TRANSLATE label + { value: true, label: 'Si' }, // DO NOT CHANGE value, ONLY TRANSLATE label ], SETTINGS_THEME_LABEL: 'Tema de Interface de Usuario', // TODO set right labels // ToDo SETTINGS_THEME_OPTIONS: [ diff --git a/web/src/config/lang/fa.js b/web/src/config/lang/fa.js index de0053a91e..f7de0d48a2 100644 --- a/web/src/config/lang/fa.js +++ b/web/src/config/lang/fa.js @@ -81,7 +81,6 @@ const nestedContent = { FOOTER: { FOOTER_LEGAL: ['با افتخار توسعه داده شده در سئول ، کره جنوبی'], FOOTER_LANGUAGE_TEXT: 'زبان', - FOOTER_COPYRIGHT: 'Powered by bitHolla', SECTIONS: { SECTION_1_TITLE: 'درباره', SECTION_1_LINK_1: 'درباره ما', @@ -652,7 +651,8 @@ const nestedContent = { WITHDRAWALS: 'برداشت ها', }, ACCOUNT_SECURITY: { - TITLE_TEXT:'Adjust the security settings for your account. From Two-factor authentication, password, API keys and other security related functions.', + TITLE_TEXT: + 'Adjust the security settings for your account. From Two-factor authentication, password, API keys and other security related functions.', OTP: { TITLE: 'رمز یکبار مصرف', OTP_ENABLED: 'رمز یکبار مصرف را فعال کنید', @@ -682,7 +682,8 @@ const nestedContent = { DISABLE: 'غیر فعال کردن رمز یکبار مصرف', SECRET_1: 'Enter yor secret key', SECRET_2: 'Please enter your secret key to confirm you wrote it down.', - SECRET_3: 'This secret key will help you recover your account if you lost access to your phone.', + SECRET_3: + 'This secret key will help you recover your account if you lost access to your phone.', INPUT_1: 'Secret Key', TITLE_2: 'Enter One-Time Password (OTP)', diff --git a/web/src/config/lang/fr.js b/web/src/config/lang/fr.js index 8d333fc0d0..08411bce21 100644 --- a/web/src/config/lang/fr.js +++ b/web/src/config/lang/fr.js @@ -78,7 +78,6 @@ export default { FOOTER: { FOOTER_LEGAL: ['Proudly made in Seoul, South Korea', 'bitHolla Inc.'], FOOTER_LANGUAGE_TEXT: 'LANGUES', - FOOTER_COPYRIGHT: 'Powered by bitHolla', SECTIONS: { SECTION_1_TITLE: ' À PROPOS', SECTION_1_LINK_1: 'NOTRE HISTOIRE', @@ -688,7 +687,8 @@ export default { WITHDRAWALS: 'Retraits', }, ACCOUNT_SECURITY: { - TITLE_TEXT:'Adjust the security settings for your account. From Two-factor authentication, password, API keys and other security related functions.', + TITLE_TEXT: + 'Adjust the security settings for your account. From Two-factor authentication, password, API keys and other security related functions.', OTP: { TITLE: 'Authentification à deux facteurs', OTP_ENABLED: 'otp activé', @@ -713,12 +713,13 @@ export default { MESSAGE_5: 'Manuellement', INPUT: 'Entrez votre One-Time Password (OTP)', WARNING: - 'Nous vous recommandons vivement de configurer l\'authentification à 2 facteurs (2FA). Cela augmentera considérablement la sécurité de vos fonds.', - ENABLE: 'Activer l\'authentification à deux facteurs ', - DISABLE: 'Désactiver l\'authentification à deux facteurs', + "Nous vous recommandons vivement de configurer l'authentification à 2 facteurs (2FA). Cela augmentera considérablement la sécurité de vos fonds.", + ENABLE: "Activer l'authentification à deux facteurs ", + DISABLE: "Désactiver l'authentification à deux facteurs", SECRET_1: 'Enter yor secret key', SECRET_2: 'Please enter your secret key to confirm you wrote it down.', - SECRET_3: 'This secret key will help you recover your account if you lost access to your phone.', + SECRET_3: + 'This secret key will help you recover your account if you lost access to your phone.', INPUT_1: 'Secret Key', TITLE_2: 'Enter One-Time Password (OTP)', diff --git a/web/src/config/lang/id.js b/web/src/config/lang/id.js index 480612de75..661c9ba6a8 100644 --- a/web/src/config/lang/id.js +++ b/web/src/config/lang/id.js @@ -79,7 +79,6 @@ export default { FOOTER: { FOOTER_LEGAL: ['Proudly made in Seoul, South Korea', 'bitHolla Inc.'], FOOTER_LANGUAGE_TEXT: 'LANGUAGE', - FOOTER_COPYRIGHT: 'Powered by bitHolla', SECTIONS: { SECTION_1_TITLE: 'ABOUT', SECTION_1_LINK_1: 'About Us', @@ -191,7 +190,7 @@ export default { CANT_LOGIN: 'Tidak bisa masuk?', NO_ACCOUNT: 'Tidak punya akun?', CREATE_ACCOUNT: 'Buatlah di sini', - HELP: 'Bantuan' + HELP: 'Bantuan', }, FORM_FIELDS: { EMAIL_LABEL: 'Email', @@ -290,7 +289,7 @@ export default { TOTAL_COST: 'Jumlah biaya', BUTTON: 'Lihat pesan {0}', INPUT: 'Pembelian {0} {1}', - TRADE_TITLE: '{0} {1}' // quick buy + TRADE_TITLE: '{0} {1}', // quick buy }, PREVIOUS_PAGE: 'halaman sebelumnya', NEXT_PAGE: 'halaman berikutnya', @@ -639,16 +638,11 @@ export default { // new ALL_AUDIO: 'Semua Audio cue', PUBLIC_TRADE_AUDIO: 'Apabila perdagangan publik dibuat', - ORDERS_PARTIAL_AUDIO: - 'Apabila salah satu pesan Anda terisi sebagian', - ORDERS_PLACED_AUDIO: - 'Apabila pesan dibuat', - ORDERS_CANCELED_AUDIO: - 'Apabila pesan Anda dibatalkan', - ORDERS_COMPLETED_AUDIO: - 'Apabila salah satu pesan Anda terisi penuh', - CLICK_AMOUNTS_AUDIO: - 'Apabila klik jumlah dan harga di orderbook', + ORDERS_PARTIAL_AUDIO: 'Apabila salah satu pesan Anda terisi sebagian', + ORDERS_PLACED_AUDIO: 'Apabila pesan dibuat', + ORDERS_CANCELED_AUDIO: 'Apabila pesan Anda dibatalkan', + ORDERS_COMPLETED_AUDIO: 'Apabila salah satu pesan Anda terisi penuh', + CLICK_AMOUNTS_AUDIO: 'Apabila klik jumlah dan harga di orderbook', GET_QUICK_TRADE_AUDIO: 'Apabila mendapatkan kutipan untuk perdagangan cepat', SUCCESS_QUICK_TRADE_AUDIO: @@ -677,7 +671,8 @@ export default { WITHDRAWALS: 'Penarikan', }, ACCOUNT_SECURITY: { - TITLE_TEXT:'Atur pengaturan keamanan akun Anda. Mulai dari Autentikasi dua-faktor, kata sandi, Kunci API, dan fungsi lain yang berkaitan dengan keamanan.', + TITLE_TEXT: + 'Atur pengaturan keamanan akun Anda. Mulai dari Autentikasi dua-faktor, kata sandi, Kunci API, dan fungsi lain yang berkaitan dengan keamanan.', OTP: { TITLE: 'Autentikasi Dua-Faktor', OTP_ENABLED: 'otp diaktifkan', @@ -707,7 +702,8 @@ export default { DISABLE: 'Nonaktifkan Autentikasi Dua-Faktor', SECRET_1: 'Enter yor secret key', SECRET_2: 'Please enter your secret key to confirm you wrote it down.', - SECRET_3: 'This secret key will help you recover your account if you lost access to your phone.', + SECRET_3: + 'This secret key will help you recover your account if you lost access to your phone.', INPUT_1: 'Secret Key', TITLE_2: 'Masukkan One-Time Password (OTP)', @@ -746,8 +742,9 @@ export default { TITLE: 'Riwayat Masuk', CONTENT: { TITLE: 'Riwayat Masuk', - MESSAGE: 'Below is login history list with details IP, country and time details. If you see any suspicious activity you should change your password and contact support' - } + MESSAGE: + 'Below is login history list with details IP, country and time details. If you see any suspicious activity you should change your password and contact support', + }, }, FREEZE: { TITLE: 'Freeze Account', @@ -820,7 +817,7 @@ export default { RECENT_TRADES: 'perdagangan saya baru-baru ini', // ToDo PUBLIC_SALES: 'PENJUALAN PUBLIK', // ToDo REMAINING: 'Remaining', - FULLFILLED: '{0} % Fullfilled', + FULLFILLED: '{0} % Filled', FILLED: 'Filled', // new LOWEST_PRICE: 'Lowest Price ({0})', // new PHASE: 'Phase', // new @@ -869,8 +866,7 @@ export default { WITHDRAWALS_FORM_AMOUNT_LABEL: 'Jumlan penarikan {0}', WITHDRAWALS_FORM_AMOUNT_PLACEHOLDER: 'Type the amount of {0} you wish to withdraw', - WITHDRAWALS_FORM_FEE_COMMON_LABEL: - 'Biaya transaksi', + WITHDRAWALS_FORM_FEE_COMMON_LABEL: 'Biaya transaksi', WITHDRAWALS_FORM_FEE_FIAT_LABEL: 'Bank withdrawal fee', WITHDRAWALS_FORM_FEE_PLACEHOLDER: 'Type the amount of {0} you wish to use in the fee of the transaction', diff --git a/web/src/config/lang/ja.js b/web/src/config/lang/ja.js index bd4585191b..4e927721e4 100644 --- a/web/src/config/lang/ja.js +++ b/web/src/config/lang/ja.js @@ -78,7 +78,6 @@ export default { FOOTER: { FOOTER_LEGAL: ['Proudly made in Seoul, South Korea', 'bitHolla Inc.'], FOOTER_LANGUAGE_TEXT: 'LANGUAGE', - FOOTER_COPYRIGHT: 'Powered by bitHolla', SECTIONS: { SECTION_1_TITLE: 'ABOUT', SECTION_1_LINK_1: 'About Us', @@ -188,10 +187,10 @@ export default { }, LOGIN: { LOGIN_TO: '{0}にログイン', - CANT_LOGIN: "ログインにお困りですか?", - NO_ACCOUNT: "アカウントをお持ちでないですか?", + CANT_LOGIN: 'ログインにお困りですか?', + NO_ACCOUNT: 'アカウントをお持ちでないですか?', CREATE_ACCOUNT: 'アカウント作成', - HELP: 'ヘルプ' + HELP: 'ヘルプ', }, FORM_FIELDS: { EMAIL_LABEL: 'メール', @@ -290,7 +289,7 @@ export default { TOTAL_COST: '総額', BUTTON: '{0}', INPUT: '{0}を{1}', - TRADE_TITLE: '{0} {1}' // quick buy + TRADE_TITLE: '{0} {1}', // quick buy }, PREVIOUS_PAGE: '前のページ', NEXT_PAGE: '次のページ', @@ -608,28 +607,20 @@ export default { TRADE_POPUPS: '注文アラート', POPUP_ORDER_CONFIRMATION: '注文を入れる前に確認する時', POPUP_ORDER_COMPLETED: '注文が完了した時', - POPUP_ORDER_PARTIALLY_FILLED: - '注文が一部成立した時' + POPUP_ORDER_PARTIALLY_FILLED: '注文が一部成立した時', }, - AUDIO_CUE_FORM: { // new + AUDIO_CUE_FORM: { + // new ALL_AUDIO: '全てのサウンド', PUBLIC_TRADE_AUDIO: '全ての取引に対して', - ORDERS_PARTIAL_AUDIO: - '注文が一部成立した時', - ORDERS_PLACED_AUDIO: - '注文が入った時', - ORDERS_CANCELED_AUDIO: - '注文がキャンセルされた時', - ORDERS_COMPLETED_AUDIO: - '注文が全て成立した時', - CLICK_AMOUNTS_AUDIO: - '注文一覧の数量と価格をクリックする時', - GET_QUICK_TRADE_AUDIO: - 'クイック取引の注文', - SUCCESS_QUICK_TRADE_AUDIO: - 'クイック取引の注文成立', - QUICK_TRADE_TIMEOUT_AUDIO: - 'クイック取引のタイムアウト', + ORDERS_PARTIAL_AUDIO: '注文が一部成立した時', + ORDERS_PLACED_AUDIO: '注文が入った時', + ORDERS_CANCELED_AUDIO: '注文がキャンセルされた時', + ORDERS_COMPLETED_AUDIO: '注文が全て成立した時', + CLICK_AMOUNTS_AUDIO: '注文一覧の数量と価格をクリックする時', + GET_QUICK_TRADE_AUDIO: 'クイック取引の注文', + SUCCESS_QUICK_TRADE_AUDIO: 'クイック取引の注文成立', + QUICK_TRADE_TIMEOUT_AUDIO: 'クイック取引のタイムアウト', }, RISK_MANAGEMENT: { INFO_TEXT: @@ -653,7 +644,8 @@ export default { WITHDRAWALS: '出金', }, ACCOUNT_SECURITY: { - TITLE_TEXT:'2段階認証、パスワード、APIキー、その他のセキュリティ関連機能でアカウントのセキュリティ設定を調整できます。', + TITLE_TEXT: + '2段階認証、パスワード、APIキー、その他のセキュリティ関連機能でアカウントのセキュリティ設定を調整できます。', OTP: { TITLE: '2段階認証(2FA)', OTP_ENABLED: '2段階認証設定', @@ -683,7 +675,8 @@ export default { DISABLE: '2段階認証を解除する', SECRET_1: 'Enter yor secret key', SECRET_2: 'Please enter your secret key to confirm you wrote it down.', - SECRET_3: 'This secret key will help you recover your account if you lost access to your phone.', + SECRET_3: + 'This secret key will help you recover your account if you lost access to your phone.', INPUT_1: 'Secret Key', TITLE_2: 'OTP(One-Time Password)コード入力', @@ -756,7 +749,7 @@ export default { }, TYPES: [ { value: 'market', label: '成行' }, // DO NOT CHANGE value, ONLY TRANSLATE label - { value: 'limit', label: '指値' } // DO NOT CHANGE value, ONLY TRANSLATE label + { value: 'limit', label: '指値' }, // DO NOT CHANGE value, ONLY TRANSLATE label ], SIDE: 'Side', SIDES_VALUES: { @@ -797,7 +790,7 @@ export default { RECENT_TRADES: '最近の取引履歴', // ToDo PUBLIC_SALES: '全取引履歴', // ToDo REMAINING: 'Remaining', - FULLFILLED: '{0} % Fullfilled', + FULLFILLED: '{0} % Filled', FILLED: 'Filled', // new LOWEST_PRICE: 'Lowest Price ({0})', // new PHASE: 'Phase', // new @@ -815,7 +808,7 @@ export default { h: 'High', l: 'Low', c: 'Close', - v: '取引量' + v: '取引量', }, QUICK_TRADE: 'クイック取引', PRO_TRADE: 'プロ向け取引', @@ -846,8 +839,7 @@ export default { WITHDRAWALS_FORM_AMOUNT_LABEL: '送金する {0} 金額', WITHDRAWALS_FORM_AMOUNT_PLACEHOLDER: 'Type the amount of {0} you wish to withdraw', - WITHDRAWALS_FORM_FEE_COMMON_LABEL: - '取引手数料', + WITHDRAWALS_FORM_FEE_COMMON_LABEL: '取引手数料', WITHDRAWALS_FORM_FEE_FIAT_LABEL: 'Bank withdrawal fee', WITHDRAWALS_FORM_FEE_PLACEHOLDER: 'Type the amount of {0} you wish to use in the fee of the transaction', diff --git a/web/src/config/lang/ko.js b/web/src/config/lang/ko.js index 642912155b..a0b2f1e6ef 100644 --- a/web/src/config/lang/ko.js +++ b/web/src/config/lang/ko.js @@ -79,7 +79,6 @@ const nestedContent = { FOOTER: { FOOTER_LEGAL: ['Proudly made in Seoul, South Korea', 'bitHolla Inc.'], FOOTER_LANGUAGE_TEXT: 'LANGUAGE', - FOOTER_COPYRIGHT: 'Powered by bitHolla', SECTIONS: { SECTION_1_TITLE: 'ABOUT', SECTION_1_LINK_1: 'About Us', @@ -604,24 +603,18 @@ const nestedContent = { POPUP_ORDER_PARTIALLY_FILLED: '주문이 부분적으로 채워질 경우 알림창', }, AUDIO_CUE_FORM: { - ALL_AUDIO: '모든 사운드', + ALL_AUDIO: '모든 사운드', PUBLIC_TRADE_AUDIO: '모든 일반적인 거래에 대해 알림사운드 재생', ORDERS_PARTIAL_AUDIO: '주문 중 하나가 부분적으로 채워질 경우 알림사운드 재생', - ORDERS_PLACED_AUDIO: - '주문 요청 시', - ORDERS_CANCELED_AUDIO: - '주문 취소 시', + ORDERS_PLACED_AUDIO: '주문 요청 시', + ORDERS_CANCELED_AUDIO: '주문 취소 시', ORDERS_COMPLETED_AUDIO: '하나의 주문이 완전히 채워질 경우 알림 사운드 재생', - CLICK_AMOUNTS_AUDIO: - '주문 목록에 금액과 가격 클릭 시', - GET_QUICK_TRADE_AUDIO: - '퀵 트레이딩 요청 받을 시', - SUCCESS_QUICK_TRADE_AUDIO: - '퀵 트레이딩 성공 시', - QUICK_TRADE_TIMEOUT_AUDIO: - '퀵 트레이딩 시간 만료 시', + CLICK_AMOUNTS_AUDIO: '주문 목록에 금액과 가격 클릭 시', + GET_QUICK_TRADE_AUDIO: '퀵 트레이딩 요청 받을 시', + SUCCESS_QUICK_TRADE_AUDIO: '퀵 트레이딩 성공 시', + QUICK_TRADE_TIMEOUT_AUDIO: '퀵 트레이딩 시간 만료 시', }, RISK_MANAGEMENT: { INFO_TEXT: @@ -644,7 +637,8 @@ const nestedContent = { WITHDRAWALS: '출금', }, ACCOUNT_SECURITY: { - TITLE_TEXT:'계정의 보안 설정을 변경하세요. 이중인증, 비밀번호, API 키 및 관련 보안기능', + TITLE_TEXT: + '계정의 보안 설정을 변경하세요. 이중인증, 비밀번호, API 키 및 관련 보안기능', OTP: { TITLE: '2단계 인증(2FA)', OTP_ENABLED: ' OTP 사용', @@ -674,7 +668,8 @@ const nestedContent = { DISABLE: '2단계 인증(2FA) 사용안함', SECRET_1: 'Enter yor secret key', SECRET_2: 'Please enter your secret key to confirm you wrote it down.', - SECRET_3: 'This secret key will help you recover your account if you lost access to your phone.', + SECRET_3: + 'This secret key will help you recover your account if you lost access to your phone.', INPUT_1: 'Secret Key', TITLE_2: 'OTP 입력', @@ -760,7 +755,7 @@ const nestedContent = { ], DEFAULT_TOGGLE_OPTIONS: [ { value: true, label: '켜기' }, - { value: false, label: '끄기' } + { value: false, label: '끄기' }, ], SIZE: 'Size', PRICE: 'Price', @@ -788,7 +783,7 @@ const nestedContent = { RECENT_TRADES: '최근 체결 내역', // ToDo PUBLIC_SALES: 'public sales', // ToDo REMAINING: 'Remaining', - FULLFILLED: '{0} % Fullfilled', + FULLFILLED: '{0} % Filled', PRICE_CURRENCY: 'PRICE ({0})', AMOUNT_SYMBOL: 'AMOUNT ({0})', MARKET_PRICE: '시장가', @@ -802,7 +797,7 @@ const nestedContent = { h: 'High', l: 'Low', c: 'Close', - v: '거래량' + v: '거래량', }, QUICK_TRADE: '퀵트레이딩', PRO_TRADE: '프로트레이딩', diff --git a/web/src/config/lang/vi.js b/web/src/config/lang/vi.js index 98ef4dc46c..81c1697e30 100644 --- a/web/src/config/lang/vi.js +++ b/web/src/config/lang/vi.js @@ -79,7 +79,6 @@ export default { FOOTER: { FOOTER_LEGAL: ['Proudly made in Seoul, South Korea', 'bitHolla Inc.'], FOOTER_LANGUAGE_TEXT: 'LANGUAGE', - FOOTER_COPYRIGHT: 'Powered by bitHolla', SECTIONS: { SECTION_1_TITLE: 'ABOUT', SECTION_1_LINK_1: 'About Us', @@ -290,7 +289,7 @@ export default { TOTAL_COST: 'Tổng chi phí', BUTTON: 'Kiểm tra yêu cầu đặt hàng {0}', INPUT: 'Số lượng {0} muốn {1}', - TRADE_TITLE: '{1} {0}' // quick buy + TRADE_TITLE: '{1} {0}', // quick buy }, PREVIOUS_PAGE: 'Trang trước', NEXT_PAGE: 'Trang tiếp theo', @@ -466,7 +465,8 @@ export default { POSTAL_CODE_LABEL: 'Mã bưu điện', POSTAL_CODE_PLACEHOLDER: 'Vui lòng nhập mã bưu điện', PHONE_CODE_LABEL: 'Quốc Gia', - PHONE_CODE_PLACEHOLDER: 'Vui lòng chọn quốc gia nơi số điện thoại của bạn được hòa mạng', + PHONE_CODE_PLACEHOLDER: + 'Vui lòng chọn quốc gia nơi số điện thoại của bạn được hòa mạng', PHONE_CODE_DISPLAY: '({0}) {1}', // 0 -> (+21) 1 -> South Korea PHONE_NUMBER_LABEL: 'Số điện thoại', PHONE_NUMBER_PLACEHOLDER: 'Vui lòng nhập số điện thoại của quý khách', @@ -637,25 +637,21 @@ export default { POPUP_ORDER_PARTIALLY_FILLED: 'Hiển thị cửa sổ thông báo khi đơn đặt hàng được tiến hành từng phần', }, - AUDIO_CUE_FORM: { // new + AUDIO_CUE_FORM: { + // new ALL_AUDIO: 'Bật âm thanh cho tất cả', PUBLIC_TRADE_AUDIO: 'KHi giao dịch công khai được thực hiện', ORDERS_PARTIAL_AUDIO: 'KHi một trong những đơn đặt hàng được tiến hành từng phần', - ORDERS_PLACED_AUDIO: - 'Khi một đơn đặt hàng được tạo', - ORDERS_CANCELED_AUDIO: - 'Khi một đơn đặt hàng bị hủy', + ORDERS_PLACED_AUDIO: 'Khi một đơn đặt hàng được tạo', + ORDERS_CANCELED_AUDIO: 'Khi một đơn đặt hàng bị hủy', ORDERS_COMPLETED_AUDIO: 'Khi một trong những đơn đặt hàng hoàn tất thành công', - CLICK_AMOUNTS_AUDIO: - 'Khi nhấn chọn số lượng và giá trong sổ lệnh', - GET_QUICK_TRADE_AUDIO: - 'Khi có một lời nhẳn trong giao dịch nhanh', + CLICK_AMOUNTS_AUDIO: 'Khi nhấn chọn số lượng và giá trong sổ lệnh', + GET_QUICK_TRADE_AUDIO: 'Khi có một lời nhẳn trong giao dịch nhanh', SUCCESS_QUICK_TRADE_AUDIO: 'Khi có một giao dịch nhanh diễn ra thành công', - QUICK_TRADE_TIMEOUT_AUDIO: - 'Khi hết thời gian giao dịch nhanh', + QUICK_TRADE_TIMEOUT_AUDIO: 'Khi hết thời gian giao dịch nhanh', }, RISK_MANAGEMENT: { INFO_TEXT: @@ -679,7 +675,8 @@ export default { WITHDRAWALS: 'Rút tiền', }, ACCOUNT_SECURITY: { - TITLE_TEXT:'Điều chỉnh các cài đặt bảo mật cho tài khoản của quý khách. Có thể cài đặt Xác thực 2 yếu tố, mật khẩu, khóa API và các chức năng bảo mật khác.', + TITLE_TEXT: + 'Điều chỉnh các cài đặt bảo mật cho tài khoản của quý khách. Có thể cài đặt Xác thực 2 yếu tố, mật khẩu, khóa API và các chức năng bảo mật khác.', OTP: { TITLE: 'Xác thực 2 yếu tố', OTP_ENABLED: 'Bật OTP', @@ -709,11 +706,13 @@ export default { DISABLE: 'Tắt Xác thực 2 yếu Tối', SECRET_1: 'Enter yor secret key', SECRET_2: 'Please enter your secret key to confirm you wrote it down.', - SECRET_3: 'This secret key will help you recover your account if you lost access to your phone.', + SECRET_3: + 'This secret key will help you recover your account if you lost access to your phone.', INPUT_1: 'Secret Key', TITLE_2: 'Nhập mật khẩu một lần (OTP)', - MESSAGE_6: 'Vui lòng nhập mật khẩu một lần bao gồm 6 ký tự xuống phía dưới.', + MESSAGE_6: + 'Vui lòng nhập mật khẩu một lần bao gồm 6 ký tự xuống phía dưới.', INPUT_2: 'Mật khẩu một lần (OTP)', }, FORM: { @@ -748,8 +747,9 @@ export default { TITLE: 'Lịch sử đăng nhập', CONTENT: { TITLE: 'Lịch sử đăng nhập', - MESSAGE: 'Below is login history list with details IP, country and time details. If you see any suspicious activity you should change your password and contact support' - } + MESSAGE: + 'Below is login history list with details IP, country and time details. If you see any suspicious activity you should change your password and contact support', + }, }, FREEZE: { TITLE: 'Freeze Account', @@ -781,7 +781,7 @@ export default { }, TYPES: [ { value: 'market', label: 'Thị trường' }, // DO NOT CHANGE value, ONLY TRANSLATE label - { value: 'limit', label: 'Giới hạn' } // DO NOT CHANGE value, ONLY TRANSLATE label + { value: 'limit', label: 'Giới hạn' }, // DO NOT CHANGE value, ONLY TRANSLATE label ], SIDE: 'Side', SIDES_VALUES: { @@ -822,7 +822,7 @@ export default { RECENT_TRADES: 'Giao dịch gần đây của tôi', // ToDo PUBLIC_SALES: 'BÁN CÔNG KHAI', // ToDo REMAINING: 'Remaining', - FULLFILLED: '{0} % Fullfilled', + FULLFILLED: '{0} % Filled', FILLED: 'Filled', // new LOWEST_PRICE: 'Lowest Price ({0})', // new PHASE: 'Phase', // new @@ -840,7 +840,7 @@ export default { h: 'High', l: 'Low', c: 'Close', - v: 'Khối lượng' + v: 'Khối lượng', }, QUICK_TRADE: 'Giao dịch nhanh', PRO_TRADE: 'Giao dịch chuyên nghiệp', @@ -871,8 +871,7 @@ export default { WITHDRAWALS_FORM_AMOUNT_LABEL: 'Số lượng {0} muốn rút', WITHDRAWALS_FORM_AMOUNT_PLACEHOLDER: 'Type the amount of {0} you wish to withdraw', - WITHDRAWALS_FORM_FEE_COMMON_LABEL: - 'Phí giao dịch', + WITHDRAWALS_FORM_FEE_COMMON_LABEL: 'Phí giao dịch', WITHDRAWALS_FORM_FEE_FIAT_LABEL: 'Bank withdrawal fee', WITHDRAWALS_FORM_FEE_PLACEHOLDER: 'Type the amount of {0} you wish to use in the fee of the transaction', diff --git a/web/src/config/lang/zh.js b/web/src/config/lang/zh.js index 3d286fa3bf..d83c12b584 100644 --- a/web/src/config/lang/zh.js +++ b/web/src/config/lang/zh.js @@ -35,7 +35,7 @@ export default { HELP_TELEGRAM_LINK: 'https://apidocs.hollaex.com', NEED_HELP_TEXT: '需要帮助吗?', // new HELP_TEXT: '帮助', - SUCCESS_TEXT: '成功', + SUCCESS_TEXT: '成功', ERROR_TEXT: '错误', PROCEED: '实行', EDIT_TEXT: '编辑', @@ -75,7 +75,6 @@ export default { FOOTER: { FOOTER_LEGAL: ['Proudly made in Seoul, South Korea', 'bitHolla Inc.'], FOOTER_LANGUAGE_TEXT: 'LANGUAGE', - FOOTER_COPYRIGHT: 'Powered by bitHolla', SECTIONS: { SECTION_1_TITLE: 'ABOUT', SECTION_1_LINK_1: 'About Us', @@ -271,7 +270,7 @@ export default { TOTAL_COST: '总费用', BUTTON: '审阅{0}订单', INPUT: '{0}{1}', - TRADE_TITLE: '{0} {1}' // quick buy + TRADE_TITLE: '{0} {1}', // quick buy }, PREVIOUS_PAGE: '上一页', NEXT_PAGE: '下一页', @@ -576,25 +575,18 @@ export default { POPUP_ORDER_COMPLETED: '订单完成时弹窗', POPUP_ORDER_PARTIALLY_FILLED: '部分订单已满时弹窗', }, - AUDIO_CUE_FORM: { // new + AUDIO_CUE_FORM: { + // new ALL_AUDIO: '所有声音提示', PUBLIC_TRADE_AUDIO: '公开交易播放提醒', - ORDERS_PARTIAL_AUDIO: - '当一个订单部分已满时,播放提醒', - ORDERS_PLACED_AUDIO: - '创建订单时', - ORDERS_CANCELED_AUDIO: - '取消订单时', - ORDERS_COMPLETED_AUDIO: - '当一个订单已满时,播放提醒', - CLICK_AMOUNTS_AUDIO: - '在订单页面点击数量和金额时', - GET_QUICK_TRADE_AUDIO: - '获得快速交易报价时 ', - SUCCESS_QUICK_TRADE_AUDIO: - '成功进行快速交易时', - QUICK_TRADE_TIMEOUT_AUDIO: - '快速交易超时时', + ORDERS_PARTIAL_AUDIO: '当一个订单部分已满时,播放提醒', + ORDERS_PLACED_AUDIO: '创建订单时', + ORDERS_CANCELED_AUDIO: '取消订单时', + ORDERS_COMPLETED_AUDIO: '当一个订单已满时,播放提醒', + CLICK_AMOUNTS_AUDIO: '在订单页面点击数量和金额时', + GET_QUICK_TRADE_AUDIO: '获得快速交易报价时 ', + SUCCESS_QUICK_TRADE_AUDIO: '成功进行快速交易时', + QUICK_TRADE_TIMEOUT_AUDIO: '快速交易超时时', }, RISK_MANAGEMENT: { INFO_TEXT: @@ -617,7 +609,7 @@ export default { WITHDRAWALS: '提款', }, ACCOUNT_SECURITY: { - TITLE_TEXT:'调整账户的安全设置,谷歌验证、密码、API密钥等安全相关功能。', + TITLE_TEXT: '调整账户的安全设置,谷歌验证、密码、API密钥等安全相关功能。', OTP: { TITLE: '谷歌验证(2FA)', OTP_ENABLED: '启用OTP', @@ -639,13 +631,13 @@ export default { MESSAGE_4: '你可以安全地保存该QR码,以便以后更换或丢失手机时恢复2FA。', MESSAGE_5: '指南', INPUT: '输入一次性密码(OTP)', - WARNING: - '强烈建议你设置谷歌验证(2FA),这将大幅提高资金的安全性。 ', + WARNING: '强烈建议你设置谷歌验证(2FA),这将大幅提高资金的安全性。 ', ENABLE: '启用谷歌验证(2FA)', DISABLE: '禁用双因素认证(2FA)', SECRET_1: 'Enter yor secret key', SECRET_2: 'Please enter your secret key to confirm you wrote it down.', - SECRET_3: 'This secret key will help you recover your account if you lost access to your phone.', + SECRET_3: + 'This secret key will help you recover your account if you lost access to your phone.', INPUT_1: 'Secret Key', TITLE_2: 'Enter One-Time Password (OTP)', @@ -683,8 +675,9 @@ export default { TITLE: '登录记录', CONTENT: { TITLE: '交易记录', - MESSAGE: 'Below is login history list with details IP, country and time details. If you see any suspicious activity you should change your password and contact support' - } + MESSAGE: + 'Below is login history list with details IP, country and time details. If you see any suspicious activity you should change your password and contact support', + }, }, FREEZE: { TITLE: 'Freeze Account', @@ -716,7 +709,7 @@ export default { }, TYPES: [ { value: 'market', label: '市价' }, - { value: 'limit', label: '限价' } + { value: 'limit', label: '限价' }, ], SIDE: 'Side', SIDES_VALUES: { @@ -757,7 +750,7 @@ export default { RECENT_TRADES: '最近交易', // ToDo PUBLIC_SALES: '实时成交', // ToDo REMAINING: 'Remaining', - FULLFILLED: '{0} % Fullfilled', + FULLFILLED: '{0} % Filled', PRICE_CURRENCY: 'PRICE ({0})', AMOUNT_SYMBOL: 'AMOUNT ({0})', MARKET_PRICE: '市场价格', @@ -952,8 +945,7 @@ export default { TITLE: 'API密钥', INFORMATION_TEXT: '该API提供的功能包括获取钱包余额、管理买入/卖出订单、请求提款以及最近交易、订单簿和行情等市场数据。', - ERROR_INACTIVE_OTP: - '若要生成API Kye,需要启用双因素认证(2FA)。', + ERROR_INACTIVE_OTP: '若要生成API Kye,需要启用双因素认证(2FA)。', ENABLE_2FA: '启用谷歌验证', WARNING_TEXT: '不要与他人分享你的API Key', GENERATE_KEY: '生成API Kye', @@ -1168,7 +1160,7 @@ export default { LEARN_MORE_WAVE_AUCTION: '了解更多关于波段拍卖的信息', // new WAVE_NUMBER: '波段号', // new DISCOUNT: '( {0}%折扣 )', // new - MY_FEES_LIMITS: ' 我的手续费及限额' // ne + MY_FEES_LIMITS: ' 我的手续费及限额', // ne }, REFERRAL_LINK: { TITLE: '邀请好友', // new diff --git a/web/src/containers/Admin/Dashboard/index.js b/web/src/containers/Admin/Dashboard/index.js index 311f56be41..ed8f38ce89 100644 --- a/web/src/containers/Admin/Dashboard/index.js +++ b/web/src/containers/Admin/Dashboard/index.js @@ -58,8 +58,15 @@ const Dashboard = ({ constants = {} }) => {
( + exchange-logo + )} />
{showLabel}
diff --git a/web/src/containers/Admin/Deposits/utils.js b/web/src/containers/Admin/Deposits/utils.js index 4818ac175d..29df8aeb98 100644 --- a/web/src/containers/Admin/Deposits/utils.js +++ b/web/src/containers/Admin/Deposits/utils.js @@ -16,10 +16,11 @@ export const renderValidation = ({ status, dismissed, rejected, + processing, completeDeposit, updatingItem, }) => - !status && !dismissed && !rejected ? ( + !status && !dismissed && !rejected && !processing ? (
-

Small text often used for copywrite or other business data

+

Add a link to your Terms of Service and Privacy Policy

+
+
+

+ Referral Badge{' '} + + + +

+
+

+ Edit the referral badge in the bottom left corner. This space can be + repurposed for copyright or other business related data. +

+ + {collateral_level === 'zero' && ( +
+
+
Fully rebrand your platform
+
+ Replace the badge with your own branding. +
+
+ +
+ )} + +
); } } -export default Description; +const mapStateToProps = (store) => ({ + constants: store.app.constants, +}); + +export default connect(mapStateToProps)(Description); diff --git a/web/src/containers/Admin/General/DisableSignupsConfirmation.js b/web/src/containers/Admin/General/DisableSignupsConfirmation.js new file mode 100644 index 0000000000..6ba449ff51 --- /dev/null +++ b/web/src/containers/Admin/General/DisableSignupsConfirmation.js @@ -0,0 +1,29 @@ +import React from 'react'; +import { Button, Modal } from 'antd'; +import { WarningFilled } from '@ant-design/icons'; + +const DisableSignupsConfirmation = ({ onConfirm, ...rest }) => ( + +
+

Turn off sign ups

+
+
+ +
+
+ Turning off sign ups will mean no new users will be able to sign up on + your platform. Are you sure you want to do this? +
+
+
+ +
+
+
+); + +export default DisableSignupsConfirmation; diff --git a/web/src/containers/Admin/General/EmailVerificationForm.js b/web/src/containers/Admin/General/EmailVerificationForm.js new file mode 100644 index 0000000000..47309c5fc4 --- /dev/null +++ b/web/src/containers/Admin/General/EmailVerificationForm.js @@ -0,0 +1,66 @@ +import React from 'react'; +import { Button, Checkbox, Form } from 'antd'; +import { InfoCircleFilled } from '@ant-design/icons'; + +const { Item } = Form; + +const EmailVerificationForm = ({ + initialValues = {}, + handleSaveEmailVerification, +}) => { + const handleSubmit = (values) => { + let formValues = {}; + if (values) { + formValues = { + email_verification_required: !!values.email_verification_required, + }; + handleSaveEmailVerification(formValues); + } + }; + return ( +
+
Email verification
+ +
+ + +
+
+ Enable email verification requirement +
+
+
+
+
+
+ + + If enabled you must have an automated email service setup + configured. + +
+
+
+
+ +
+ +
+ ); +}; + +export default EmailVerificationForm; diff --git a/web/src/containers/Admin/General/index.css b/web/src/containers/Admin/General/index.css index 3010dae5fe..a07263f589 100644 --- a/web/src/containers/Admin/General/index.css +++ b/web/src/containers/Admin/General/index.css @@ -296,10 +296,14 @@ h2, right: 8px; bottom: 3px; } -.description_footer { +.referral_badge_note { bottom: 60px; right: 7px; } +.description_footer { + bottom: 42px; + right: 4px; +} .custom-form-wrapper { display: flex; margin: 1.5rem 0rem; diff --git a/web/src/containers/Admin/General/index.js b/web/src/containers/Admin/General/index.js index bd2fc2b0f9..e9d812982b 100644 --- a/web/src/containers/Admin/General/index.js +++ b/web/src/containers/Admin/General/index.js @@ -1,5 +1,5 @@ import React, { Component } from 'react'; -import { Button, Modal, message, Collapse, Spin } from 'antd'; +import { Switch, Button, Modal, message, Collapse, Spin } from 'antd'; import { connect } from 'react-redux'; import { browserHistory } from 'react-router'; import { bindActionCreators } from 'redux'; @@ -7,6 +7,8 @@ import { bindActionCreators } from 'redux'; import FooterConfig from './FooterConfig'; import Description from './Description'; import InterfaceForm from './InterfaceForm'; +import EmailVerificationForm from './EmailVerificationForm'; +import DisableSignupsConfirmation from './DisableSignupsConfirmation'; import { EmailSettingsForm } from '../Settings/SettingsForm'; import { AdminHocForm } from '../../../components'; import Image from '../../../components/Image'; @@ -37,7 +39,10 @@ class General extends Component { initialThemeValues: {}, initialEmailValues: {}, initialLinkValues: {}, + initialEmailVerificationValues: {}, pendingPublishIcons: {}, + showDisableSignUpsConfirmation: false, + isSignUpActive: true, loading: false, }; } @@ -71,15 +76,28 @@ class General extends Component { let initialNameValues = { ...this.state.initialNameValues }; let initialLanguageValues = { ...this.state.initialLanguageValues }; let initialThemeValues = { ...this.state.initialThemeValues }; + let initialEmailVerificationValues = { + ...this.state.initialEmailVerificationValues, + }; const { kit = {}, secrets = { smtp: {}, captcha: {}, emails: {} } } = this.state.constants || {}; - const { api_name, defaults = {}, links = {} } = kit; + const { + api_name, + defaults = {}, + links = {}, + new_user_is_activated: isSignUpActive, + email_verification_required, + } = kit; initialNameValues = { ...initialNameValues, api_name }; initialLanguageValues = { ...initialLanguageValues, language: defaults.language, }; initialThemeValues = { ...initialThemeValues, theme: defaults.theme }; + initialEmailVerificationValues = { + ...initialEmailVerificationValues, + email_verification_required, + }; const { configuration = {} } = this.state.initialEmailValues || {}; const initialEmailValues = { @@ -94,6 +112,9 @@ class General extends Component { initialThemeValues, initialEmailValues, initialLinkValues, + isSignUpActive, + initialEmailVerificationValues, + showDisableSignUpsConfirmation: false, }); }; @@ -266,6 +287,26 @@ class General extends Component { this.handleSubmitGeneral(formValues); }; + handleSubmitReferralBadge = (formProps) => { + this.handleSubmitGeneral({ + kit: { + links: { + ...formProps, + }, + }, + }); + }; + + handleSubmitTOSlinks = (formProps) => { + this.handleSubmitGeneral({ + kit: { + links: { + ...formProps, + }, + }, + }); + }; + handleSubmitHelpDesk = (formProps) => { this.handleSubmitGeneral({ kit: { @@ -276,6 +317,22 @@ class General extends Component { }); }; + handleSubmitEmailVerification = (formProps) => { + this.handleSubmitGeneral({ + kit: { + ...formProps, + }, + }); + }; + + handleSubmitSignUps = (new_user_is_activated) => { + return this.handleSubmitGeneral({ + kit: { + new_user_is_activated, + }, + }); + }; + renderImageUpload = (id, theme, index, showLable = true) => { const { allIcons } = this.props; return ( @@ -318,6 +375,21 @@ class General extends Component { reload = () => window.location.reload(false); + handleSignUpsSwitch = () => { + const { isSignUpActive } = this.state; + if (isSignUpActive) { + this.setState({ + showDisableSignUpsConfirmation: true, + }); + } else { + this.handleSubmitSignUps(true); + } + }; + + disableSignUpsConfirmation = () => { + this.handleSubmitSignUps(false); + }; + render() { const { initialEmailValues, @@ -325,7 +397,10 @@ class General extends Component { initialLanguageValues, initialThemeValues, initialLinkValues, + initialEmailVerificationValues, loading, + isSignUpActive, + showDisableSignUpsConfirmation, } = this.state; const { kit = {} } = this.state.constants; const { coins, themeOptions } = this.props; @@ -450,7 +525,11 @@ class General extends Component { + Theme Specific Graphics + + } key="2" >
@@ -488,7 +567,11 @@ class General extends Component { + Theme Specific Graphics + + } key="2" > {themeOptions @@ -532,7 +615,60 @@ class General extends Component {
-
Onboarding background image
+

Onboarding

+
+ Setup the login and sign up section of your platform. +
+
Allow new sign ups
+
+ (Turning on sign ups will allow new users to sign up on your + platforms) +
+
+
+
+ + Off + + + + On + +
+
+
+ + + + this.setState({ showDisableSignUpsConfirmation: false }) + } + onConfirm={this.disableSignUpsConfirmation} + /> + +
Onboarding background image
+
+ (The image displayed in the background on your onboarding page) +
@@ -550,7 +686,11 @@ class General extends Component { + Theme Specific Graphics + + } key="2" >
@@ -589,8 +729,19 @@ class General extends Component { descriptionFields={generalFields.section_5} descriptionInitialValues={{ description: kit.description }} footerFields={generalFields.section_6} - footerInitialValues={{ description: kit.footer_description }} + ReferralBadgeFields={generalFields.section_8} + ReferralBadgeInitialValues={{ + hide_referral_badge: initialLinkValues.hide_referral_badge, + referral_label: initialLinkValues.referral_label, + referral_link: initialLinkValues.referral_link, + }} + footerInitialValues={{ + terms: initialLinkValues.terms, + privacy: initialLinkValues.privacy, + }} handleSubmitDescription={this.handleSubmitName} + handleSubmitFooterText={this.handleSubmitTOSlinks} + handleSubmitReferralBadge={this.handleSubmitReferralBadge} />
diff --git a/web/src/containers/Admin/General/utils.js b/web/src/containers/Admin/General/utils.js index 154ada501f..3879595af7 100644 --- a/web/src/containers/Admin/General/utils.js +++ b/web/src/containers/Admin/General/utils.js @@ -63,11 +63,15 @@ export const getGeneralFields = (coins) => ({ }, }, section_6: { - footer_description: { - type: 'textarea', - label: 'Small text', - placeholder: 'Write your small text filler', - validate: [validateRequired], + terms: { + type: 'input', + label: 'Terms of service link', + placeholder: 'https://', + }, + privacy: { + type: 'input', + label: 'Privacy policy link', + placeholder: 'https://', }, }, section_7: { @@ -77,4 +81,20 @@ export const getGeneralFields = (coins) => ({ placeholder: 'http://', }, }, + section_8: { + hide_referral_badge: { + type: 'checkbox', + label: 'Hide referral badge', + }, + referral_label: { + type: 'input', + label: 'Text label', + placeholder: 'Powered by bitHolla', + }, + referral_link: { + type: 'input', + label: 'Link', + placeholder: 'https://bitholla.com/', + }, + }, }); diff --git a/web/src/containers/Admin/Plugins/PluginConfigureForm.js b/web/src/containers/Admin/Plugins/PluginConfigureForm.js index 60fd228f56..4e60caf3a8 100644 --- a/web/src/containers/Admin/Plugins/PluginConfigureForm.js +++ b/web/src/containers/Admin/Plugins/PluginConfigureForm.js @@ -4,14 +4,14 @@ import { message, Spin } from 'antd'; import { STATIC_ICONS } from 'config/icons'; import { AdminHocForm } from '../../../components'; -import { updatePlugin, getPluginMeta } from './action'; +import { updatePluginMeta, getPluginMeta } from './action'; import './index.css'; const Form = AdminHocForm('PLUGIN_CONFIGURE_FORM'); const renderContent = (selectedPlugin, requestPlugin, metaData) => { const onSaveMeta = (values, plugin) => { - return updatePlugin({ name: plugin.name, meta: values }) + return updatePluginMeta({ name: plugin.name, meta: values }) .then((res) => { message.success('Data saved successfully'); requestPlugin(); diff --git a/web/src/containers/Admin/Plugins/PluginDetails.js b/web/src/containers/Admin/Plugins/PluginDetails.js index 312894a55d..ef05c5eb69 100644 --- a/web/src/containers/Admin/Plugins/PluginDetails.js +++ b/web/src/containers/Admin/Plugins/PluginDetails.js @@ -4,7 +4,7 @@ import { StarFilled, ClockCircleOutlined } from '@ant-design/icons'; import { Carousel } from 'components'; import { STATIC_ICONS } from 'config/icons'; -import { addPlugin, updatePlugin } from './action'; +import { addPlugin, updatePlugins } from './action'; const PluginDetails = ({ handleBreadcrumb, @@ -45,13 +45,14 @@ const PluginDetails = ({ const handleUpdatePlugin = () => { handleClose(); const body = { - name: pluginData.name, + ...pluginData, meta: { + ...pluginData.meta, version: pluginData.version, }, }; setUpdateLoading(true); - updatePlugin(body) + updatePlugins({ name: pluginData.name }, body) .then((res) => { setUpdateLoading(false); message.success('Plugin updated successfully'); diff --git a/web/src/containers/Admin/Plugins/action.js b/web/src/containers/Admin/Plugins/action.js index 240cfd8900..f2775e2663 100644 --- a/web/src/containers/Admin/Plugins/action.js +++ b/web/src/containers/Admin/Plugins/action.js @@ -4,14 +4,6 @@ import { REQUEST_VAULT_SUPPORTED_COINS } from '../../../config/constants'; import { requestAuthenticated } from '../../../utils'; import { PLUGIN_URL, NETWORK_API_URL } from '../../../config/constants'; -export const updatePlugins = (values) => { - const options = { - method: 'PUT', - body: JSON.stringify(values), - }; - return requestAuthenticated(`/plugins`, options); -}; - export const getConstants = (query) => requestAuthenticated(`/plugins?${querystring.stringify(query)}`); @@ -57,7 +49,20 @@ export const getPluginMeta = (params) => { ); }; -export const updatePlugin = (values) => { +export const updatePlugins = (params, values) => { + const options = { + method: 'PUT', + body: JSON.stringify(values), + }; + return requestAuthenticated( + `/plugins?${querystring.stringify(params)}`, + options, + null, + PLUGIN_URL + ); +}; + +export const updatePluginMeta = (values) => { const options = { method: 'PUT', body: JSON.stringify(values), diff --git a/web/src/containers/Admin/Tiers/Fees.js b/web/src/containers/Admin/Tiers/Fees.js index 40a9bc68dd..7f187ca004 100644 --- a/web/src/containers/Admin/Tiers/Fees.js +++ b/web/src/containers/Admin/Tiers/Fees.js @@ -91,7 +91,12 @@ const getHeaders = (userTiers, ICONS, onEditFees) => { return headers; }; -const Fees = ({ pairs, userTiers, icons: ICONS = {}, onEditFees }) => { +const Fees = ({ + pairs, + userTiers, + allIcons: { dark: ICONS = {} }, + onEditFees, +}) => { const coinsData = Object.keys(pairs).map((key) => pairs[key]); return (
diff --git a/web/src/containers/Admin/Tiers/Limits.js b/web/src/containers/Admin/Tiers/Limits.js index ed094caca1..5a3298965a 100644 --- a/web/src/containers/Admin/Tiers/Limits.js +++ b/web/src/containers/Admin/Tiers/Limits.js @@ -110,7 +110,7 @@ const getHeaders = (userTiers, ICONS, constants = {}, onEditLimit) => { const Limits = ({ coins, userTiers, - icons: ICONS = {}, + allIcons: { dark: ICONS = {} }, constants = {}, onEditLimit, }) => { diff --git a/web/src/containers/Admin/Tiers/ModalForm.js b/web/src/containers/Admin/Tiers/ModalForm.js index 15cfe8dc1a..a017bc312f 100644 --- a/web/src/containers/Admin/Tiers/ModalForm.js +++ b/web/src/containers/Admin/Tiers/ModalForm.js @@ -1,5 +1,5 @@ import React, { Component, Fragment } from 'react'; -import { Button, Input, InputNumber, message, Modal } from 'antd'; +import { Button, Input, InputNumber, message, Modal, Collapse } from 'antd'; import { WarningFilled, ExclamationCircleOutlined } from '@ant-design/icons'; import { Editor } from 'react-draft-wysiwyg'; import { EditorState, convertToRaw, ContentState } from 'draft-js'; @@ -7,7 +7,9 @@ import draftToHtml from 'draftjs-to-html'; import htmlToDraft from 'html-to-draftjs'; import 'react-draft-wysiwyg/dist/react-draft-wysiwyg.css'; import _get from 'lodash/get'; +import merge from 'lodash.merge'; +import { publish } from 'actions/operatorActions'; import withConfig from '../../../components/ConfigProvider/withConfig'; import Image from '../../../components/Image'; import { upload } from './action'; @@ -48,7 +50,7 @@ const Preview = ({ tierData = {}, onTypeChange, handleSave, - icons = {}, + allIcons = {}, ...rest }) => { return ( @@ -62,7 +64,7 @@ const Preview = ({
{/* {tierData.icon @@ -156,9 +158,10 @@ class NewTierForm extends Component { this.state = { requirements: [{ id: 1, point: '' }], tierData: this.props.editData || {}, - tierIcon: {}, + currentIcon: {}, editorState: EditorState.createEmpty(), loading: false, + pendingPublishIcons: {}, }; } @@ -179,71 +182,87 @@ class NewTierForm extends Component { }); } - handleSaveIcon = async () => { - const { tierIcon } = this.state; + handleSaveIcon = async (iconKey) => { + const { currentIcon } = this.state; const { updateIcons } = this.props; const icons = {}; + this.setState({ - error: false, loading: true, }); - for (const key in tierIcon) { - if (tierIcon.hasOwnProperty(key)) { - const file = tierIcon[key]; - if (file) { - const formData = new FormData(); - const { name: fileName } = file; - const extension = fileName.split('.').pop(); - const name = `${key}.${extension}`; - formData.append('name', name); - formData.append('file', file); - try { - const { - data: { path }, - } = await upload(formData); - icons[key] = path; - this.setState({ - tierData: { - ...this.state.tierData, - icon: path, - }, - }); - } catch (error) { - this.setState({ - loading: false, - }); - message.error('Something went wrong!'); - return; + for (const themeKey in currentIcon) { + if (currentIcon.hasOwnProperty(themeKey)) { + icons[themeKey] = {}; + + for (const key in currentIcon[themeKey]) { + if (currentIcon[themeKey].hasOwnProperty(key)) { + const file = currentIcon[themeKey][key]; + if (file) { + const formData = new FormData(); + const { name: fileName } = file; + const extension = fileName.split('.').pop(); + const name = `${key}__${themeKey}.${extension}`; + + formData.append('name', name); + formData.append('file', file); + + try { + const { + data: { path }, + } = await upload(formData); + icons[themeKey][key] = path; + this.setState((prevState) => ({ + currentIcon: {}, + })); + } catch (error) { + this.setState({ + loading: false, + }); + message.error('Something went wrong!'); + return; + } + } } } } } - this.setState({ + this.setState((prevState) => ({ + ...prevState, loading: false, - }); + pendingPublishIcons: merge({}, prevState.pendingPublishIcons, { + [iconKey]: icons, + }), + })); + updateIcons(icons); }; handleCancelIcon = () => { - this.setState({ tierIcon: {} }); + this.setState({ currentIcon: {} }); }; - handleChangeFile = (event) => { - if (event.target.files) { + handleChangeFile = ({ target: { name, files } }) => { + const [theme, iconKey] = name.split(','); + + if (files) { this.setState( - { - tierIcon: { - ...this.state.tierIcon, - [event.target.name]: event.target.files[0], + (prevState) => ({ + ...prevState, + currentIcon: { + ...prevState.currentIcon, + [theme]: { + ...prevState.currentIcon[theme], + [iconKey]: files[0], + }, }, - }, + }), () => { Modal.confirm({ content: 'Do you want to save this icon?', okText: 'Save', cancelText: 'Cancel', - onOk: this.handleSaveIcon, + onOk: () => this.handleSaveIcon(iconKey), onCancel: this.handleCancelIcon, }); } @@ -289,9 +308,21 @@ class NewTierForm extends Component { }); }; - saveForm = () => { - this.props.handleNext(this.state.tierData); - this.props.onTypeChange('preview'); + saveForm = (id) => { + const { + pendingPublishIcons: { [id]: published = {} }, + } = this.state; + const iconsOverwrites = JSON.parse(localStorage.getItem('icons') || '{}'); + + const icons = merge({}, iconsOverwrites, published); + const configs = { icons }; + + publish(configs).then(() => { + localStorage.setItem('icons', JSON.stringify(icons)); + this.setState({ pendingPublishIcons: {} }); + this.props.handleNext(this.state.tierData); + this.props.onTypeChange('preview'); + }); }; onEditorStateChange = (edState) => { @@ -305,9 +336,30 @@ class NewTierForm extends Component { }); }; + renderImageUpload = (id, theme, index, showLable = true) => { + const { allIcons } = this.props; + return ( +
+
+ +
+ +
+ ); + }; + render() { const { tierData, editorState } = this.state; - const { icons = {}, isNew = false } = this.props; + const { isNew = false, themeOptions } = this.props; return (

Account tier details

@@ -322,23 +374,45 @@ class NewTierForm extends Component {
-
-
- -
- -
+ + +
+ {themeOptions + .filter(({ value: theme }) => theme === 'dark') + .map(({ value: theme }, index) => + this.renderImageUpload( + `LEVEL_ACCOUNT_ICON_${tierData.id}`, + theme, + index + ) + )} +
+
+ + Theme Specific Graphics + + } + key="2" + > +
+ {themeOptions + .filter(({ value: theme }) => theme !== 'dark') + .map(({ value: theme }, index) => + this.renderImageUpload( + `LEVEL_ACCOUNT_ICON_${tierData.id}`, + theme, + index + ) + )} +
+
+
@@ -425,7 +499,7 @@ class NewTierForm extends Component { diff --git a/web/src/containers/Admin/Tiers/Tiers.js b/web/src/containers/Admin/Tiers/Tiers.js index 741d6cc9b2..fc88911b67 100644 --- a/web/src/containers/Admin/Tiers/Tiers.js +++ b/web/src/containers/Admin/Tiers/Tiers.js @@ -11,7 +11,7 @@ const createMarkup = (row) => { }; const TiersContainer = ({ - icons = {}, + allIcons = {}, userTiers = {}, handleEdit, handleAdd, @@ -32,7 +32,7 @@ const TiersContainer = ({
{/* {tierData.icon diff --git a/web/src/containers/Admin/UploadIds/actions.js b/web/src/containers/Admin/UploadIds/actions.js index 8f62adf75a..4082cbf175 100644 --- a/web/src/containers/Admin/UploadIds/actions.js +++ b/web/src/containers/Admin/UploadIds/actions.js @@ -1,5 +1,6 @@ import { PLUGIN_URL } from 'config/constants'; -import { requestAuthenticated } from '../../../utils'; +// import { requestAuthenticated } from '../../../utils'; +import axios from 'axios'; export const uploadFiles = (id, values) => { const formData = new FormData(); @@ -7,19 +8,12 @@ export const uploadFiles = (id, values) => { formData.append(key, value); }); - const options = { + return axios({ + headers: { + 'Content-Type': 'multipart/form-data', + }, + data: formData, + url: `${PLUGIN_URL}/plugins/kyc/admin/upload?user_id=${id}`, method: 'POST', - body: formData, - }; - - const headers = { - 'Content-Type': 'multipart/form-data', - }; - - return requestAuthenticated( - `/plugins/kyc/admin/upload?user_id=${id}`, - options, - headers, - PLUGIN_URL - ); + }); }; diff --git a/web/src/containers/Admin/UploadIds/index.js b/web/src/containers/Admin/UploadIds/index.js index 479981a7f2..bebeccbcdf 100644 --- a/web/src/containers/Admin/UploadIds/index.js +++ b/web/src/containers/Admin/UploadIds/index.js @@ -43,9 +43,13 @@ const FORM_FIELDS = { class UploadIds extends Component { onSubmit = (refreshData) => (values) => { return uploadFiles(this.props.user_id, values) - .then(({ success, data: { data, user } }) => { - refreshData(data, 'files'); - refreshData(user); + .then(({ data }) => { + if (data.data) { + refreshData(data.data, 'files'); + } + if (data.user) { + refreshData(data.user); + } this.props.closeUpload(); AntdMessage.success('Files upload successfully', 5); }) diff --git a/web/src/containers/Admin/User/AboutData.js b/web/src/containers/Admin/User/AboutData.js index 42dc439838..3a1ac3604d 100644 --- a/web/src/containers/Admin/User/AboutData.js +++ b/web/src/containers/Admin/User/AboutData.js @@ -154,6 +154,7 @@ const AboutData = ({ disableOTP, flagUser, freezeAccount, + verifyEmail, onChangeSuccess, icons: ICONS, }) => { @@ -270,6 +271,37 @@ const AboutData = ({
+
+ {userData.email_verified ? ( + +
+
Email verification
+
Verified
+
+
+ +
+
+ ) : ( + +
+
Email verification
+
+ Mark as verified +
+
+
+ +
+
+ )} +
{userData.otp_enabled ? ( diff --git a/web/src/containers/Admin/User/UserContent.js b/web/src/containers/Admin/User/UserContent.js index 4b03cbc50c..c3dff11274 100644 --- a/web/src/containers/Admin/User/UserContent.js +++ b/web/src/containers/Admin/User/UserContent.js @@ -19,9 +19,10 @@ import { // import UserData from './UserData'; import BankData from './BankData'; import AboutData from './AboutData'; +import VerifyEmailConfirmation from './VerifyEmailConfirmation'; import { isSupport, isKYC } from '../../../utils/token'; import { STATIC_ICONS } from 'config/icons'; -import { deactivateOtp, flagUser, activateUser } from './actions'; +import { deactivateOtp, flagUser, activateUser, verifyUser } from './actions'; // import Flagger from '../Flaguser'; // import Notes from './Notes'; @@ -30,6 +31,10 @@ const TabPane = Tabs.TabPane; const { Item } = Breadcrumb; class UserContent extends Component { + state = { + showVerifyEmailModal: false, + }; + disableOTP = () => { const { userInformation = {}, refreshData } = this.props; const postValues = { @@ -150,6 +155,30 @@ class UserContent extends Component { }); }; + verifyUserEmail = () => { + const { userInformation = {}, refreshData } = this.props; + const postValues = { + user_id: parseInt(userInformation.id, 10), + }; + + verifyUser(postValues) + .then((res) => { + refreshData(postValues); + this.setState({ showVerifyEmailModal: false }); + }) + .catch((err) => { + const _error = + err.data && err.data.message ? err.data.message : err.message; + message.error(_error); + }); + }; + + openVerifyEmailModal = () => { + this.setState({ + showVerifyEmailModal: true, + }); + }; + render() { const { coins, @@ -161,6 +190,9 @@ class UserContent extends Component { refreshAllData, onChangeUserDataSuccess, } = this.props; + + const { showVerifyEmailModal } = this.state; + const { id, // activated, @@ -241,6 +273,7 @@ class UserContent extends Component { disableOTP={this.disableOTP} flagUser={this.flagUser} freezeAccount={this.freezeAccount} + verifyEmail={this.openVerifyEmailModal} />
@@ -319,6 +352,12 @@ class UserContent extends Component { )} + this.setState({ showVerifyEmailModal: false })} + onConfirm={this.verifyUserEmail} + userData={userInformation} + />
); } diff --git a/web/src/containers/Admin/User/VerifyEmailConfirmation.js b/web/src/containers/Admin/User/VerifyEmailConfirmation.js new file mode 100644 index 0000000000..11802a43f5 --- /dev/null +++ b/web/src/containers/Admin/User/VerifyEmailConfirmation.js @@ -0,0 +1,33 @@ +import React from 'react'; +import { Button, Modal } from 'antd'; +import { ReactSVG } from 'react-svg'; +import { STATIC_ICONS } from 'config/icons'; + +const VerifyEmailConfirmation = ({ userData = {}, onConfirm, ...rest }) => ( + +
+
+
+ +
+

+ Email verification +

+
+
+
+ Email: + {userData.email} +
+
Are you sure you want to mark this email as verified?
+
+
+ +
+
+
+); + +export default VerifyEmailConfirmation; diff --git a/web/src/containers/Admin/User/actions.js b/web/src/containers/Admin/User/actions.js index 0d5c336696..b92ddff73c 100644 --- a/web/src/containers/Admin/User/actions.js +++ b/web/src/containers/Admin/User/actions.js @@ -143,6 +143,15 @@ export const activateUser = (values) => { return requestAuthenticated('/admin/user/activate', options); }; +export const verifyUser = (values) => { + const options = { + method: 'POST', + body: JSON.stringify(values), + }; + + return requestAuthenticated('/admin/verify-email', options); +}; + export const performVerificationLevelUpdate = (values) => { const options = { method: 'POST', diff --git a/web/src/containers/Admin/Wallets/index.js b/web/src/containers/Admin/Wallets/index.js index cd97d7f9d0..722c1bb34d 100644 --- a/web/src/containers/Admin/Wallets/index.js +++ b/web/src/containers/Admin/Wallets/index.js @@ -96,19 +96,6 @@ class Wallets extends Component { />

USER WALLETS

-
- {!plugins.enabled.includes('vault') ? ( - - ) : ( - Vault Activated - )} -
{!balance ? ( diff --git a/web/src/containers/App/App.js b/web/src/containers/App/App.js index 0d15cbbc25..94e31a5fcb 100644 --- a/web/src/containers/App/App.js +++ b/web/src/containers/App/App.js @@ -284,7 +284,7 @@ class App extends Component { }; renderDialogContent = ({ type, data }, prices = {}) => { - const { icons: ICONS } = this.props; + const { icons: ICONS, config_level } = this.props; switch (type) { case NOTIFICATIONS.ORDERS: case NOTIFICATIONS.TRADES: @@ -357,6 +357,7 @@ class App extends Component { case FEES_STRUCTURE_AND_LIMITS: return ( console.error(err)); + + this.props.requestTiers(); }; getUserDetails = () => { @@ -728,6 +731,7 @@ const mapDispatchToProps = (dispatch) => ({ setInfo: bindActionCreators(setInfo, dispatch), getMe: bindActionCreators(getMe, dispatch), setPlugins: bindActionCreators(setPlugins, dispatch), + requestTiers: bindActionCreators(requestTiers, dispatch), }); export default connect(mapStateToProps, mapDispatchToProps)(Container); diff --git a/web/src/containers/App/index.js b/web/src/containers/App/index.js index 1b01b0c819..556d09d6b0 100644 --- a/web/src/containers/App/index.js +++ b/web/src/containers/App/index.js @@ -60,6 +60,7 @@ const mapStateToProps = (store) => ({ info: store.app.info, enabledPlugins: store.app.enabledPlugins, features: store.app.features, + config_level: store.app.config_level, }); const mapDispatchToProps = (dispatch) => ({ diff --git a/web/src/containers/Init/Login.js b/web/src/containers/Init/Login.js index 8c0ae37bd7..e6784c34a8 100644 --- a/web/src/containers/Init/Login.js +++ b/web/src/containers/Init/Login.js @@ -82,10 +82,10 @@ const Login = (props) => { label: 'Password', validate: [validateRequired], }, - // otp_code: { - // type: 'number', - // label: '2FA (if active)', - // }, + otp_code: { + type: 'number', + label: '2FA (if active)', + }, captcha: { type: 'captcha', language: getLanguage(), diff --git a/web/src/containers/OperatorControls/components/UploadIcon.js b/web/src/containers/OperatorControls/components/UploadIcon.js index f4a9077378..2d70273d3f 100644 --- a/web/src/containers/OperatorControls/components/UploadIcon.js +++ b/web/src/containers/OperatorControls/components/UploadIcon.js @@ -114,7 +114,7 @@ class UploadIcon extends Component { Upload graphic
-
+
When uploading new content a file size under 1mb is recommended.
@@ -144,7 +144,11 @@ class UploadIcon extends Component { + Theme Specific Graphics + + } key="2" > {themeOptions diff --git a/web/src/containers/Signup/index.js b/web/src/containers/Signup/index.js index 9b72d91bef..756aa66f97 100644 --- a/web/src/containers/Signup/index.js +++ b/web/src/containers/Signup/index.js @@ -52,6 +52,15 @@ class Signup extends Component { } } + componentDidUpdate() { + const { constants = {}, router } = this.props; + const { success } = this.state; + + if (success && !constants.email_verification_required) { + router.push('/login'); + } + } + componentWillUnmount() { if (errorTimeOut) { clearTimeout(errorTimeOut); @@ -154,7 +163,7 @@ class Signup extends Component { } = this.props; const { success, showContactForm, isReferral } = this.state; - if (success) { + if (success && constants.email_verification_required) { return (
{isMobile && } diff --git a/web/src/containers/Summary/MobileAccountTypeList.js b/web/src/containers/Summary/MobileAccountTypeList.js index a99e43ae42..f364c8a99b 100644 --- a/web/src/containers/Summary/MobileAccountTypeList.js +++ b/web/src/containers/Summary/MobileAccountTypeList.js @@ -45,10 +45,9 @@ const MobileAccountTypeList = ({ : 'LEVEL_ACCOUNT_ICON_4' } icon={ - configData.icon || - (ICONS[`LEVEL_ACCOUNT_ICON_${key}`] + ICONS[`LEVEL_ACCOUNT_ICON_${key}`] ? ICONS[`LEVEL_ACCOUNT_ICON_${key}`] - : ICONS['LEVEL_ACCOUNT_ICON_4']) + : ICONS['LEVEL_ACCOUNT_ICON_4'] } wrapperClassName="trader-account-icon" /> diff --git a/web/src/containers/Summary/components/AccountTypesList.js b/web/src/containers/Summary/components/AccountTypesList.js index 3ba35f9c95..075eae4acb 100644 --- a/web/src/containers/Summary/components/AccountTypesList.js +++ b/web/src/containers/Summary/components/AccountTypesList.js @@ -39,10 +39,9 @@ const AccountTypesList = ({ : 'LEVEL_ACCOUNT_ICON_4' } icon={ - configData.icon || - (ICONS[`LEVEL_ACCOUNT_ICON_${key}`] + ICONS[`LEVEL_ACCOUNT_ICON_${key}`] ? ICONS[`LEVEL_ACCOUNT_ICON_${key}`] - : ICONS['LEVEL_ACCOUNT_ICON_4']) + : ICONS['LEVEL_ACCOUNT_ICON_4'] } wrapperClassName="trader-account-icon" /> diff --git a/web/src/containers/Summary/components/FeesAndLimits.js b/web/src/containers/Summary/components/FeesAndLimits.js index 78edca095e..20913ea0b3 100644 --- a/web/src/containers/Summary/components/FeesAndLimits.js +++ b/web/src/containers/Summary/components/FeesAndLimits.js @@ -15,6 +15,7 @@ const FeesAndLimits = ({ pairs, constants = {}, icons: ICONS, + tiers, }) => { const { verification_level, discount = 0 } = data; const LEVEL_OF_ACCOUNT = STRINGS.formatString( @@ -65,7 +66,7 @@ const FeesAndLimits = ({
- +
diff --git a/web/src/containers/Summary/components/FeesBlock.js b/web/src/containers/Summary/components/FeesBlock.js index a8c604c884..3277f2ee9b 100644 --- a/web/src/containers/Summary/components/FeesBlock.js +++ b/web/src/containers/Summary/components/FeesBlock.js @@ -6,10 +6,11 @@ import { formatPercentage } from '../../../utils/currency'; import { DEFAULT_COIN_DATA } from '../../../config/constants'; import { EditWrapper } from 'components'; -const getMakerRow = (pairs, coins, pair, level, index, discount) => { - const { pair_base, pair_2, maker_fees, taker_fees } = pairs[pair]; - const makersFee = maker_fees ? maker_fees[level] : 0; - const takersFee = taker_fees ? taker_fees[level] : 0; +const getMakerRow = (pairs, coins, pair, level, index, discount, tiers) => { + const { pair_base, pair_2 } = pairs[pair]; + const { fees: { maker, taker } = {} } = tiers[level] || {}; + const makersFee = maker ? maker[pair] : 0; + const takersFee = taker ? taker[pair] : 0; const pairBase = coins[pair_base] || DEFAULT_COIN_DATA; const pairTwo = coins[pair_2] || DEFAULT_COIN_DATA; const makersData = discount @@ -57,16 +58,18 @@ const getMakerRow = (pairs, coins, pair, level, index, discount) => { // ); // }; -const getRows = (pairs, level, coins, discount) => { +const getRows = (pairs, level, coins, discount, tiers) => { const rowData = []; Object.keys(pairs).map((pair, index) => { - rowData.push(getMakerRow(pairs, coins, pair, level, index, discount)); + rowData.push( + getMakerRow(pairs, coins, pair, level, index, discount, tiers) + ); return ''; }); return rowData; }; -const FeesBlock = ({ pairs, coins, level, discount }) => { +const FeesBlock = ({ pairs, coins, level, discount, tiers }) => { return (
@@ -97,7 +100,7 @@ const FeesBlock = ({ pairs, coins, level, discount }) => { - {getRows(pairs, level, coins, discount)} + {getRows(pairs, level, coins, discount, tiers)}
diff --git a/web/src/containers/Summary/components/LimitsBlock.js b/web/src/containers/Summary/components/LimitsBlock.js index 2a9c1b65f7..006691090f 100644 --- a/web/src/containers/Summary/components/LimitsBlock.js +++ b/web/src/containers/Summary/components/LimitsBlock.js @@ -26,14 +26,11 @@ const getLimitValue = (limit, increment_unit) => { } }; -const getDepositRow = (currency, index, coins, level) => { - const { - symbol = '', - fullname, - deposit_limits = {}, - withdrawal_limits = {}, - increment_unit, - } = coins[currency] || DEFAULT_COIN_DATA; +const getDepositRow = (currency, index, coins, level, tier) => { + const { symbol = '', fullname, increment_unit } = + coins[currency] || DEFAULT_COIN_DATA; + + const { deposit_limit, withdrawal_limit } = tier[level] || {}; // const format = currency === BASE_CURRENCY ? formatBaseAmount : formatBtcAmount; return ( @@ -44,10 +41,10 @@ const getDepositRow = (currency, index, coins, level) => {
- {getLimitValue(deposit_limits[level], increment_unit)} + {getLimitValue(deposit_limit, increment_unit)} - {getLimitValue(withdrawal_limits[level], increment_unit)} + {getLimitValue(withdrawal_limit, increment_unit)} ); @@ -64,15 +61,15 @@ const getDepositRow = (currency, index, coins, level) => { // ); // }; -const getRows = (coins, level) => { +const getRows = (coins, level, tiers) => { const rowData = []; Object.keys(coins).forEach((currency, index) => { - rowData.push(getDepositRow(currency, index, coins, level)); + rowData.push(getDepositRow(currency, index, coins, level, tiers)); }); return rowData; }; -const LimitsBlock = ({ level, coins }) => { +const LimitsBlock = ({ level, coins, tiers }) => { return (
@@ -103,7 +100,7 @@ const LimitsBlock = ({ level, coins }) => { - {getRows(coins, level)} + {getRows(coins, level, tiers)}
diff --git a/web/src/containers/Summary/components/TraderAccounts.js b/web/src/containers/Summary/components/TraderAccounts.js index 156bac5c8e..6503f620af 100644 --- a/web/src/containers/Summary/components/TraderAccounts.js +++ b/web/src/containers/Summary/components/TraderAccounts.js @@ -33,11 +33,9 @@ const TraderAccounts = ({ (STRINGS[`SUMMARY.LEVEL_${verification_level}_TXT`] ? STRINGS[`SUMMARY.LEVEL_${verification_level}_TXT`] : STRINGS['SUMMARY.LEVEL_TXT_DEFAULT']); - let icon = - accountData.icon || - (ICONS[`LEVEL_ACCOUNT_ICON_${verification_level}`] - ? ICONS[`LEVEL_ACCOUNT_ICON_${verification_level}`] - : ICONS['LEVEL_ACCOUNT_ICON_4']); + let icon = ICONS[`LEVEL_ACCOUNT_ICON_${verification_level}`] + ? ICONS[`LEVEL_ACCOUNT_ICON_${verification_level}`] + : ICONS['LEVEL_ACCOUNT_ICON_4']; // if (!isAccountDetails) { // description = user.is_hap // ? STRINGS["SUMMARY.HAP_ACCOUNT_TXT"] diff --git a/web/src/containers/Summary/index.js b/web/src/containers/Summary/index.js index a976c9078c..e950d70b58 100644 --- a/web/src/containers/Summary/index.js +++ b/web/src/containers/Summary/index.js @@ -21,7 +21,6 @@ import { logoutconfirm, setNotification, NOTIFICATIONS, - requestTiers, } from '../../actions/appActions'; import { BASE_CURRENCY, @@ -47,7 +46,6 @@ class Summary extends Component { if (user.id) { this.setCurrentTradeAccount(user); this.props.getUserReferralCount(); - this.props.requestTiers(); } if (tradeVolumes.fetched) { let lastMonthVolume = getLastMonthVolume( @@ -324,7 +322,6 @@ const mapDispatchToProps = (dispatch) => ({ ), setNotification: bindActionCreators(setNotification, dispatch), getUserReferralCount: bindActionCreators(getUserReferralCount, dispatch), - requestTiers: bindActionCreators(requestTiers, dispatch), }); export default connect(mapStateToProps, mapDispatchToProps)(Summary); diff --git a/web/src/containers/Trade/_TradeContainer.scss b/web/src/containers/Trade/_TradeContainer.scss index 5981435674..7b181794d5 100644 --- a/web/src/containers/Trade/_TradeContainer.scss +++ b/web/src/containers/Trade/_TradeContainer.scss @@ -8,7 +8,7 @@ .trade-main_content { flex: 6; min-height: 25vh; - max-height: 67vh; + max-height: 78vh; } .trade-tabs_content { diff --git a/web/src/containers/Trade/components/OrderEntry.js b/web/src/containers/Trade/components/OrderEntry.js index 1fa1b0b1eb..d9a33f6498 100644 --- a/web/src/containers/Trade/components/OrderEntry.js +++ b/web/src/containers/Trade/components/OrderEntry.js @@ -412,7 +412,15 @@ class OrderEntry extends Component { className="pointer text-uppercase blue-link" onClick={() => this.setMax()} > - {balance[`${side === 'buy' ? pair_2 : pair_base}_available`]}{' '} + {side === 'buy' + ? formatToCurrency( + balance[`${pair_2}_available`], + increment_price + ) + : formatToCurrency( + balance[`${pair_base}_available`], + increment_size + )}{' '} {side === 'buy' ? pair_2.toUpperCase() : pair_base.toUpperCase()} diff --git a/web/src/containers/Trade/components/OrderEntryForm.js b/web/src/containers/Trade/components/OrderEntryForm.js index 25bb4422d8..4e8c756c81 100644 --- a/web/src/containers/Trade/components/OrderEntryForm.js +++ b/web/src/containers/Trade/components/OrderEntryForm.js @@ -1,4 +1,5 @@ import React from 'react'; +import { Collapse } from 'antd'; import classnames from 'classnames'; import { connect } from 'react-redux'; import { reduxForm, formValueSelector } from 'redux-form'; @@ -51,6 +52,8 @@ const Form = ({ }) => { const fields = getFields(formValues, type, orderType); const errorText = error || outsideFormError; + const hasPostOnly = + Object.entries(fields).filter(([key]) => key === 'postOnly').length !== 0; return (
- {Object.entries(fields).map(renderFields)} + {Object.entries(fields) + .filter(([key]) => key !== 'postOnly') + .map(renderFields)} + {hasPostOnly && ( + + + {STRINGS['ORDER_ENTRY_ADVANCED']} + + } + key="1" + > + {Object.entries(fields) + .filter(([key]) => key === 'postOnly') + .map(renderFields)} + + + )} {errorText && (
{errorText} @@ -80,7 +102,7 @@ const Form = ({ currencyName ).join(' ')} disabled={submitting || !valid || !!errorText || !isLoggedIn()} - className={classnames('trade_order_entry-form-action')} + className={classnames('trade_order_entry-form-action', 'mb-1')} />
diff --git a/web/src/containers/Trade/components/_OrderEntry.scss b/web/src/containers/Trade/components/_OrderEntry.scss index 4e4a876047..4ad1897b50 100644 --- a/web/src/containers/Trade/components/_OrderEntry.scss +++ b/web/src/containers/Trade/components/_OrderEntry.scss @@ -105,7 +105,16 @@ } .trade_order_entry-form_fields-wrapper { - margin-bottom: 0.5rem !important; + margin-bottom: 0.2rem !important; + .ant-collapse-header { + text-align: right; + color: $colors-black; + padding: 0; + font-size: $font-size-mobile-txt; + } + .ant-collapse-content-box { + padding: 0; + } } } diff --git a/web/src/containers/TransactionsHistory/_TransactionsHistory.scss b/web/src/containers/TransactionsHistory/_TransactionsHistory.scss index 1101bed94b..7a41c93394 100644 --- a/web/src/containers/TransactionsHistory/_TransactionsHistory.scss +++ b/web/src/containers/TransactionsHistory/_TransactionsHistory.scss @@ -57,6 +57,31 @@ background-color: $history-color-buy; color: $buy-bids-text; } + + &.fullfilled .cell-wrapper { + border: 1px solid $colors-main-border; + margin: 0; + padding: 1px; + min-width: 5rem; + max-width: 7rem; + + > .cell_value-wrapper { + padding: 0 0.15rem; + position: relative; + width: 100%; + color: $trade-fill-indicator-text; + z-index: 2; + + .cell_value-bar { + position: absolute; + left: 0; + top: 0; + bottom: 0; + background-color: $trade-fill-indicator; + z-index: -1; + } + } + } } .amount_text { diff --git a/web/src/containers/TransactionsHistory/components/DepositAndWithdrawlFilters.js b/web/src/containers/TransactionsHistory/components/DepositAndWithdrawlFilters.js index 965f161244..ad4bb5507f 100644 --- a/web/src/containers/TransactionsHistory/components/DepositAndWithdrawlFilters.js +++ b/web/src/containers/TransactionsHistory/components/DepositAndWithdrawlFilters.js @@ -1,4 +1,4 @@ -import React, { useEffect } from 'react'; +import React from 'react'; import { Select, Form, Row, Button } from 'antd'; import { CaretDownOutlined } from '@ant-design/icons'; import STRINGS from 'config/localizedStrings'; @@ -23,12 +23,6 @@ const STATUS_OPTIONS = { const Filters = ({ coins = {}, onSearch, formName }) => { const [form] = Form.useForm(); - useEffect(() => { - form.submit(); - // TODO: Fix react-hooks/exhaustive-deps - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - const onFinish = (values) => { onSearch(values); }; diff --git a/web/src/containers/TransactionsHistory/components/TradeAndOrderFilters.js b/web/src/containers/TransactionsHistory/components/TradeAndOrderFilters.js index 1b9133913a..843c30c1ba 100644 --- a/web/src/containers/TransactionsHistory/components/TradeAndOrderFilters.js +++ b/web/src/containers/TransactionsHistory/components/TradeAndOrderFilters.js @@ -1,4 +1,4 @@ -import React, { useEffect } from 'react'; +import React from 'react'; import { Select, Form, Row, DatePicker, Button, Radio } from 'antd'; import { CaretDownOutlined } from '@ant-design/icons'; import { dateFilters } from '../filterUtils'; @@ -9,12 +9,6 @@ const { RangePicker } = DatePicker; const Filters = ({ pairs, onSearch, formName }) => { const [form] = Form.useForm(); - useEffect(() => { - form.submit(); - // TODO: Fix react-hooks/exhaustive-deps - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - const onFinish = (values) => { onSearch(values); }; diff --git a/web/src/containers/TransactionsHistory/index.js b/web/src/containers/TransactionsHistory/index.js index 93a20c3dd0..c1ddffe8e7 100644 --- a/web/src/containers/TransactionsHistory/index.js +++ b/web/src/containers/TransactionsHistory/index.js @@ -25,6 +25,7 @@ import { } from '../../components'; import { FLEX_CENTER_CLASSES, BASE_CURRENCY } from '../../config/constants'; import { + generateOrderHistoryHeaders, generateTradeHeaders, generateTradeHeadersMobile, generateDepositsHeaders, @@ -67,6 +68,8 @@ class TransactionsHistory extends Component { this.props.location.query.tab ) { this.setActiveTab(parseInt(this.props.location.query.tab, 10)); + } else { + this.setActiveTab(); } } @@ -160,8 +163,8 @@ class TransactionsHistory extends Component { this.setState({ headers: { orders: isMobile - ? generateTradeHeadersMobile(symbol, pairs, coins, discount) - : generateTradeHeaders(symbol, pairs, coins, discount, prices), + ? generateOrderHistoryHeaders(symbol, pairs, coins, discount) + : generateOrderHistoryHeaders(symbol, pairs, coins, discount, prices), trades: isMobile ? generateTradeHeadersMobile(symbol, pairs, coins, discount) : generateTradeHeaders(symbol, pairs, coins, discount, prices), @@ -324,6 +327,7 @@ class TransactionsHistory extends Component { withdrawals, symbol, downloadUserTrades, + downloadUserOrders, downloadUserWithdrawal, downloadUserDeposit, } = this.props; @@ -339,13 +343,13 @@ class TransactionsHistory extends Component { case 0: props.stringId = 'ORDER_HISTORY'; props.title = `${STRINGS['ORDER_HISTORY']}`; - props.headers = headers.trades; + props.headers = headers.orders; props.data = orders; props.filename = `order-history-${moment().unix()}`; props.withIcon = false; props.handleNext = this.handleNext; props.jumpToPage = jumpToPage; - props.handleDownload = downloadUserTrades; + props.handleDownload = downloadUserOrders; props.filters = filters.orders; break; case 1: @@ -558,6 +562,7 @@ const mapDispatchToProps = (dispatch) => ({ downloadUserTrades: () => dispatch(downloadUserTrades('trade')), downloadUserDeposit: () => dispatch(downloadUserTrades('deposit')), downloadUserWithdrawal: () => dispatch(downloadUserTrades('withdrawal')), + downloadUserOrders: () => dispatch(downloadUserTrades('orders')), setDeposit: (params) => dispatch(setDeposit(params)), }); diff --git a/web/src/containers/TransactionsHistory/utils.js b/web/src/containers/TransactionsHistory/utils.js index e48c559a58..11cb56a489 100644 --- a/web/src/containers/TransactionsHistory/utils.js +++ b/web/src/containers/TransactionsHistory/utils.js @@ -15,14 +15,14 @@ import { DEFAULT_COIN_DATA, } from '../../config/constants'; import { getFormatTimestamp, isBlockchainTx } from '../../utils/utils'; -import { formatToCurrency } from '../../utils/currency'; +import { formatToCurrency, formatBaseAmount } from 'utils/currency'; notification.config({ placement: 'topLeft', duration: 3, }); -const calculateFeeAmount = ( +/*const calculateFeeAmount = ( fee = 0, quick = false, price = 1, @@ -40,7 +40,7 @@ const calculateFeeAmount = ( feeAmount = mathjs.chain(amount).multiply(fee).divide(100).done(); } return feeAmount; -}; +};*/ const calculateAmount = (isQuick = false, price, size) => { if (isQuick) { @@ -58,9 +58,9 @@ const calculatePrice = (isQuick = false, price, size) => { return price; }; -export const generateTradeHeaders = ( +export const generateOrderHistoryHeaders = ( symbol, - pairs, + pairs = {}, coins, discount, prices = {} @@ -224,17 +224,202 @@ export const generateTradeHeaders = ( }, }, { - stringId: 'AMOUNT_IN', - label: `${STRINGS['AMOUNT_IN']} ${BASE_CURRENCY.toUpperCase()}`, - key: 'amount-in', + label: '', + key: 'status', + renderCell: ({ size = 0, filled = 0 }, key, index) => { + const fullfilled = formatBaseAmount( + mathjs.chain(filled).divide(size).multiply(100).done() + ); + return ( + +
+
+ {STRINGS.formatString(STRINGS['FULLFILLED'], fullfilled)} + +
+
+ + ); + }, + }, + { + label: STRINGS['STATUS'], + key: 'status', + exportToCsv: ({ status }) => status, + renderCell: ({ status }, key, index) => { + return ( + + {status} + + ); + }, + }, + { + stringId: 'FEE,NO_FEE', + label: STRINGS['FEE'], + key: 'fee', + exportToCsv: ({ fee = 0, fee_coin = '' }) => `${fee} ${fee_coin}`, + renderCell: ({ fee = 0, fee_coin = '' }, key, index) => ( + + {STRINGS.formatString( + CURRENCY_PRICE_FORMAT, + formatToCurrency(fee, 0, true), + fee_coin.toUpperCase() + )} + + ), + }, + { + stringId: 'TIME', + label: STRINGS['TIME'], + key: 'updated_at', + className: isMobile ? 'text-center' : '', + exportToCsv: ({ updated_at = '' }) => updated_at, + renderCell: ({ updated_at = '' }, key, index) => { + return ( + + {getFormatTimestamp(updated_at)} + + ); + }, + }, + ]; +}; + +export const generateTradeHeaders = ( + symbol, + pairs, + coins, + discount, + prices = {} +) => { + return [ + { + stringId: 'PAIR', + label: STRINGS['PAIR'], + key: 'pair', + exportToCsv: ({ symbol }) => symbol.toUpperCase(), + renderCell: ({ symbol }, key, index) => { + return ( + + {symbol} + + ); + }, + }, + { + stringId: 'TYPE', + label: STRINGS['TYPE'], + key: 'side', + exportToCsv: ({ side = '' }) => side, + renderCell: ({ side = '' }, key, index) => { + return ( + +
+ {STRINGS[`SIDES_VALUES.${side}`]} +
+ + ); + }, + }, + { + stringId: 'SIZE', + label: STRINGS['SIZE'], + key: 'size', + exportToCsv: ({ size = 0, ...data }) => { + if (pairs[data.symbol]) { + const { pair_base, increment_size } = pairs[data.symbol]; + const { min, ...rest } = + coins[pair_base || BASE_CURRENCY] || DEFAULT_COIN_DATA; + const shortName = rest.symbol.toUpperCase(); + return STRINGS.formatString( + CURRENCY_PRICE_FORMAT, + formatToCurrency(size, increment_size), + shortName + ).join(''); + } else { + return size; + } + }, + renderCell: ({ size = 0, ...data }, key, index) => { + if (pairs[data.symbol]) { + const { pair_base, increment_size } = pairs[data.symbol]; + const { min, ...rest } = + coins[pair_base || BASE_CURRENCY] || DEFAULT_COIN_DATA; + const shortName = rest.symbol.toUpperCase(); + return ( + + {STRINGS.formatString( + CURRENCY_PRICE_FORMAT, + formatToCurrency(size, increment_size), + shortName + )} + + ); + } else { + return {size}; + } + }, + }, + { + stringId: 'PRICE', + label: STRINGS['PRICE'], + key: 'price', exportToCsv: ({ price = 0, size = 0, quick, symbol }) => { if (pairs[symbol]) { - const { increment_price, pair_base } = pairs[symbol]; - const { min, ...rest } = coins[BASE_CURRENCY] || DEFAULT_COIN_DATA; + const { pair_2, increment_price } = pairs[symbol]; + const { min, ...rest } = + coins[pair_2 || BASE_CURRENCY] || DEFAULT_COIN_DATA; return STRINGS.formatString( CURRENCY_PRICE_FORMAT, formatToCurrency( - calculateAmount(quick, prices[pair_base] || 0, size), + calculatePrice(quick, price, size), + increment_price + ), + rest.symbol.toUpperCase() + ).join(''); + } else { + return calculatePrice(quick, price, size); + } + }, + renderCell: ({ price = 0, size = 0, quick, symbol }, key, index) => { + if (pairs[symbol]) { + const { pair_2, increment_price } = pairs[symbol]; + const { min, ...rest } = + coins[pair_2 || BASE_CURRENCY] || DEFAULT_COIN_DATA; + return ( + + {STRINGS.formatString( + CURRENCY_PRICE_FORMAT, + formatToCurrency( + calculatePrice(quick, price, size), + increment_price + ), + rest.symbol.toUpperCase() + )} + + ); + } else { + return {calculatePrice(quick, price, size)}; + } + }, + }, + { + stringId: 'AMOUNT', + label: STRINGS['AMOUNT'], + key: 'amount', + exportToCsv: ({ price = 0, size = 0, quick, symbol }) => { + if (pairs[symbol]) { + const { pair_2, increment_price } = pairs[symbol]; + const { min, ...rest } = + coins[pair_2 || BASE_CURRENCY] || DEFAULT_COIN_DATA; + return STRINGS.formatString( + CURRENCY_PRICE_FORMAT, + formatToCurrency( + calculateAmount(quick, price, size), increment_price ), rest.symbol.toUpperCase() @@ -245,14 +430,15 @@ export const generateTradeHeaders = ( }, renderCell: ({ price = 0, size = 0, quick, symbol }, key, index) => { if (pairs[symbol]) { - const { increment_price, pair_base } = pairs[symbol]; - const { min, ...rest } = coins[BASE_CURRENCY] || DEFAULT_COIN_DATA; + const { pair_2, increment_price } = pairs[symbol]; + const { min, ...rest } = + coins[pair_2 || BASE_CURRENCY] || DEFAULT_COIN_DATA; return ( {STRINGS.formatString( CURRENCY_PRICE_FORMAT, formatToCurrency( - calculateAmount(quick, prices[pair_base] || 0, size), + calculateAmount(quick, price, size), increment_price ), rest.symbol.toUpperCase() @@ -268,59 +454,65 @@ export const generateTradeHeaders = ( } }, }, - { - stringId: 'FEE,NO_FEE', - label: STRINGS['FEE'], - key: 'fee', - exportToCsv: ({ fee = 0, price = 0, size = 0, quick, symbol, side }) => { - let feeData = discount ? fee - (fee * discount) / 100 : fee; - if (!feeData) { - return calculateFeeAmount(feeData); - } + /* { + stringId: 'AMOUNT_IN', + label: `${STRINGS['AMOUNT_IN']} ${BASE_CURRENCY.toUpperCase()}`, + key: 'amount-in', + exportToCsv: ({ price = 0, size = 0, quick, symbol }) => { if (pairs[symbol]) { - const { pair_base, pair_2 } = pairs[symbol]; - const pair = side === 'buy' ? pair_base : pair_2; - const { min, ...rest } = - coins[pair || BASE_CURRENCY] || DEFAULT_COIN_DATA; + const { increment_price, pair_base } = pairs[symbol]; + const { min, ...rest } = coins[BASE_CURRENCY] || DEFAULT_COIN_DATA; return STRINGS.formatString( CURRENCY_PRICE_FORMAT, formatToCurrency( - calculateFeeAmount(feeData, quick, price, size, side), - min + calculateAmount(quick, prices[pair_base] || 0, size), + increment_price ), rest.symbol.toUpperCase() ).join(''); } else { - calculateFeeAmount(feeData, quick, price, size, side); + return calculateAmount(quick, price, size); } }, - renderCell: ({ fee, price, size, quick, symbol, side }, key, index) => { - let feeData = discount ? fee - (fee * discount) / 100 : fee; - if (!feeData) { - return {calculateFeeAmount(feeData)}; - } + renderCell: ({ price = 0, size = 0, quick, symbol }, key, index) => { if (pairs[symbol]) { - const { pair_base, pair_2 } = pairs[symbol]; - const pair = side === 'buy' ? pair_base : pair_2; - const { min, ...rest } = - coins[pair || BASE_CURRENCY] || DEFAULT_COIN_DATA; + const { increment_price, pair_base } = pairs[symbol]; + const { min, ...rest } = coins[BASE_CURRENCY] || DEFAULT_COIN_DATA; return ( {STRINGS.formatString( CURRENCY_PRICE_FORMAT, formatToCurrency( - calculateFeeAmount(feeData, quick, price, size, side), - min, - true + calculateAmount(quick, prices[pair_base] || 0, size), + increment_price ), rest.symbol.toUpperCase() )} ); } else { - calculateFeeAmount(feeData, quick, price, size, side); + return ( + + {formatToCurrency(calculateAmount(quick, price, size), 0.0001)} + + ); } }, + },*/ + { + stringId: 'FEE,NO_FEE', + label: STRINGS['FEE'], + key: 'fee', + exportToCsv: ({ fee = 0, fee_coin = '' }) => `${fee} ${fee_coin}`, + renderCell: ({ fee = 0, fee_coin = '' }, key, index) => ( + + {STRINGS.formatString( + CURRENCY_PRICE_FORMAT, + formatToCurrency(fee, 0, true), + fee_coin.toUpperCase() + )} + + ), }, { stringId: 'TIME', @@ -438,31 +630,16 @@ export const generateWithdrawalsHeaders = ( stringId: 'FEE,NO_FEE', label: STRINGS['FEE'], key: 'fee', - exportToCsv: ({ fee = 0 }) => fee, - renderCell: ({ fee, price, size, currency }, key, index) => { - const data = coins[currency] || DEFAULT_COIN_DATA; - if (fee === 0) { - return {calculateFeeAmount(fee)}; - } - return ( - // STRINGS[`${currency.toUpperCase()}_PRICE_FORMAT`] - // ? - - {STRINGS.formatString( - CURRENCY_PRICE_FORMAT, - fee, - data.symbol.toUpperCase() - )} - /*: {fee}*/ /*: {fee}*/ /*: {fee}*/ /*: {fee}*/ /*: {fee}*/ /*: {fee}*/ /*: {fee}*/ /*: {fee}*/ /*: {fee}*/ /*: {fee}*/ /*: {fee}*/ /*: {fee}*/ /*: {fee}*/ /*: {fee}*/ /*: {fee}*/ /*: {fee}*/ /*: {fee}*/ /*: {fee}*/ /*: {fee}*/ /*: {fee}*/ /*: {fee}*/ /*: {fee}*/ /*: {fee}*/ /*: {fee}*/ /*: {fee}*/ /*: {fee}*/ /*: {fee}*/ /*: {fee}*/ /*: {fee}*/ /*: {fee}*/ /*: {fee}*/ - // : {fee} - /*: {fee}*/ - /*: {fee}*/ - /*: {fee}*/ - /*: {fee}*/ - /*: {fee}*/ - /*: {fee}*/ - /*: {fee}*/); - }, + exportToCsv: ({ fee = 0, fee_coin = '' }) => `${fee} ${fee_coin}`, + renderCell: ({ fee = 0, fee_coin = '' }, key, index) => ( + + {STRINGS.formatString( + CURRENCY_PRICE_FORMAT, + formatToCurrency(fee, 0, true), + fee_coin.toUpperCase() + )} + + ), }, { stringId: 'TIME', diff --git a/web/src/containers/Verification/VerificationSentModal.js b/web/src/containers/Verification/VerificationSentModal.js new file mode 100644 index 0000000000..6ca0352c83 --- /dev/null +++ b/web/src/containers/Verification/VerificationSentModal.js @@ -0,0 +1,47 @@ +import React, { Component } from 'react'; +import STRINGS from 'config/localizedStrings'; +import { Dialog, IconTitle, Button } from 'components'; +import { EditWrapper } from 'components'; +import withConfig from 'components/ConfigProvider/withConfig'; + +class VerificationSentModal extends Component { + render() { + const { icons: ICONS, onCloseDialog, ...rest } = this.props; + + return ( + +
+ +
+
+ +
{STRINGS['USER_VERIFICATION.VERIFICATION_SENT_INFO']}
+
+
+
+
+
+
+
+ ); + } +} + +export default withConfig(VerificationSentModal); diff --git a/web/src/containers/Verification/index.js b/web/src/containers/Verification/index.js index 6111ef1e94..49fbd93fc7 100644 --- a/web/src/containers/Verification/index.js +++ b/web/src/containers/Verification/index.js @@ -13,10 +13,11 @@ import { Notification, // MobileBarTabs, PanelInformationRow, + Button, } from '../../components'; import withConfig from 'components/ConfigProvider/withConfig'; import STRINGS from '../../config/localizedStrings'; -import { logout } from '../../actions/authAction'; +import { logout, requestVerificationEmail } from '../../actions/authAction'; import BankVerification from './BankVerification'; import { isBrowser, isMobile } from 'react-device-detect'; @@ -24,6 +25,7 @@ import VerificationHome from './VerificationHome'; import IdentityVerification from './IdentityVerification'; import MobileVerification from './MobileVerification'; import DocumentsVerification from './DocumentsVerification'; +import VerificationSentModal from './VerificationSentModal'; import { mobileInitialValues, identityInitialValues, @@ -41,6 +43,7 @@ import BankVerificationHome from './BankVerificationHome'; import IdentityVerificationHome from './IdentityVerificationHome'; import MobileVerificationHome from './MobileVerificationHome'; import DocumentsVerificationHome from './DocumentsVerificationHome'; +import { EditWrapper } from 'components'; // import MobileTabs from './MobileTabs'; // const CONTENT_CLASS = @@ -54,6 +57,7 @@ class Verification extends Component { dialogIsOpen: false, user: {}, activePage: 'email', + showVerificationSentModal: false, }; componentDidMount() { @@ -145,6 +149,27 @@ class Verification extends Component { return { activeTab, currentTabs }; }; + sendVerificationEmail = () => { + const { user: { email } = {} } = this.props; + return requestVerificationEmail({ email }) + .then(() => { + this.setState({ showVerificationSentModal: true }); + }) + .catch((error) => { + if (error.response && error.response.status === 404) { + this.setState({ showVerificationSentModal: true }); + } else { + const errors = {}; + if (error.response) { + errors._error = error.response.data.message; + } else { + errors._error = error.message; + } + console.error(errors); + } + }); + }; + updateTabs = ( user = {}, activeLanguage = this.props.activeLanguage, @@ -155,7 +180,14 @@ class Verification extends Component { return; } const { icons: ICONS } = this.props; - const { email, bank_account, address, id_data, phone_number } = user; + const { + email, + bank_account, + address, + id_data, + phone_number, + email_verified, + } = user; let bank_status = 0; if (bank_account.length) { if (bank_account.filter((data) => data.status === 3).length) { @@ -186,7 +218,7 @@ class Verification extends Component { ) : ( ), content: activeTab === 0 && ( @@ -205,6 +237,16 @@ class Verification extends Component { className={'title-font'} disable /> + {!email_verified && ( +
+ +
+ )}
), }, @@ -482,7 +524,13 @@ class Verification extends Component { render() { const { activeLanguage, activeTheme, icons: ICONS } = this.props; - const { activeTab, tabs, dialogIsOpen, dialogType } = this.state; + const { + activeTab, + tabs, + dialogIsOpen, + dialogType, + showVerificationSentModal, + } = this.state; if (activeTab === -1 && tabs.length > 0) { return (
@@ -539,6 +587,12 @@ class Verification extends Component { > {this.renderDialogContent(dialogType)} + { + this.setState({ showVerificationSentModal: false }); + }} + />
); } diff --git a/web/src/index.css b/web/src/index.css index 38ead05c49..22b1d4f94d 100644 --- a/web/src/index.css +++ b/web/src/index.css @@ -1441,6 +1441,26 @@ table th { background-color: var(--trading_buying-related-elements); color: var(--calculated_trading_buying-related-text); } +.cell_box-type.fullfilled .cell-wrapper { + border: 1px solid var(--calculated_important-border); + margin: 0; + padding: 1px; + min-width: 5rem; + max-width: 7rem; } + .cell_box-type.fullfilled .cell-wrapper > .cell_value-wrapper { + padding: 0 0.15rem; + position: relative; + width: 100%; + color: var(--labels_secondary-inactive-label-text-graphics); + z-index: 2; } + .cell_box-type.fullfilled .cell-wrapper > .cell_value-wrapper .cell_value-bar { + position: absolute; + left: 0; + top: 0; + bottom: 0; + background-color: var(--calculated_specials_highlight-box); + z-index: -1; } + .amount_text .positive { color: var(--specials_checks-okay-done); } @@ -1512,7 +1532,7 @@ table th { .trade-container .trade-col_main_wrapper .trade-main_content { flex: 6; min-height: 25vh; - max-height: 67vh; } + max-height: 78vh; } .trade-container .trade-col_main_wrapper .trade-tabs_content { flex: 3; height: 30vh; } @@ -1787,7 +1807,14 @@ table th { .trade_order_entry-wrapper.order_side-selector-sell .trade_order_entry-form_inputs-wrapper .form-error { margin: 0.5rem 0; } .trade_order_entry-wrapper.order_side-selector-sell .trade_order_entry-form_fields-wrapper { - margin-bottom: 0.5rem !important; } + margin-bottom: 0.2rem !important; } + .trade_order_entry-wrapper.order_side-selector-sell .trade_order_entry-form_fields-wrapper .ant-collapse-header { + text-align: right; + color: var(--labels_secondary-inactive-label-text-graphics); + padding: 0; + font-size: 12px; } + .trade_order_entry-wrapper.order_side-selector-sell .trade_order_entry-form_fields-wrapper .ant-collapse-content-box { + padding: 0; } .trade_order_entry-wrapper.order_side-selector-buy { flex: 1; @@ -1848,7 +1875,14 @@ table th { .trade_order_entry-wrapper.order_side-selector-buy .trade_order_entry-form_inputs-wrapper .form-error { margin: 0.5rem 0; } .trade_order_entry-wrapper.order_side-selector-buy .trade_order_entry-form_fields-wrapper { - margin-bottom: 0.5rem !important; } + margin-bottom: 0.2rem !important; } + .trade_order_entry-wrapper.order_side-selector-buy .trade_order_entry-form_fields-wrapper .ant-collapse-header { + text-align: right; + color: var(--labels_secondary-inactive-label-text-graphics); + padding: 0; + font-size: 12px; } + .trade_order_entry-wrapper.order_side-selector-buy .trade_order_entry-form_fields-wrapper .ant-collapse-content-box { + padding: 0; } .risky-trade-wrapper { width: 32rem; } @@ -6091,6 +6125,15 @@ table th { width: 35% !important; transform: translateY(1.4rem); } +.ant-slider-mark-text { + color: var(--labels_secondary-inactive-label-text-graphics); + font-family: 'Open Sans' !important; + font-size: 0.9rem !important; } + +.ant-slider-mark-text-active { + color: var(--labels_important-active-labels-text-graphics); + font-weight: bold; } + .exir-button { width: 100%; font-size: 0.9rem; diff --git a/web/src/index.js b/web/src/index.js index 0d3e0b008f..e89ab1dbc2 100644 --- a/web/src/index.js +++ b/web/src/index.js @@ -97,8 +97,7 @@ const getConfigs = async () => { localStorage.setItem(key, JSON.stringify(remoteConfigs[key])); }); - const { data: { coins = {} } = {} } = await requestConstant(); - const coin_keys = Object.keys(coins); + const { data: { coins: coin_icons = {} } = {} } = await requestConstant(); setDefaultLogo(logo_image); setBaseCurrency(native_currency); @@ -107,7 +106,7 @@ const getConfigs = async () => { setExchangeInitialized(initialized); setSetupCompleted(setup_completed); - return merge({}, defaultConfig, remoteConfigs, { coin_keys }); + return merge({}, defaultConfig, remoteConfigs, { coin_icons }); }; const bootstrapApp = (appConfig) => { diff --git a/web/src/reducers/userReducer.js b/web/src/reducers/userReducer.js index 72128a43f6..ff6524422f 100644 --- a/web/src/reducers/userReducer.js +++ b/web/src/reducers/userReducer.js @@ -11,6 +11,7 @@ const USER_DATA_KEYS = [ 'phone_number', 'id_data', 'bank_account', + 'email_verified', ]; const INITIAL_OTP_OBJECT = { @@ -130,6 +131,7 @@ export default function reducer(state = INITIAL_STATE, action) { const { id, email, + email_verified, balance, crypto_wallet, verification_level, @@ -159,6 +161,7 @@ export default function reducer(state = INITIAL_STATE, action) { fetched: true, id, email, + email_verified, balance: { ...state.balance, ...balance, diff --git a/web/src/routes.js b/web/src/routes.js index 3a0a48e55e..7660231966 100644 --- a/web/src/routes.js +++ b/web/src/routes.js @@ -40,7 +40,7 @@ import { MobileHome, Broker, Plugins, - PluginServices, + // PluginServices, Settings, // Transfer, AdminFees, @@ -459,11 +459,11 @@ export default ( name="Admin plugins" component={withAdminProps(Plugins, 'plugins')} /> - + /> */} { + return new Promise((resolve) => setTimeout(resolve, ms)); +}; diff --git a/web/src/utils/icon.js b/web/src/utils/icon.js index 1fe8000924..ec7964cb7b 100644 --- a/web/src/utils/icon.js +++ b/web/src/utils/icon.js @@ -16,11 +16,30 @@ const BACKGROUND_ICON_IDS = ['EXCHANGE_LOGO', 'EXCHANGE_LOADER']; export const isBackgroundIcon = (iconId) => BACKGROUND_ICON_IDS.includes(iconId); -export const generateAllIcons = (themes, icons, coinKeys = []) => { +const generateServerSideDefaultIcons = (coins) => { + const icons = {}; + Object.entries(coins).forEach(([key, { logo }]) => { + if (logo) { + icons[`${key.toUpperCase()}_ICON`] = logo; + } + }); + + return icons; +}; + +export const generateAllIcons = (themes, icons, coin_icons = {}) => { + const coinKeys = Object.keys(coin_icons); const themeKeys = Object.keys(themes); + // server-side coin icons + const serverSideDefaultCoinIcons = generateServerSideDefaultIcons(coin_icons); + // missing keys and values are set from the default Icons Object - const defaultIconsObject = icons[defaultIconsKey]; + const defaultIconsObject = merge( + {}, + icons[defaultIconsKey], + serverSideDefaultCoinIcons + ); const allIcons = {};