Chore: Add basic screen and toggle

Chore: form skeleton

Feat: wizard step 1 and 2

Feat: toggle button group for formik

Feat: select input type Form and styling

Feat: text entry page

Feat: Choice list and CSS

Fix: scroll to bottom on Add choice button click

Feat: format data at end of wizard

Feat: wizard toggle button and background blur

Feat: data table for custom info requests

Feat: editing and deleting custom info request

Feat: add icons

Fix: Wizard changes

Feat: custom requests migrations

Feat: fetch custom info requests

Feat: add mutations

Feat: add custom request option in trigger wizard

Feat: show customrequests on table

Feat: Triggers page code refactor

Feat: integrate custom info requests on Customer graphql type

Feat: Show custom info requests on user page

Fix: use normal table instead of datatable

Feat: modal for custom information request details

Feat: poller returns custom request information details

Feat: send customer custom info requests to machine

Chore: add field CustomInfoRequestsData on customer updates

Feat: customer custom info request data saving

Chore: variable name changes and lots of fixes

Feat: remove default value in query, sort request on customer profile

Signed-off-by: csrapr <26280794+csrapr@users.noreply.github.com>

Fix: return promise when array of ids is empty

Feat: TitleSection can receive more than one button
This commit is contained in:
csrapr 2021-04-16 18:03:53 +01:00 committed by Sérgio Salgado
parent 3de2bb3d86
commit ba8cac60f8
48 changed files with 2424 additions and 146 deletions

View file

