diff --git a/web/src/config/lang/en.json b/web/src/config/lang/en.json index e48f04990d..d1b992edc3 100644 --- a/web/src/config/lang/en.json +++ b/web/src/config/lang/en.json @@ -2286,7 +2286,9 @@ "CHECK_CONFIRM_PAYMENT_DESC_2": "After confirming below the crypto ({0} {1}) will be released.", "CHECK_CONFIRM_DECLARATION": "I confirm that I have received the correct payment", "P2P_ORDER_COMPLETE": "P2P Order Complete", - "EXIST_PAYMENT_METHOD_DESC": "You’ve already added a payment method for {0}" + "EXIST_PAYMENT_METHOD_DESC": "You’ve already added a payment method for {0}", + "ADMIN_ORDER_CANCELLATION_MESSAGE": "An exchange operator has cancelled this order. The transaction is closed.", + "TIME_LIMIT_REACHED": "0:00 (Please contact your counter-party)" }, "VOLUME": { "VOLUME": "VOLUME", diff --git a/web/src/containers/Admin/Trades/index.css b/web/src/containers/Admin/Trades/index.css index 2b541ee076..1ff30ecbba 100644 --- a/web/src/containers/Admin/Trades/index.css +++ b/web/src/containers/Admin/Trades/index.css @@ -819,3 +819,26 @@ .p2p-admin-confirm-warning-popup-wrapper .ant-modal-close-x { color: white; } + +.p2p-admin-order-details-popup-wrapper + .p2p-admin-order-details-container + .title { + font-size: 22px; + margin-bottom: 3%; +} + +.p2p-admin-order-details-popup-wrapper + .p2p-admin-order-details-container + .order-details { + margin-top: 2%; + display: flex; + align-items: center; +} + +.p2p-admin-order-details-popup-wrapper + .p2p-admin-order-details-container + .order-details + .asset-icon { + position: relative; + top: 3px; +} diff --git a/web/src/containers/Admin/Trades/p2pActive.js b/web/src/containers/Admin/Trades/p2pActive.js index fb42a2034e..a8815b7712 100644 --- a/web/src/containers/Admin/Trades/p2pActive.js +++ b/web/src/containers/Admin/Trades/p2pActive.js @@ -9,8 +9,9 @@ import { } from 'containers/P2P/actions/p2pActions'; import { getToken } from 'utils/token'; import { WS_URL } from 'config/constants'; +import { Coin } from 'components'; -const dataSource = (setIsConfirmWarning, setUserData) => { +const dataSource = (setIsConfirmWarning, setUserData, setIsOrderDetails) => { return [ { title: 'Order Id', dataIndex: 'id', key: 'id' }, { @@ -51,15 +52,32 @@ const dataSource = (setIsConfirmWarning, setUserData) => { ); }, }, + { + title: 'View More', + render: (data) => { + return ( + + ); + }, + }, ]; }; -const P2PActive = ({ user }) => { +const P2PActive = ({ user, coins }) => { const [getOrders, setGetOrders] = useState(); const [selectedTransaction, setSelectedTransaction] = useState(); const [webSocket, setWebSocket] = useState(); const [isConfirmWarning, setIsConfirmWarning] = useState(false); const [userData, setUserData] = useState(); + const [isOrderDetails, setIsOrderDetails] = useState(false); const fetchData = async () => { try { @@ -162,7 +180,7 @@ const P2PActive = ({ user }) => { user_status: 'cancelled', }); fetchData(); - notificationStatus('cancelled'); + notificationStatus('cancelled', 'admin cancel'); setSelectedTransaction(data); } catch (error) { console.error(error); @@ -210,8 +228,91 @@ const P2PActive = ({ user }) => { + } + visible={isOrderDetails} + footer={null} + onCancel={() => { + setIsOrderDetails(false); + }} + width={450} + className="p2p-admin-order-details-popup-wrapper p2p-admin-confirm-warning-popup-wrapper" + > +
+
Order Details
+
+ + Transaction Id : + + {userData?.transaction_id} +
+
+ Fiat Amount: + {userData?.amount_fiat} + + {userData?.deal?.spending_asset?.toUpperCase()} + + + + +
+ +
+ Price: + {userData?.price} + + {userData?.deal?.spending_asset?.toUpperCase()} + + + + + (per coin) + + {userData?.deal?.buying_asset?.toUpperCase()} + + + + +
+
+ Crypto Amount: + {userData?.amount_digital_currency} + + {userData?.deal?.buying_asset?.toUpperCase()} + + + + +
+
+ + Payment Method: + + + {userData?.payment_method_used?.system_name} + +
+
+
@@ -221,6 +322,7 @@ const P2PActive = ({ user }) => { const mapStateToProps = (state) => ({ user: state.user, + coins: state.app.coins, }); export default connect(mapStateToProps)(P2PActive); diff --git a/web/src/containers/App/MobileBarMoreOptions.js b/web/src/containers/App/MobileBarMoreOptions.js index 9a3f3e873a..622a840a52 100644 --- a/web/src/containers/App/MobileBarMoreOptions.js +++ b/web/src/containers/App/MobileBarMoreOptions.js @@ -38,6 +38,7 @@ const MobileBarMoreOptions = ({ pinnedAsset, getMarkets, quickTrade, + getRemoteRoutes, }) => { const [search, setSearch] = useState(''); const [isDialogOpen, setIsDialogOpen] = useState(false); @@ -274,10 +275,8 @@ const MobileBarMoreOptions = ({ STRINGS['MORE_OPTIONS_LABEL.OTHER_FUNCTIONS.SECURITY'], ], }, - { - icon_id: 'BUY_CRYPTO_OPTION', - iconText: 'MORE_OPTIONS_LABEL.ICONS.BUY_CRYPTO', - path: '/buy-crypto', + ...getRemoteRoutes?.map((route, index) => ({ + ...route, isDisplay: true, searchContent: [ STRINGS['MARKET_OPTIONS.CARD'], @@ -293,7 +292,7 @@ const MobileBarMoreOptions = ({ STRINGS['MORE_OPTIONS_LABEL.OTHER_FUNCTIONS.BUY_COIN'], STRINGS['MORE_OPTIONS_LABEL.OTHER_FUNCTIONS.BUY_TOKEN'], ], - }, + })), { icon_id: 'DEFI_STAKE_OPTION_ICON', iconText: 'MORE_OPTIONS_LABEL.ICONS.DEFI_STAKE', @@ -731,7 +730,9 @@ const MobileBarMoreOptions = ({ const filterOptions = (options) => { return options?.filter((option) => { - const iconTextMatch = (STRINGS[option?.iconText] || '') + const iconTextMatch = ( + STRINGS[option?.iconText ? option?.iconText : option?.string_id] || '' + ) ?.toLowerCase() .includes(search?.toLowerCase()); const searchContentMatch = option?.searchContent?.some((content) => @@ -753,7 +754,12 @@ const MobileBarMoreOptions = ({
onHandleRoute(data?.iconText, data?.path)} + onClick={() => + onHandleRoute( + data?.iconText ? data?.iconText : data?.string_id, + data?.path + ) + } > {fieldHasCoinIcon?.includes(data?.iconText) ? (
@@ -769,13 +775,29 @@ const MobileBarMoreOptions = ({ ) : ( )}
- - {STRINGS[data?.iconText]} + + { + STRINGS[ + data?.iconText ? data?.iconText : data?.string_id + ] + }
@@ -919,6 +941,7 @@ const mapStateToProps = (store) => ({ pinnedAsset: store.app.pinned_assets, getMarkets: MarketsSelector(store), quickTrade: store.app.quicktrade, + getRemoteRoutes: store.app.remoteRoutes, }); const mapDispatchToProps = (dispatch) => ({ diff --git a/web/src/containers/App/Socket.js b/web/src/containers/App/Socket.js index 7c5c473dfa..095c848059 100644 --- a/web/src/containers/App/Socket.js +++ b/web/src/containers/App/Socket.js @@ -461,6 +461,10 @@ class Container extends Component { {data.action === 'getStatus' && data?.data?.status === 'appeal' ? STRINGS['P2P.APPEAL_STATUS_MESSAGE'] + : data?.action === 'getStatus' && + data?.data?.status === 'cancelled' && + data?.data?.title === 'admin cancel' + ? STRINGS['P2P.ADMIN_ORDER_CANCELLATION_MESSAGE'] : data?.action === 'getStatus' && data?.data?.status === 'cancelled' ? STRINGS['P2P.CANCEL_STATUS_MESSAGE'] @@ -541,17 +545,18 @@ class Container extends Component { notification.close(newLastNotification?.key); notification.open({ ...newLastNotification, - icon: - newLastNotificationMessage === - (STRINGS['P2P.CANCEL_STATUS_MESSAGE'] || - STRINGS['P2P.APPEAL_STATUS_MESSAGE']) ? ( - - ) : newLastNotificationMessage === - STRINGS['P2P.NEW_MESSAGE'] ? ( - - ) : ( - - ), + icon: [ + STRINGS['P2P.CANCEL_STATUS_MESSAGE'], + STRINGS['P2P.ADMIN_ORDER_CANCELLATION_MESSAGE'], + STRINGS['P2P.APPEAL_STATUS_MESSAGE'], + ]?.includes(newLastNotificationMessage) ? ( + + ) : newLastNotificationMessage === + STRINGS['P2P.NEW_MESSAGE'] ? ( + + ) : ( + + ), className: isMobile ? 'p2p-chat-notification-wrapper p2p-chat-notification-wrapper-mobile' : 'p2p-chat-notification-wrapper', @@ -583,17 +588,18 @@ class Container extends Component { notification.close(previousNotification?.key); notification.open({ ...previousNotification, - icon: - previousNotificationMessage === - (STRINGS['P2P.CANCEL_STATUS_MESSAGE'] || - STRINGS['P2P.APPEAL_STATUS_MESSAGE']) ? ( - - ) : previousNotificationMessage === - STRINGS['P2P.NEW_MESSAGE'] ? ( - - ) : ( - - ), + icon: [ + STRINGS['P2P.CANCEL_STATUS_MESSAGE'], + STRINGS['P2P.ADMIN_ORDER_CANCELLATION_MESSAGE'], + STRINGS['P2P.APPEAL_STATUS_MESSAGE'], + ]?.includes(previousNotificationMessage) ? ( + + ) : previousNotificationMessage === + STRINGS['P2P.NEW_MESSAGE'] ? ( + + ) : ( + + ), className: isMobile ? 'p2p-chat-notification-wrapper p2p-chat-notification-wrapper-mobile p2p-chat-notification' : 'p2p-chat-notification-wrapper p2p-chat-notification', diff --git a/web/src/containers/P2P/P2POrder/P2POrder.js b/web/src/containers/P2P/P2POrder/P2POrder.js index 40c445032a..de6ce58334 100644 --- a/web/src/containers/P2P/P2POrder/P2POrder.js +++ b/web/src/containers/P2P/P2POrder/P2POrder.js @@ -26,7 +26,7 @@ import { import { formatToCurrency } from 'utils/currency'; import { getToken } from 'utils/token'; import { WS_URL } from 'config/constants'; -import { renderFeedback } from '../Utilis'; +import { renderFeedback, Timer } from '../Utilis'; import classnames from 'classnames'; import BigNumber from 'bignumber.js'; import '../_P2P.scss'; @@ -1168,12 +1168,14 @@ const P2POrder = ({
-
- - {STRINGS['P2P.EXPECTED_TIME']} - -
- + {selectedOrder?.user_status === 'pending' && ( +
+ + {STRINGS['P2P.EXPECTED_TIME']} + + +
+ )} {user.id === selectedOrder?.user_id && ( <> {selectedOrder.user_status === 'pending' && ( diff --git a/web/src/containers/P2P/P2POrder/P2POrderDetails.js b/web/src/containers/P2P/P2POrder/P2POrderDetails.js index b55ba8b250..ddf3976a67 100644 --- a/web/src/containers/P2P/P2POrder/P2POrderDetails.js +++ b/web/src/containers/P2P/P2POrder/P2POrderDetails.js @@ -9,6 +9,7 @@ import { CheckCircleTwoTone } from '@ant-design/icons'; import STRINGS from 'config/localizedStrings'; import { Coin, EditWrapper, Image } from 'components'; import { setIsChat } from 'actions/appActions'; +import { Timer } from '../Utilis'; const P2POrderDetails = ({ user, @@ -321,11 +322,14 @@ const P2POrderDetails = ({
- {/*
- - {STRINGS['P2P.EXPECTED_TIME']} - -
*/} + {selectedOrder?.user_status === 'pending' && ( +
+ + {STRINGS['P2P.EXPECTED_TIME']} + + +
+ )} {user?.id === selectedOrder?.user_id && ( <> diff --git a/web/src/containers/P2P/P2POrders.js b/web/src/containers/P2P/P2POrders.js index bf3e6d766b..53c0650346 100644 --- a/web/src/containers/P2P/P2POrders.js +++ b/web/src/containers/P2P/P2POrders.js @@ -466,6 +466,22 @@ const P2POrders = ({
+
+ + + {STRINGS['P2P.PRICE']}: + + + + {formatAmount( + transaction?.deal?.buying_asset, + transaction?.price + )} + + + {transaction?.deal?.buying_asset?.toUpperCase()} + +
@@ -479,7 +495,7 @@ const P2POrders = ({ transaction?.amount_digital_currency )} - + {transaction?.deal?.buying_asset?.toUpperCase()} { + const orderCreatedDate = new Date(order?.created_at)?.getTime(); + const [time, setTime] = useState({ min: 30, sec: 0 }); + const [startTime] = useState(orderCreatedDate); + const [timeLimitReached, setTimeLimitReached] = useState(false); + const frameIdRef = useRef(); + + const cancelTimer = () => { + if (frameIdRef.current) { + cancelAnimationFrame(frameIdRef.current); + frameIdRef.current = null; + } + }; + + useEffect(() => { + const update = () => { + const now = Date.now(); + const elapsed = Math.floor((now - startTime) / 1000); + const totalSeconds = 30 * 60 - elapsed; + + if (totalSeconds <= 0) { + setTime({ min: 0, sec: 0 }); + setTimeLimitReached(true); + cancelTimer(); + } else { + const min = Math.floor(totalSeconds / 60); + const sec = totalSeconds % 60; + setTime({ min, sec }); + setTimeLimitReached(false); + frameIdRef.current = requestAnimationFrame(update); + if ( + order?.user_status === 'confirmed' || + order?.transaction_status === 'expired' + ) { + setTime({ min: 0, sec: 0 }); + setTimeLimitReached(true); + cancelTimer(); + } + } + }; + + frameIdRef.current = requestAnimationFrame(update); + + return () => cancelTimer(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [startTime, order]); + + const totalSeconds = 30 * 60; + const elapsedSeconds = Math.floor((Date.now() - startTime) / 1000); + const percentage = (elapsedSeconds / totalSeconds) * 100; + + return ( +
+
+ {!timeLimitReached && ( +
+ )} +
+ {timeLimitReached ? ( +
+ + {STRINGS['P2P.TIME_LIMIT_REACHED']} + +
+ ) : ( +
+ {time?.min < 10 ? `0${time?.min}` : time?.min}: + {time?.sec < 10 ? `0${time?.sec}` : time?.sec} +
+ )} +
+
+
+ ); +}; + export default NoDealsData; diff --git a/web/src/containers/P2P/_P2P.scss b/web/src/containers/P2P/_P2P.scss index fa127c8fbb..ab42e97809 100644 --- a/web/src/containers/P2P/_P2P.scss +++ b/web/src/containers/P2P/_P2P.scss @@ -1792,6 +1792,37 @@ } .order-verification-container { + .order-timer-wrapper { + .timer-container { + display: flex; + + .timer-details { + padding: 2% 0; + display: flex; + justify-content: center; + align-items: center; + gap: 10px; + border-radius: 25px; + } + + .timer-circle { + width: 20px; + height: 20px; + border-radius: 50%; + background: conic-gradient( + var(--labels_important-active-labels-text-graphics) 0%, + var(--base_background) 0% + ); + display: flex; + justify-content: center; + align-items: center; + position: relative; + border: 1px solid + var(--labels_important-active-labels-text-graphics); + } + } + } + .order-confirmed-container { .order-complete-title, .go-to-deposit-link, @@ -3870,6 +3901,13 @@ } .order-verification-container { + .order-timer-wrapper { + .timer-circle { + width: 30px; + height: 30px; + } + } + .order-confirmed-container { .order-complete-title { .edit-wrapper__container { diff --git a/web/src/index.css b/web/src/index.css index f81ddea12f..d9d478e51c 100644 --- a/web/src/index.css +++ b/web/src/index.css @@ -11928,6 +11928,25 @@ table th { border-left: 5px solid var(--specials_checks-okay-done); } .p2p-summary-container .p2p-order-wrapper .p2p-order-container .p2p-order-details-container .amount-transfer-container .active-sell { border-left: 5px solid var(--trading_selling-related-elements); } + .p2p-summary-container .p2p-order-wrapper .p2p-order-container .p2p-order-details-container .order-verification-container .order-timer-wrapper .timer-container { + display: flex; } + .p2p-summary-container .p2p-order-wrapper .p2p-order-container .p2p-order-details-container .order-verification-container .order-timer-wrapper .timer-container .timer-details { + padding: 2% 0; + display: flex; + justify-content: center; + align-items: center; + gap: 10px; + border-radius: 25px; } + .p2p-summary-container .p2p-order-wrapper .p2p-order-container .p2p-order-details-container .order-verification-container .order-timer-wrapper .timer-container .timer-circle { + width: 20px; + height: 20px; + border-radius: 50%; + background: conic-gradient(var(--labels_important-active-labels-text-graphics) 0%, var(--base_background) 0%); + display: flex; + justify-content: center; + align-items: center; + position: relative; + border: 1px solid var(--labels_important-active-labels-text-graphics); } .p2p-summary-container .p2p-order-wrapper .p2p-order-container .p2p-order-details-container .order-verification-container .order-confirmed-container .order-complete-title, .p2p-summary-container .p2p-order-wrapper .p2p-order-container .p2p-order-details-container .order-verification-container .order-confirmed-container .go-to-deposit-link, .p2p-summary-container .p2p-order-wrapper .p2p-order-container .p2p-order-details-container .order-verification-container .order-confirmed-container .go-to-withdraw-link { @@ -12560,7 +12579,7 @@ table th { .release-amount-popup-wrapper .ReactModal__Content .release-amount-details-container .check-icon svg path:nth-child(2) { fill: var(--specials_checks-okay-done); } .release-amount-popup-wrapper .ReactModal__Content .release-amount-details-container .check-icon svg path:nth-child(3) { - fill: #FFFFFF; } + fill: #ffffff; } .cancel-popup-wrapper .ReactModal__Content, .confirm-popup-wrapper .ReactModal__Content, @@ -12573,7 +12592,7 @@ table th { .cancel-popup-wrapper .ReactModal__Content .feedback-submit-popup-container .warning-icon, .confirm-popup-wrapper .ReactModal__Content .feedback-submit-popup-container .warning-icon, .confirmation-remove-deal-popup-wrapper .ReactModal__Content .feedback-submit-popup-container .warning-icon { - background-color: #FFFFFF; + background-color: #ffffff; display: flex; align-items: center; justify-content: center; @@ -13398,6 +13417,9 @@ table th { .layout-mobile .p2p-summary-container .p2p-order-wrapper .p2p-order-container .p2p-order-details-container .confirm-notify-button-container .chat-link-container .chat-link { color: var(--specials_buttons-links-and-highlights); font-size: 24px; } + .layout-mobile .p2p-summary-container .p2p-order-wrapper .p2p-order-container .p2p-order-details-container .order-verification-container .order-timer-wrapper .timer-circle { + width: 30px; + height: 30px; } .layout-mobile .p2p-summary-container .p2p-order-wrapper .p2p-order-container .p2p-order-details-container .order-verification-container .order-confirmed-container .order-complete-title .edit-wrapper__container { font-size: 20px; } .layout-mobile .p2p-summary-container .p2p-order-wrapper .p2p-order-container .p2p-order-details-container .order-verification-container .order-confirmed-container .go-to-deposit-link,