diff --git a/lib/loyalty.js b/lib/loyalty.js
index ec61c594..370c25b1 100644
--- a/lib/loyalty.js
+++ b/lib/loyalty.js
@@ -1,5 +1,6 @@
const db = require('./db')
const uuid = require('uuid')
+const _ = require('lodash/fp')
function getAvailablePromoCodes () {
const sql = `SELECT * FROM coupons WHERE soft_deleted=false`
@@ -28,12 +29,23 @@ function getNumberOfAvailablePromoCodes () {
function getAvailableIndividualDiscounts () {
const sql = `SELECT * from individual_discounts WHERE soft_deleted=false`
- return db.any(sql)
+ return db.any(sql).then(res => _.map(it => ({
+ id: it.id,
+ idType: it.identification,
+ value: it.value,
+ discount: it.discount,
+ softDeleted: it.soft_deleted
+ }), res))
}
function createIndividualDiscount (idType, value, discount) {
+ const idTypes = {
+ phone: 'phone',
+ idNumber: 'id_number'
+ }
+
const sql = `INSERT INTO individual_discounts (id, identification, value, discount) VALUES ($1, $2, $3, $4) RETURNING *`
- return db.one(sql, [uuid.v4(), idType, value, discount])
+ return db.one(sql, [uuid.v4(), idTypes[idType], value, discount])
}
function deleteIndividualDiscount (id) {
diff --git a/lib/new-admin/graphql/resolvers/loyalty.resolver.js b/lib/new-admin/graphql/resolvers/loyalty.resolver.js
index 9a621451..bebf4c08 100644
--- a/lib/new-admin/graphql/resolvers/loyalty.resolver.js
+++ b/lib/new-admin/graphql/resolvers/loyalty.resolver.js
@@ -8,7 +8,7 @@ const resolvers = {
Mutation: {
createPromoCode: (...[, { code, discount }]) => loyalty.createPromoCode(code, discount),
deletePromoCode: (...[, { codeId }]) => loyalty.deletePromoCode(codeId),
- createIndividualDiscount: (...[, { identificationType, value, discount }]) => loyalty.createIndividualDiscount(identificationType, value, discount),
+ createIndividualDiscount: (...[, { idType, value, discount }]) => loyalty.createIndividualDiscount(idType, value, discount),
deleteIndividualDiscount: (...[, { discountId }]) => loyalty.deleteIndividualDiscount(discountId)
}
}
diff --git a/lib/new-admin/graphql/types/loyalty.type.js b/lib/new-admin/graphql/types/loyalty.type.js
index ba84e032..c5d8862e 100644
--- a/lib/new-admin/graphql/types/loyalty.type.js
+++ b/lib/new-admin/graphql/types/loyalty.type.js
@@ -3,14 +3,14 @@ const { gql } = require('apollo-server-express')
const typeDef = gql`
type IndividualDiscount {
id: ID!
- identificationType: DiscountIdentificationType
+ idType: DiscountIdentificationType
value: String!
discount: Int
}
enum DiscountIdentificationType {
phone
- idCard
+ idNumber
}
type PromoCode {
@@ -27,7 +27,7 @@ const typeDef = gql`
type Mutation {
createPromoCode(code: String!, discount: Int!): PromoCode @auth
deletePromoCode(codeId: ID!): PromoCode @auth
- createIndividualDiscount(identificationType: DiscountIdentificationType!, value: String!, discount: Int!): IndividualDiscount @auth
+ createIndividualDiscount(idType: DiscountIdentificationType!, value: String!, discount: Int!): IndividualDiscount @auth
deleteIndividualDiscount(discountId: ID!): IndividualDiscount @auth
}
`
diff --git a/migrations/1626891847835-add-individual-discounts.js b/migrations/1626891847835-add-individual-discounts.js
index 037ec658..28539a4d 100644
--- a/migrations/1626891847835-add-individual-discounts.js
+++ b/migrations/1626891847835-add-individual-discounts.js
@@ -10,7 +10,7 @@ exports.up = function (next) {
discount SMALLINT NOT NULL,
soft_deleted BOOLEAN DEFAULT false
)`,
- `CREATE UNIQUE INDEX uq_individual_discount ON individual_discounts (value) WHERE NOT soft_deleted`
+ `CREATE UNIQUE INDEX uq_individual_discount ON individual_discounts (identification, value) WHERE NOT soft_deleted`
]
db.multi(sql, next)
diff --git a/new-lamassu-admin/src/pages/LoyaltyPanel/IndividualDiscount.styles.js b/new-lamassu-admin/src/pages/LoyaltyPanel/IndividualDiscount.styles.js
new file mode 100644
index 00000000..ff7c95cb
--- /dev/null
+++ b/new-lamassu-admin/src/pages/LoyaltyPanel/IndividualDiscount.styles.js
@@ -0,0 +1,53 @@
+import { spacer, errorColor } from 'src/styling/variables'
+
+const styles = {
+ identification: {
+ display: 'flex',
+ flexDirection: 'row',
+ alignItems: 'center',
+ '& > *:first-child': {
+ marginRight: 10
+ }
+ },
+ form: {
+ display: 'flex',
+ flexDirection: 'column',
+ height: '100%',
+ '& > *': {
+ marginBottom: 20
+ }
+ },
+ radioGroup: {
+ display: 'flex',
+ flexDirection: 'row',
+ '& > *': {
+ marginLeft: 15
+ },
+ '& > *:first-child': {
+ marginLeft: 0
+ }
+ },
+ discountRateWrapper: {
+ display: 'flex',
+ flexDirection: 'row',
+ alignItems: 'center'
+ },
+ discountInput: {
+ display: 'flex',
+ flexDirection: 'row',
+ alignItems: 'center'
+ },
+ footer: {
+ display: 'flex',
+ flexDirection: 'row',
+ margin: [['auto', 0, spacer * 3, 0]]
+ },
+ submit: {
+ margin: [['auto', 0, 0, 'auto']]
+ },
+ error: {
+ color: errorColor
+ }
+}
+
+export default styles
diff --git a/new-lamassu-admin/src/pages/LoyaltyPanel/IndividualDiscountModal.js b/new-lamassu-admin/src/pages/LoyaltyPanel/IndividualDiscountModal.js
new file mode 100644
index 00000000..c9cc9f9a
--- /dev/null
+++ b/new-lamassu-admin/src/pages/LoyaltyPanel/IndividualDiscountModal.js
@@ -0,0 +1,183 @@
+import { makeStyles } from '@material-ui/core/styles'
+import classNames from 'classnames'
+import { Form, Formik, Field } from 'formik'
+import React from 'react'
+import * as Yup from 'yup'
+
+import ErrorMessage from 'src/components/ErrorMessage'
+import Modal from 'src/components/Modal'
+import { Tooltip } from 'src/components/Tooltip'
+import { Button } from 'src/components/buttons'
+import {
+ NumberInput,
+ RadioGroup,
+ TextInput
+} from 'src/components/inputs/formik'
+import { H3, TL1, P } from 'src/components/typography'
+
+import styles from './IndividualDiscount.styles'
+
+const useStyles = makeStyles(styles)
+
+const initialValues = {
+ idType: '',
+ value: '',
+ discount: ''
+}
+
+const validationSchema = Yup.object().shape({
+ idType: Yup.string()
+ .required('An identification type is required!')
+ .trim(),
+ value: Yup.string()
+ .required('A value is required!')
+ .trim()
+ .min(3, 'Value should have at least 3 characters!')
+ .max(20, 'Value should have a maximum of 20 characters!'),
+ discount: Yup.number()
+ .required('A discount rate is required!')
+ .min(0, 'Discount rate should be a positive number!')
+ .max(100, 'Discount rate should have a maximum value of 100%!')
+})
+
+const radioOptions = [
+ {
+ code: 'phone',
+ display: 'Phone number'
+ },
+ {
+ code: 'idNumber',
+ display: 'ID card number'
+ }
+]
+
+const getErrorMsg = (formikErrors, formikTouched, mutationError) => {
+ if (!formikErrors || !formikTouched) return null
+ if (mutationError) return 'Internal server error'
+ if (formikErrors.idType && formikTouched.idType) return formikErrors.idType
+ if (formikErrors.value && formikTouched.value) return formikErrors.value
+ if (formikErrors.discount && formikTouched.discount)
+ return formikErrors.discount
+ return null
+}
+
+const IndividualDiscountModal = ({
+ showModal,
+ setShowModal,
+ onClose,
+ creationError,
+ addDiscount
+}) => {
+ const classes = useStyles()
+
+ const handleAddDiscount = (idType, value, discount) => {
+ addDiscount({
+ variables: {
+ idType: idType,
+ value: value,
+ discount: parseInt(discount)
+ }
+ })
+ setShowModal(false)
+ }
+
+ const idTypeClass = (formikErrors, formikTouched) => ({
+ [classes.error]: formikErrors.idType && formikTouched.idType
+ })
+
+ return (
+ <>
+ {showModal && (
+