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>
<H3 className={classes.modalLabel1}>Coupon code name</H3>
<TextInput
error={invalidCode}
name="coupon-code"
autoFocus
id="coupon-code"
type="text"
size="lg"
width={338}
onChange={handleCodeChange}
value={codeField}
inputProps={{ style: { textTransform: 'uppercase' } }}
/>
<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.
</P>
<P>
(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"
size="lg"
width={50}
onChange={handleDiscountChange}
value={discountField}
decimalScale={0}
className={classes.discountInputField}
/>
<TL1 inline className={classes.inputLabel}>
%
</TL1>
</div>
<div className={classes.footer}>
<Button onClick={handleAddCoupon}>Add coupon</Button>
</div>
<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>
<Field
name="code"
autoFocus
size="lg"
autoComplete="off"
width={338}
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.
</P>
<P>
(It should be a number between 0 (zero) and 100 (one
hundred)).
</P>
</Tooltip>
</div>
<div className={classes.discountInput}>
<Field
name="discount"
size="lg"
autoComplete="off"
width={50}
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 type="submit" form="coupon-form">
Add coupon
</Button>
</div>
</Form>
</Formik>
</Modal>
)}
</>