From 847b87ecb08baa3cc3a098d61e4aab85a5cc1914 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?S=C3=A9rgio=20Salgado?= Date: Wed, 28 Jul 2021 02:04:21 +0100 Subject: [PATCH] feat: use customer Autocomplete on discount creation --- lib/loyalty.js | 49 +--------- .../graphql/resolvers/loyalty.resolver.js | 2 +- lib/new-admin/graphql/types/loyalty.type.js | 17 +--- lib/plugins.js | 2 + .../1626891847835-add-individual-discounts.js | 6 +- .../LoyaltyPanel/IndividualDiscount.styles.js | 22 +++-- .../LoyaltyPanel/IndividualDiscountModal.js | 87 ++++++---------- .../pages/LoyaltyPanel/IndividualDiscounts.js | 98 ++++++++----------- 8 files changed, 94 insertions(+), 189 deletions(-) diff --git a/lib/loyalty.js b/lib/loyalty.js index 606c70d7..7883b907 100644 --- a/lib/loyalty.js +++ b/lib/loyalty.js @@ -32,20 +32,14 @@ function getAvailableIndividualDiscounts () { const sql = `SELECT * from individual_discounts WHERE soft_deleted=false` return db.any(sql).then(res => _.map(it => ({ id: it.id, - idType: _.camelCase(it.identification), - value: it.value, + customerId: it.customer_id, discount: it.discount }), 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)` - return db.none(sql, [uuid.v4(), idTypes[idType], value, discount]) +function createIndividualDiscount (customerId, discount) { + const sql = `INSERT INTO individual_discounts (id, customer_id, discount) VALUES ($1, $2, $3)` + return db.none(sql, [uuid.v4(), customerId, discount]) } function deleteIndividualDiscount (id) { @@ -53,38 +47,6 @@ function deleteIndividualDiscount (id) { return db.none(sql, [id]) } -function getCustomersWithDiscounts (discounts) { - let phoneNumbers = [] - let idCardNumbers = [] - - _.each(it => { - switch (it.idType) { - case 'phone': - phoneNumbers.push(it.value) - break - case 'idNumber': - idCardNumbers.push(it.value) - break - default: - break - } - }, discounts) - - if (_.isEmpty(phoneNumbers) && _.isEmpty(idCardNumbers)) { - return Promise.resolve([]) - } - - const phoneNumbersSql = _.map(pgp.as.text, phoneNumbers).join(',') - const idCardNumbersSql = _.map(pgp.as.text, idCardNumbers).join(',') - - const hasPhoneNumbers = !_.isEmpty(phoneNumbers) - const hasIDNumbers = !_.isEmpty(idCardNumbers) - - const sql = `SELECT * FROM customers WHERE ${hasPhoneNumbers ? `phone IN ($1^)` : ``} ${hasPhoneNumbers && hasIDNumbers ? `OR` : ``} ${hasIDNumbers ? `id_card_data_number IN ($2^)` : ``}` - return db.any(sql, [phoneNumbersSql, idCardNumbersSql]) - .then(res => _.map(it => it ? _.mapKeys(_.camelCase, it) : null, res)) -} - module.exports = { getAvailablePromoCodes, getPromoCode, @@ -93,6 +55,5 @@ module.exports = { getNumberOfAvailablePromoCodes, getAvailableIndividualDiscounts, createIndividualDiscount, - deleteIndividualDiscount, - getCustomersWithDiscounts + deleteIndividualDiscount } diff --git a/lib/new-admin/graphql/resolvers/loyalty.resolver.js b/lib/new-admin/graphql/resolvers/loyalty.resolver.js index bebf4c08..9a12a89e 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: (...[, { idType, value, discount }]) => loyalty.createIndividualDiscount(idType, value, discount), + createIndividualDiscount: (...[, { customerId, discount }]) => loyalty.createIndividualDiscount(customerId, 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 3e8aa648..c353f7d1 100644 --- a/lib/new-admin/graphql/types/loyalty.type.js +++ b/lib/new-admin/graphql/types/loyalty.type.js @@ -3,23 +3,10 @@ const { gql } = require('apollo-server-express') const typeDef = gql` type IndividualDiscount { id: ID! - idType: DiscountIdentificationType - value: String! + customerId: ID! discount: Int } - input IndividualDiscountInput { - id: ID - idType: DiscountIdentificationType - value: String - discount: Int - } - - enum DiscountIdentificationType { - phone - idNumber - } - type PromoCode { id: ID! code: String! @@ -34,7 +21,7 @@ const typeDef = gql` type Mutation { createPromoCode(code: String!, discount: Int!): PromoCode @auth deletePromoCode(codeId: ID!): PromoCode @auth - createIndividualDiscount(idType: DiscountIdentificationType!, value: String!, discount: Int!): IndividualDiscount @auth + createIndividualDiscount(customerId: ID!, discount: Int!): IndividualDiscount @auth deleteIndividualDiscount(discountId: ID!): IndividualDiscount @auth } ` diff --git a/lib/plugins.js b/lib/plugins.js index 4c7dd893..427fa7a8 100644 --- a/lib/plugins.js +++ b/lib/plugins.js @@ -260,6 +260,8 @@ function plugins (settings, deviceId) { const areThereAvailablePromoCodes = arr[arr.length - 2] > 0 const individualDiscounts = arr[arr.length - 1] + console.log('individualDiscounts', individualDiscounts) + return { cassettes, rates: buildRates(tickers), diff --git a/migrations/1626891847835-add-individual-discounts.js b/migrations/1626891847835-add-individual-discounts.js index 28539a4d..8d4002a4 100644 --- a/migrations/1626891847835-add-individual-discounts.js +++ b/migrations/1626891847835-add-individual-discounts.js @@ -2,15 +2,13 @@ var db = require('./db') exports.up = function (next) { var sql = [ - `CREATE TYPE individual_discount_identification_type AS ENUM('phone', 'id_number')`, `CREATE TABLE individual_discounts ( id UUID PRIMARY KEY, - identification individual_discount_identification_type NOT NULL, - value TEXT NOT NULL, + customer_id UUID NOT NULL REFERENCES customers(id), discount SMALLINT NOT NULL, soft_deleted BOOLEAN DEFAULT false )`, - `CREATE UNIQUE INDEX uq_individual_discount ON individual_discounts (identification, value) WHERE NOT soft_deleted` + `CREATE UNIQUE INDEX uq_individual_discount ON individual_discounts (customer_id) 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 index ff7c95cb..b7918df7 100644 --- a/new-lamassu-admin/src/pages/LoyaltyPanel/IndividualDiscount.styles.js +++ b/new-lamassu-admin/src/pages/LoyaltyPanel/IndividualDiscount.styles.js @@ -6,26 +6,28 @@ const styles = { flexDirection: 'row', alignItems: 'center', '& > *:first-child': { - marginRight: 10 + marginLeft: 0 + }, + '& > *': { + marginLeft: 6 + }, + '& > *:nth-child(3)': { + marginLeft: 15 } }, form: { display: 'flex', flexDirection: 'column', height: '100%', + '& > *:first-child': { + marginTop: 10 + }, '& > *': { marginBottom: 20 } }, - radioGroup: { - display: 'flex', - flexDirection: 'row', - '& > *': { - marginLeft: 15 - }, - '& > *:first-child': { - marginLeft: 0 - } + customerAutocomplete: { + width: 350 }, discountRateWrapper: { display: 'flex', diff --git a/new-lamassu-admin/src/pages/LoyaltyPanel/IndividualDiscountModal.js b/new-lamassu-admin/src/pages/LoyaltyPanel/IndividualDiscountModal.js index c9cc9f9a..3b59b8e1 100644 --- a/new-lamassu-admin/src/pages/LoyaltyPanel/IndividualDiscountModal.js +++ b/new-lamassu-admin/src/pages/LoyaltyPanel/IndividualDiscountModal.js @@ -1,6 +1,6 @@ import { makeStyles } from '@material-ui/core/styles' -import classNames from 'classnames' import { Form, Formik, Field } from 'formik' +import * as R from 'ramda' import React from 'react' import * as Yup from 'yup' @@ -8,11 +8,7 @@ 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 { NumberInput, Autocomplete } from 'src/components/inputs/formik' import { H3, TL1, P } from 'src/components/typography' import styles from './IndividualDiscount.styles' @@ -20,42 +16,23 @@ import styles from './IndividualDiscount.styles' const useStyles = makeStyles(styles) const initialValues = { - idType: '', - value: '', + customer: '', 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!'), + customer: Yup.string().required('A customer is required!'), 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.customer && formikTouched.customer) + return formikErrors.customer if (formikErrors.discount && formikTouched.discount) return formikErrors.discount return null @@ -66,25 +43,21 @@ const IndividualDiscountModal = ({ setShowModal, onClose, creationError, - addDiscount + addDiscount, + customers }) => { const classes = useStyles() - const handleAddDiscount = (idType, value, discount) => { + const handleAddDiscount = (customer, discount) => { addDiscount({ variables: { - idType: idType, - value: value, + customerId: customer, discount: parseInt(discount) } }) setShowModal(false) } - const idTypeClass = (formikErrors, formikTouched) => ({ - [classes.error]: formikErrors.idType && formikTouched.idType - }) - return ( <> {showModal && ( @@ -100,33 +73,29 @@ const IndividualDiscountModal = ({ validateOnChange={false} initialValues={initialValues} validationSchema={validationSchema} - onSubmit={({ idType, value, discount }) => { - handleAddDiscount(idType, value, discount) + onSubmit={({ customer, discount }) => { + handleAddDiscount(customer, discount) }}> - {({ values, errors, touched }) => ( + {({ errors, touched }) => (
-
-

- Select customer identification option -

+
({ + code: it.id, + display: `${it.idCardData.firstName ?? ``}${ + it.idCardData.firstName && it.idCardData.lastName + ? ` ` + : `` + }${it.idCardData.lastName ?? ``} (${it.phone})` + }))(customers)} + labelProp="display" + valueProp="code" />
-

Define discount rate

diff --git a/new-lamassu-admin/src/pages/LoyaltyPanel/IndividualDiscounts.js b/new-lamassu-admin/src/pages/LoyaltyPanel/IndividualDiscounts.js index 0acaf876..a7682708 100644 --- a/new-lamassu-admin/src/pages/LoyaltyPanel/IndividualDiscounts.js +++ b/new-lamassu-admin/src/pages/LoyaltyPanel/IndividualDiscounts.js @@ -1,4 +1,4 @@ -import { useQuery, useLazyQuery, useMutation } from '@apollo/react-hooks' +import { useQuery, useMutation } from '@apollo/react-hooks' import { makeStyles, Box } from '@material-ui/core' import gql from 'graphql-tag' import * as R from 'ramda' @@ -21,8 +21,7 @@ const GET_INDIVIDUAL_DISCOUNTS = gql` query individualDiscounts { individualDiscounts { id - idType - value + customerId discount } } @@ -37,27 +36,20 @@ const DELETE_DISCOUNT = gql` ` const CREATE_DISCOUNT = gql` - mutation createIndividualDiscount( - $idType: DiscountIdentificationType! - $value: String! - $discount: Int! - ) { - createIndividualDiscount( - idType: $idType - value: $value - discount: $discount - ) { + mutation createIndividualDiscount($customerId: ID!, $discount: Int!) { + createIndividualDiscount(customerId: $customerId, discount: $discount) { id } } ` -const GET_CUSTOMERS_WITH_DISCOUNTS = gql` - query getCustomersWithDiscounts($discounts: [IndividualDiscountInput]!) { - getCustomersWithDiscounts(discounts: $discounts) { +const GET_CUSTOMERS = gql` + { + customers { id phone idCardData + phone } } ` @@ -72,26 +64,9 @@ const IndividualDiscounts = () => { const [showModal, setShowModal] = useState(false) const toggleModal = () => setShowModal(!showModal) - const [ - getCustomers, - { data: customerData, loading: customerLoading } - ] = useLazyQuery(GET_CUSTOMERS_WITH_DISCOUNTS) - - const { data: discountResponse, loading } = useQuery( - GET_INDIVIDUAL_DISCOUNTS, - { - onCompleted: res => { - const discounts = R.map(it => - R.pick(['id', 'idType', 'value', 'discount'])(it) - )(res.individualDiscounts) - - return getCustomers({ - variables: { - discounts: discounts - } - }) - } - } + const { data: discountResponse, loading } = useQuery(GET_INDIVIDUAL_DISCOUNTS) + const { data: customerData, loading: customerLoading } = useQuery( + GET_CUSTOMERS ) const [createDiscount, { error: creationError }] = useMutation( @@ -101,6 +76,11 @@ const IndividualDiscounts = () => { } ) + const getCustomer = id => { + const customers = R.path(['customers'])(customerData) + return R.find(R.propEq('id', id))(customers) + } + const [deleteDiscount] = useMutation(DELETE_DISCOUNT, { onError: ({ message }) => { const errorMessage = message ?? 'Error while deleting row' @@ -110,25 +90,27 @@ const IndividualDiscounts = () => { refetchQueries: () => ['individualDiscounts'] }) - const findCustomer = (customers = [], idType, value) => - R.find(it => - idType === 'phone' - ? it.phone === value - : it.idCardData.documentNumber === value - )(customers) - const elements = [ { header: 'Identification', width: 312, textAlign: 'left', size: 'sm', - view: t => ( -
- {t.idType === 'phone' ? : } - {t.value} -
- ) + view: t => { + const customer = getCustomer(t.customerId) + return ( +
+ + {customer.phone} + {customer.idCardData?.documentNumber && ( + <> + + {customer.idCardData.documentNumber} + + )} +
+ ) + } }, { header: 'Name', @@ -136,14 +118,17 @@ const IndividualDiscounts = () => { textAlign: 'left', size: 'sm', view: t => { - const customer = findCustomer( - customerData?.getCustomersWithDiscounts, - t.idType, - t.value - ) - if (R.isNil(customer)) return <>{'-'} + const customer = getCustomer(t.customerId) + if (R.isNil(customer.idCardData)) { + return <>{'-'} + } + return ( - <>{`${customer.idCardData.firstName} ${customer.idCardData.lastName}`} + <>{`${customer.idCardData.firstName ?? ``}${ + customer.idCardData.firstName && customer.idCardData.lastName + ? ` ` + : `` + }${customer.idCardData.lastName ?? ``}`} ) } }, @@ -228,6 +213,7 @@ const IndividualDiscounts = () => { }} creationError={creationError} addDiscount={createDiscount} + customers={R.path(['customers'])(customerData)} /> )