@ -43,6 +43,7 @@ function add (customer) {
.then(populateOverrideUsernames)
.then(computeStatus)
.then(populateDailyVolume)
.then(getCustomInfoRequestsData)
.then(camelize)
}
@ -61,6 +62,7 @@ function get (phone) {
const sql = 'select * from customers where phone=$1'
return db.oneOrNone(sql, [phone])
.then(populateDailyVolume)
.then(getCustomInfoRequestsData)
.then(camelize)
}
@ -90,6 +92,7 @@ function update (id, data, userToken, txId) {
.then(populateOverrideUsernames)
.then(computeStatus)
.then((it) => populateDailyVolume(it, txId))
.then(getCustomInfoRequestsData)
.then(camelize)
}
@ -315,6 +318,7 @@ function getById (id, userToken) {
.then(populateOverrideUsernames)
.then(computeStatus)
.then(populateDailyVolume)
.then(getCustomInfoRequestsData)
.then(camelize)
}
@ -606,6 +610,7 @@ function batch () {
return populateOverrideUsernames(customer)
.then(computeStatus)
.then(populateDailyVolume)
.then(getCustomInfoRequestsData)
.then(camelize)
}, customers)))
}
@ -1001,6 +1006,12 @@ function removeCustomField (customerId, fieldId) {
}))
}
function getCustomInfoRequestsData (customer) {
if (!customer) return
const sql = `SELECT * FROM customers_custom_info_requests WHERE customer_id = $1`
return db.any(sql, [customer.id]).then(res => _.set('custom_info_request_data', res, customer))
}
module.exports = {
add,
get,

View file

@ -0,0 +1,28 @@
const queries = require('../../services/customInfoRequests')
const DataLoader = require('dataloader')
const customerCustomInfoRequestsLoader = new DataLoader(ids => queries.batchGetAllCustomInfoRequestsForCustomer(ids), { cache: false })
const customInfoRequestLoader = new DataLoader(ids => queries.batchGetCustomInfoRequest(ids), { cache: false })
const resolvers = {
Customer: {
customInfoRequests: parent => customerCustomInfoRequestsLoader.load(parent.id)
},
CustomRequestData: {
customInfoRequest: parent => customInfoRequestLoader.load(parent.infoRequestId)
},
Query: {
customInfoRequests: (...[, { onlyEnabled }]) => queries.getCustomInfoRequests(onlyEnabled),
customerCustomInfoRequests: (...[, { customerId }]) => queries.getAllCustomInfoRequestsForCustomer(customerId),
customerCustomInfoRequest: (...[, { customerId, infoRequestId }]) => queries.getCustomInfoRequestForCustomer(customerId, infoRequestId)
},
Mutation: {
insertCustomInfoRequest: (...[, { customRequest }]) => queries.addCustomInfoRequest(customRequest),
removeCustomInfoRequest: (...[, { id }]) => queries.removeCustomInfoRequest(id),
editCustomInfoRequest: (...[, { id, customRequest }]) => queries.editCustomInfoRequest(id, customRequest),
setAuthorizedCustomRequest: (...[, { customerId, infoRequestId, isAuthorized }]) => queries.setAuthorizedCustomRequest(customerId, infoRequestId, isAuthorized)
}
}
module.exports = resolvers

View file

@ -6,6 +6,7 @@ const cashbox = require('./cashbox.resolver')
const config = require('./config.resolver')
const currency = require('./currency.resolver')
const customer = require('./customer.resolver')
const customInfoRequests = require('./customInfoRequests.resolver')
const funding = require('./funding.resolver')
const log = require('./log.resolver')
const loyalty = require('./loyalty.resolver')
@ -28,6 +29,7 @@ const resolvers = [
config,
currency,
customer,
customInfoRequests,
funding,
log,
loyalty,

View file

@ -0,0 +1,54 @@
const { gql } = require('apollo-server-express')
const typeDef = gql`
type CustomInfoRequest {
id: ID!,
enabled: Boolean,
customRequest: JSON
}
input CustomRequestInputField {
choiceList: [String]
constraintType: String
type: String
numDigits: String
label1: String
label2: String
}
input CustomRequestInputScreen {
text: String
title: String
}
input CustomRequestInput {
name: String
input: CustomRequestInputField
screen1: CustomRequestInputScreen
screen2: CustomRequestInputScreen
}
type CustomRequestData {
customerId: ID
infoRequestId: ID
approved: Boolean
customerData: JSON
customInfoRequest: CustomInfoRequest
}
type Query {
customInfoRequests(onlyEnabled: Boolean): [CustomInfoRequest] @auth
customerCustomInfoRequests(customerId: ID!): [CustomRequestData] @auth
customerCustomInfoRequest(customerId: ID!, infoRequestId: ID!): CustomRequestData @auth
}
type Mutation {
insertCustomInfoRequest(customRequest: CustomRequestInput!): CustomInfoRequest @auth
removeCustomInfoRequest(id: ID!): CustomInfoRequest @auth
editCustomInfoRequest(id: ID!, customRequest: CustomRequestInput!): CustomInfoRequest @auth
setAuthorizedCustomRequest(customerId: ID!, infoRequestId: ID!, isAuthorized: Boolean!): Boolean @auth
}
`
module.exports = typeDef

View file

@ -40,6 +40,7 @@ const typeDef = gql`
transactions: [Transaction]
subscriberInfo: JSONObject
customFields: [CustomerCustomField]
customInfoRequests: [CustomRequestData]
}
input CustomerInput {

View file

@ -6,6 +6,7 @@ const cashbox = require('./cashbox.type')
const config = require('./config.type')
const currency = require('./currency.type')
const customer = require('./customer.type')
const customInfoRequests = require('./customInfoRequests.type')
const funding = require('./funding.type')
const log = require('./log.type')
const loyalty = require('./loyalty.type')
@ -28,6 +29,7 @@ const types = [
config,
currency,
customer,
customInfoRequests,
funding,
log,
loyalty,

View file

@ -0,0 +1,122 @@
const db = require('../../db')
const uuid = require('uuid')
const _ = require('lodash/fp')
const pgp = require('pg-promise')()
const getCustomInfoRequests = (onlyEnabled = false) => {
const sql = onlyEnabled
? `SELECT * FROM custom_info_requests WHERE enabled = true ORDER BY custom_request->>'name'`
: `SELECT * FROM custom_info_requests ORDER BY custom_request->>'name'`
return db.any(sql).then(res => {
return res.map(item => ({
id: item.id,
enabled: item.enabled,
customRequest: item.custom_request
}))
})
}
const addCustomInfoRequest = (customRequest) => {
const sql = 'INSERT INTO custom_info_requests (id, custom_request) VALUES ($1, $2)'
const id = uuid.v4()
return db.none(sql, [id, customRequest]).then(() => ({ id }))
}
const removeCustomInfoRequest = (id) => {
return db.none('UPDATE custom_info_requests SET enabled = false WHERE id = $1', [id]).then(() => ({ id }))
}
const editCustomInfoRequest = (id, customRequest) => {
return db.none('UPDATE custom_info_requests SET custom_request = $1 WHERE id=$2', [customRequest, id]).then(() => ({ id, customRequest }))
}
const getAllCustomInfoRequestsForCustomer = (customerId) => {
const sql = `SELECT * FROM customers_custom_info_requests WHERE customer_id = $1`
return db.any(sql, [customerId]).then(res => res.map(item => ({
customerId: item.customer_id,
infoRequestId: item.info_request_id,
approved: item.approved,
customerData: item.customer_data
})))
}
const getCustomInfoRequestForCustomer = (customerId, infoRequestId) => {
const sql = `SELECT * FROM customers_custom_info_requests WHERE customer_id = $1 AND info_request_id = $2`
return db.one(sql, [customerId, infoRequestId]).then(item => {
return {
customerId: item.customer_id,
infoRequestId: item.info_request_id,
approved: item.approved,
customerData: item.customer_data
}
})
}
const batchGetAllCustomInfoRequestsForCustomer = (customerIds) => {
const sql = `SELECT * FROM customers_custom_info_requests WHERE customer_id IN ($1^)`
return db.any(sql, [_.map(pgp.as.text, customerIds).join(',')]).then(res => {
const map = _.groupBy('customer_id', res)
return customerIds.map(id => {
const items = map[id] || []
return items.map(item => ({
customerId: item.customer_id,
infoRequestId: item.info_request_id,
approved: item.approved,
customerData: item.customer_data
}))
})
})
}
const getCustomInfoRequest = (infoRequestId) => {
const sql = `SELECT * FROM custom_info_requests WHERE id = $1`
return db.one(sql, [infoRequestId]).then(item => ({
id: item.id,
enabled: item.enabled,
customRequest: item.custom_request
}))
}
const batchGetCustomInfoRequest = (infoRequestIds) => {
if (infoRequestIds.length === 0) return Promise.resolve([])
const sql = `SELECT * FROM custom_info_requests WHERE id IN ($1^)`
return db.any(sql, [_.map(pgp.as.text, infoRequestIds).join(',')]).then(res => {
const map = _.groupBy('id', res)
return infoRequestIds.map(id => {
const item = map[id][0] // since id is primary key the array always has 1 element
return {
id: item.id,
enabled: item.enabled,
customRequest: item.custom_request
}
})
})
}
const setAuthorizedCustomRequest = (customerId, infoRequestId, isAuthorized) => {
const sql = `UPDATE customers_custom_info_requests SET approved = $1 WHERE customer_id = $2 AND info_request_id = $3`
return db.none(sql, [isAuthorized, customerId, infoRequestId]).then(() => true)
}
const setCustomerData = (customerId, infoRequestId, data) => {
const sql = `
INSERT INTO customers_custom_info_requests (customer_id, info_request_id, customer_data)
VALUES ($1, $2, $3)
ON CONFLICT (customer_id, info_request_id)
DO UPDATE SET customer_data = $3, approved = null`
return db.none(sql, [customerId, infoRequestId, data])
}
module.exports = {
getCustomInfoRequests,
addCustomInfoRequest,
removeCustomInfoRequest,
editCustomInfoRequest,
getAllCustomInfoRequestsForCustomer,
getCustomInfoRequestForCustomer,
batchGetAllCustomInfoRequestsForCustomer,
getCustomInfoRequest,
batchGetCustomInfoRequest,
setAuthorizedCustomRequest,
setCustomerData
}

View file

@ -18,6 +18,13 @@ const { getTx } = require('../new-admin/services/transactions.js')
const { getCustomerById } = require('../customers')
const machineLoader = require('../machine-loader')
const { loadLatestConfig } = require('../new-settings-loader')
const customInfoRequestQueries = require('../new-admin/services/customInfoRequests')
function updateCustomerCustomInfoRequest (customerId, dataToSave, req, res) {
return customInfoRequestQueries.setCustomerData(customerId, dataToSave.info_request_id, dataToSave)
.then(() => customers.getById(customerId))
.then(customer => respond(req, res, { customer }))
}
function updateCustomer (req, res, next) {
const id = req.params.id
@ -27,6 +34,10 @@ function updateCustomer (req, res, next) {
const triggers = configManager.getTriggers(req.settings.config)
const compatTriggers = complianceTriggers.getBackwardsCompatibleTriggers(triggers)
if (patch.customRequestPatch) {
return updateCustomerCustomInfoRequest(id, patch.dataToSave, req, res).catch(next)
}
customers.getById(id)
.then(customer => {
if (!customer) { throw httpError('Not Found', 404) }

View file

@ -10,6 +10,7 @@ const plugins = require('../plugins')
const semver = require('semver')
const state = require('../middlewares/state')
const version = require('../../package.json').version
const customRequestQueries = require('../new-admin/services/customInfoRequests')
const urlsToPing = [
`us.archive.ubuntu.com`,
@ -37,6 +38,24 @@ const createTerms = terms => (terms.active && terms.text) ? ({
cancel: terms.cancelButtonText
}) : null
const buildTriggers = (allTriggers) => {
const normalTriggers = []
const customTriggers = _.filter(o => {
if (o.customInfoRequestId === '') normalTriggers.push(o)
return o.customInfoRequestId !== ''
}, allTriggers)
return _.flow([_.map(_.get('customInfoRequestId')), customRequestQueries.batchGetCustomInfoRequest])(customTriggers)
.then(res => {
res.forEach((details, index) => {
// make sure we aren't attaching the details to the wrong trigger
if (customTriggers[index].customInfoRequestId !== details.id) return
customTriggers[index] = { ...customTriggers[index], customInfoRequest: details }
})
return [...normalTriggers, ...customTriggers]
})
}
function poll (req, res, next) {
const machineVersion = req.query.version
const machineModel = req.query.model
@ -54,8 +73,8 @@ function poll (req, res, next) {
const pi = plugins(settings, deviceId)
const hasLightning = checkHasLightning(settings)
const triggers = configManager.getTriggers(settings.config)
const triggersAutomation = configManager.getTriggersAutomation(settings.config)
const triggersPromise = buildTriggers(configManager.getTriggers(settings.config))
const operatorInfo = configManager.getOperatorInfo(settings.config)
const machineInfo = { deviceId: req.deviceId, deviceName: req.deviceName }
@ -65,8 +84,8 @@ function poll (req, res, next) {
state.pids[operatorId] = { [deviceId]: { pid, ts: Date.now() } }
return pi.pollQueries(serialNumber, deviceTime, req.query, machineVersion, machineModel)
.then(results => {
return Promise.all([pi.pollQueries(serialNumber, deviceTime, req.query, machineVersion, machineModel), triggersPromise])
.then(([results, triggers]) => {
const cassettes = results.cassettes
const reboot = pid && state.reboots?.[operatorId]?.[deviceId] === pid