diff --git a/lib/cash-in/cash-in-low.js b/lib/cash-in/cash-in-low.js index 59b3b1d8..0e696fd6 100644 --- a/lib/cash-in/cash-in-low.js +++ b/lib/cash-in/cash-in-low.js @@ -8,10 +8,13 @@ const E = require('../error') const PENDING_INTERVAL_MS = 60 * T.minutes -const massage = _.flow(_.omit(['direction', 'cryptoNetwork', 'bills', 'blacklisted', 'addressReuse']), +const massageFields = ['direction', 'cryptoNetwork', 'bills', 'blacklisted', 'addressReuse'] +const massageUpdateFields = _.concat(massageFields, 'cryptoAtoms') + +const massage = _.flow(_.omit(massageFields), convertBigNumFields, _.mapKeys(_.snakeCase)) -const massageUpdates = _.flow(_.omit(['cryptoAtoms', 'direction', 'cryptoNetwork', 'bills', 'blacklisted', 'addressReuse']), +const massageUpdates = _.flow(_.omit(massageUpdateFields), convertBigNumFields, _.mapKeys(_.snakeCase)) module.exports = {toObj, upsert, insert, update, massage, isClearToSend} diff --git a/lib/coin-utils.js b/lib/coin-utils.js index eee6f306..2fdbd45b 100644 --- a/lib/coin-utils.js +++ b/lib/coin-utils.js @@ -12,7 +12,8 @@ const CRYPTO_CURRENCIES = [ configFile: 'bitcoin.conf', daemon: 'bitcoind', defaultPort: 8332, - unitScale: 8 + unitScale: 8, + displayScale: 5 }, { cryptoCode: 'ETH', @@ -21,7 +22,8 @@ const CRYPTO_CURRENCIES = [ configFile: 'geth.conf', daemon: 'geth', defaultPort: 8545, - unitScale: 18 + unitScale: 18, + displayScale: 15 }, { cryptoCode: 'LTC', @@ -30,7 +32,8 @@ const CRYPTO_CURRENCIES = [ configFile: 'litecoin.conf', daemon: 'litecoind', defaultPort: 9332, - unitScale: 8 + unitScale: 8, + displayScale: 5 }, { cryptoCode: 'DASH', @@ -39,7 +42,8 @@ const CRYPTO_CURRENCIES = [ configFile: 'dash.conf', daemon: 'dashd', defaultPort: 9998, - unitScale: 8 + unitScale: 8, + displayScale: 5 }, { cryptoCode: 'ZEC', @@ -48,7 +52,8 @@ const CRYPTO_CURRENCIES = [ configFile: 'zcash.conf', daemon: 'zcashd', defaultPort: 8232, - unitScale: 8 + unitScale: 8, + displayScale: 5 }, { cryptoCode: 'BCH', @@ -57,7 +62,8 @@ const CRYPTO_CURRENCIES = [ configFile: 'bitcoincash.conf', daemon: 'bitcoincashd', defaultPort: 8335, - unitScale: 8 + unitScale: 8, + displayScale: 5 } ] diff --git a/lib/commission-math.js b/lib/commission-math.js index a3fc89c0..d02880ed 100644 --- a/lib/commission-math.js +++ b/lib/commission-math.js @@ -6,7 +6,7 @@ function truncateCrypto (cryptoAtoms, cryptoCode) { const DECIMAL_PLACES = 3 if (cryptoAtoms.eq(0)) return cryptoAtoms - const scale = 5 // TODO: change this to coins.displayScale when coins have that attribute + const scale = coinUtils.getCryptoCurrency(cryptoCode).displayScale const scaleFactor = BN(10).pow(scale) return BN(cryptoAtoms).truncated().div(scaleFactor) diff --git a/lib/coupons.js b/lib/coupons.js index f6bb1254..1c8fdd94 100644 --- a/lib/coupons.js +++ b/lib/coupons.js @@ -16,7 +16,7 @@ function createCoupon (code, discount) { return db.one(sql, [uuid.v4(), code, discount]) } -function softDeleteCoupon (couponId) { +function deleteCoupon (couponId) { const sql = `UPDATE coupons SET soft_deleted=true WHERE id=$1` return db.none(sql, [couponId]) } @@ -26,4 +26,4 @@ function getNumberOfAvailableCoupons () { return db.one(sql).then(res => res.count) } -module.exports = { getAvailableCoupons, getCoupon, createCoupon, softDeleteCoupon, getNumberOfAvailableCoupons } +module.exports = { getAvailableCoupons, getCoupon, createCoupon, deleteCoupon, getNumberOfAvailableCoupons } diff --git a/new-lamassu-admin/src/pages/LoyaltyPanel/CouponCodes.js b/new-lamassu-admin/src/pages/LoyaltyPanel/CouponCodes.js index 98ee1f4a..19d2de43 100644 --- a/new-lamassu-admin/src/pages/LoyaltyPanel/CouponCodes.js +++ b/new-lamassu-admin/src/pages/LoyaltyPanel/CouponCodes.js @@ -47,11 +47,12 @@ const Coupons = () => { const classes = useStyles() const [showModal, setShowModal] = useState(false) + const [errorMsg, setErrorMsg] = useState(null) const toggleModal = () => setShowModal(!showModal) const { data: couponResponse, loading } = useQuery(GET_COUPONS) - const [softDeleteCoupon] = useMutation(DELETE_COUPON, { + const [deleteCoupon] = useMutation(DELETE_COUPON, { refetchQueries: () => ['coupons'] }) @@ -59,8 +60,22 @@ const Coupons = () => { refetchQueries: () => ['coupons'] }) - const addCoupon = (code, discount) => { - createCoupon({ variables: { code: code, discount: discount } }) + const addCoupon = async (code, discount) => { + setErrorMsg(null) + const res = await createCoupon({ + variables: { code: code, discount: discount } + }) + + if (!res.errors) { + return setShowModal(false) + } + + const duplicateCouponError = res.errors.some(e => { + return e.message.includes('duplicate') + }) + + if (duplicateCouponError) + setErrorMsg('There is already a coupon with that code!') } const elements = [ @@ -90,7 +105,7 @@ const Coupons = () => { view: t => ( { - softDeleteCoupon({ variables: { couponId: t.id } }) + deleteCoupon({ variables: { couponId: t.id } }) }}> @@ -127,7 +142,11 @@ const Coupons = () => { )} { + setErrorMsg(null) + setShowModal(false) + }} + errorMsg={errorMsg} addCoupon={addCoupon} /> > diff --git a/new-lamassu-admin/src/pages/LoyaltyPanel/CouponCodes.styles.js b/new-lamassu-admin/src/pages/LoyaltyPanel/CouponCodes.styles.js index 32c1571e..0aa04594 100644 --- a/new-lamassu-admin/src/pages/LoyaltyPanel/CouponCodes.styles.js +++ b/new-lamassu-admin/src/pages/LoyaltyPanel/CouponCodes.styles.js @@ -1,14 +1,14 @@ -import { spacer, fontPrimary, primaryColor } from 'src/styling/variables' +import { + spacer, + fontPrimary, + primaryColor, + errorColor +} from 'src/styling/variables' const styles = { footer: { margin: [['auto', 0, spacer * 3, 'auto']] }, - modalTitle: { - marginTop: -5, - color: primaryColor, - fontFamily: fontPrimary - }, modalLabel1: { marginTop: 20 }, @@ -20,7 +20,6 @@ const styles = { }, discountInput: { display: 'flex', - height: '100%', flexDirection: 'row', alignItems: 'flex-start' }, @@ -33,6 +32,14 @@ const styles = { }, tableWidth: { width: 620 + }, + error: { + color: errorColor + }, + form: { + display: 'flex', + flexDirection: 'column', + height: '100%' } } diff --git a/new-lamassu-admin/src/pages/LoyaltyPanel/CouponCodesModal.js b/new-lamassu-admin/src/pages/LoyaltyPanel/CouponCodesModal.js index 8f832f0c..5588fadc 100644 --- a/new-lamassu-admin/src/pages/LoyaltyPanel/CouponCodesModal.js +++ b/new-lamassu-admin/src/pages/LoyaltyPanel/CouponCodesModal.js @@ -1,121 +1,106 @@ import { makeStyles } from '@material-ui/core/styles' +import { Form, Formik, Field } from 'formik' import * as R from 'ramda' -import React, { useState } from 'react' +import React from 'react' +import * as Yup from 'yup' import Modal from 'src/components/Modal' import { Tooltip } from 'src/components/Tooltip' import { Button } from 'src/components/buttons' -import { TextInput, NumberInput } from 'src/components/inputs/base' -import { H1, H3, TL1, P } from 'src/components/typography' +import { TextInput, NumberInput } from 'src/components/inputs/formik' +import { H3, TL1, P } from 'src/components/typography' import styles from './CouponCodes.styles' const useStyles = makeStyles(styles) -const CouponCodesModal = ({ showModal, toggleModal, addCoupon }) => { +const initialValues = { + code: '', + discount: '' +} + +const validationSchema = Yup.object().shape({ + code: Yup.string() + .required() + .trim() + .max(25), + discount: Yup.number() + .required() + .min(0) + .max(100) +}) + +const CouponCodesModal = ({ showModal, onClose, errorMsg, addCoupon }) => { const classes = useStyles() - const [codeField, setCodeField] = useState('') - const [discountField, setDiscountField] = useState('') - const [invalidCode, setInvalidCode] = useState(false) - const [invalidDiscount, setInvalidDiscount] = useState(false) - - const handleCodeChange = event => { - if (event.target.value === '') { - setInvalidCode(false) - } - setCodeField(event.target.value) - } - - const handleDiscountChange = event => { - if (event.target.value === '') { - setInvalidDiscount(false) - } - setDiscountField(event.target.value) - } - - const handleClose = () => { - setCodeField('') - setDiscountField('') - setInvalidCode(false) - setInvalidDiscount(false) - toggleModal() - } - - const handleAddCoupon = () => { - if (codeField.trim() === '') { - setInvalidCode(true) - return - } - if (!validDiscount(discountField)) { - setInvalidDiscount(true) - return - } - if (codeField.trim() !== '' && validDiscount(discountField)) { - addCoupon(R.toUpper(codeField.trim()), parseInt(discountField)) - handleClose() - } - } - - const validDiscount = discount => { - const parsedDiscount = parseInt(discount) - return parsedDiscount >= 0 && parsedDiscount <= 100 + const handleAddCoupon = (code, discount) => { + addCoupon(R.toUpper(code), parseInt(discount)) } return ( <> {showModal && ( - Add coupon code discount - Coupon code name - - - Define discount rate - - - The discount rate inserted will be applied to the commissions of - all transactions performed with this respective coupon code. - - - (It should be a number between 0 (zero) and 100 (one hundred)). - - - - - - - % - - - - Add coupon - + { + handleAddCoupon(code, discount) + resetForm() + }}> + + Coupon code name + + + Define discount rate + + + The discount rate inserted will be applied to the + commissions of all transactions performed with this + respective coupon code. + + + (It should be a number between 0 (zero) and 100 (one + hundred)). + + + + + + + % + + + {errorMsg} + + + Add coupon + + + + )} >
- The discount rate inserted will be applied to the commissions of - all transactions performed with this respective coupon code. -
- (It should be a number between 0 (zero) and 100 (one hundred)). -
+ The discount rate inserted will be applied to the + commissions of all transactions performed with this + respective coupon code. +
+ (It should be a number between 0 (zero) and 100 (one + hundred)). +