fix: coupon form, styling and server handling

This commit is contained in:
Sérgio Salgado 2021-01-07 15:01:49 +00:00 committed by Josh Harvey
parent 5045821593
commit 9d4c4041dc
7 changed files with 138 additions and 118 deletions

View file

@ -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}

View file

@ -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
}
]

View file

@ -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)

View file

@ -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 }

View file

@ -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 => (
<IconButton
onClick={() => {
softDeleteCoupon({ variables: { couponId: t.id } })
deleteCoupon({ variables: { couponId: t.id } })
}}>
<DeleteIcon />
</IconButton>
@ -127,7 +142,11 @@ const Coupons = () => {
)}
<CouponCodesModal
showModal={showModal}
toggleModal={toggleModal}
onClose={() => {
setErrorMsg(null)
setShowModal(false)
}}
errorMsg={errorMsg}
addCoupon={addCoupon}
/>
</>

View file

@ -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%'
}
}

View file

@ -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 && (
<Modal
title="Add coupon code discount"
closeOnBackdropClick={true}
width={600}
height={500}
handleClose={handleClose}
handleClose={onClose}
open={true}>
<H1 className={classes.modalTitle}>Add coupon code discount</H1>
<Formik
initialValues={initialValues}
validationSchema={validationSchema}
onSubmit={({ code, discount }, { resetForm }) => {
handleAddCoupon(code, discount)
resetForm()
}}>
<Form id="coupon-form" className={classes.form}>
<H3 className={classes.modalLabel1}>Coupon code name</H3>
<TextInput
error={invalidCode}
name="coupon-code"
<Field
name="code"
autoFocus
id="coupon-code"
type="text"
size="lg"
autoComplete="off"
width={338}
onChange={handleCodeChange}
value={codeField}
inputProps={{ style: { textTransform: 'uppercase' } }}
component={TextInput}
/>
<div className={classes.modalLabel2Wrapper}>
<H3 className={classes.modalLabel2}>Define discount rate</H3>
<Tooltip width={304}>
<P>
The discount rate inserted will be applied to the commissions of
all transactions performed with this respective coupon code.
The discount rate inserted will be applied to the
commissions of all transactions performed with this
respective coupon code.
</P>
<P>
(It should be a number between 0 (zero) and 100 (one hundred)).
(It should be a number between 0 (zero) and 100 (one
hundred)).
</P>
</Tooltip>
</div>
<div className={classes.discountInput}>
<NumberInput
error={invalidDiscount}
name="coupon-discount"
id="coupon-discount"
<Field
name="discount"
size="lg"
autoComplete="off"
width={50}
onChange={handleDiscountChange}
value={discountField}
decimalScale={0}
className={classes.discountInputField}
component={NumberInput}
/>
<TL1 inline className={classes.inputLabel}>
%
</TL1>
</div>
<span className={classes.error}>{errorMsg}</span>
<div className={classes.footer}>
<Button onClick={handleAddCoupon}>Add coupon</Button>
<Button type="submit" form="coupon-form">
Add coupon
</Button>
</div>
</Form>
</Formik>
</Modal>
)}
</>