diff --git a/package.json b/package.json index 1c50742..58e82c0 100644 --- a/package.json +++ b/package.json @@ -51,6 +51,7 @@ "nodemailer": "^4.0.1", "q": "^1.5.1", "snyk": "^1.239.5", + "strip": "^3.0.0", "underscore": "^1.8.3", "validator": "^10.9.0" }, diff --git a/src/data/resolvers/mutations/messenger.ts b/src/data/resolvers/mutations/messenger.ts index 743cb6d..5b9be6f 100644 --- a/src/data/resolvers/mutations/messenger.ts +++ b/src/data/resolvers/mutations/messenger.ts @@ -1,3 +1,5 @@ +import * as strip from 'strip'; + import { Brands, Companies, Conversations, Customers, Integrations, Messages } from '../../../db/models'; import { IBrowserInfo, IVisitorContactInfoParams } from '../../../db/models/Customers'; @@ -16,6 +18,7 @@ export default { brandCode: string; email?: string; phone?: string; + code?: string; isUser?: boolean; companyData?: any; data?: any; @@ -23,7 +26,7 @@ export default { deviceToken?: string; }, ) { - const { brandCode, email, phone, isUser, companyData, data, cachedCustomerId, deviceToken } = args; + const { brandCode, email, phone, code, isUser, companyData, data, cachedCustomerId, deviceToken } = args; const customData = data; @@ -41,6 +44,7 @@ export default { cachedCustomerId, email, phone, + code, }); if (customer) { @@ -50,6 +54,7 @@ export default { doc: { email, phone, + code, isUser, deviceToken, }, @@ -63,6 +68,7 @@ export default { integrationId: integration._id, email, phone, + code, isUser, deviceToken, }, @@ -108,12 +114,14 @@ export default { ) { const { integrationId, customerId, conversationId, message, attachments } = args; + const conversationContent = strip(message || '').substring(0, 100); + // get or create conversation const conversation = await Conversations.getOrCreateConversation({ conversationId, integrationId, customerId, - content: message, + content: conversationContent, }); // create message @@ -132,7 +140,7 @@ export default { status: Conversations.getConversationStatuses().OPEN, // setting conversation's content to last message - content: message, + content: conversationContent, // Mark as unread readUserIds: [], diff --git a/src/data/schema.ts b/src/data/schema.ts index 1a0cbce..a8a6d5c 100755 --- a/src/data/schema.ts +++ b/src/data/schema.ts @@ -242,6 +242,7 @@ export const mutations = ` brandCode: String! email: String phone: String + code: String isUser: Boolean companyData: JSON diff --git a/src/db/factories.ts b/src/db/factories.ts index 7544277..f16fd68 100644 --- a/src/db/factories.ts +++ b/src/db/factories.ts @@ -141,6 +141,7 @@ export function customerFactory(params: ICustomerParams = {}) { }, urlVisits: params.urlVisits, deviceTokens: params.deviceToken || [], + code: faker.random.word(), }); return customer.save(); diff --git a/src/db/models/Customers.ts b/src/db/models/Customers.ts index 4e81e6c..8ff31df 100644 --- a/src/db/models/Customers.ts +++ b/src/db/models/Customers.ts @@ -6,6 +6,7 @@ import Integrations from './Integrations'; interface IGetCustomerParams { email?: string; phone?: string; + code?: string; cachedCustomerId?: string; } @@ -14,6 +15,7 @@ interface ICreateCustomerParams { email?: string; hasValidEmail?: boolean; phone?: string; + code?: string; isUser?: boolean; firstName?: string; lastName?: string; @@ -27,6 +29,7 @@ export interface IUpdateMessengerCustomerParams { doc: { email?: string; phone?: string; + code?: string; isUser?: boolean; deviceToken?: string; }; @@ -131,12 +134,15 @@ export const loadClass = () => { if (!nullValues.includes(customer.primaryEmail || '')) { score += 15; - searchText = searchText.concat(' ', customer.primaryEmail); } if (!nullValues.includes(customer.primaryPhone || '')) { score += 10; - searchText = searchText.concat(' ', customer.primaryPhone); + } + + if (!nullValues.includes(customer.code || '')) { + score += 10; + searchText = searchText.concat(' ', customer.code); } if (customer.visitorContactInfo != null) { @@ -164,26 +170,32 @@ export const loadClass = () => { /* * Get customer */ - public static getCustomer(params: IGetCustomerParams) { - const { email, phone, cachedCustomerId } = params; + public static async getCustomer(params: IGetCustomerParams) { + const { email, phone, code, cachedCustomerId } = params; + + let customer: ICustomerDocument; if (email) { - return Customers.findOne({ + customer = await Customers.findOne({ $or: [{ emails: { $in: [email] } }, { primaryEmail: email }], }); } - if (phone) { - return Customers.findOne({ + if (!customer && phone) { + customer = await Customers.findOne({ $or: [{ phones: { $in: [phone] } }, { primaryPhone: phone }], }); } - if (cachedCustomerId) { - return Customers.findOne({ _id: cachedCustomerId }); + if (!customer && code) { + customer = await Customers.findOne({ code }); + } + + if (!customer && cachedCustomerId) { + customer = await Customers.findOne({ _id: cachedCustomerId }); } - return null; + return customer; } /* diff --git a/src/db/models/definitions/boards.ts b/src/db/models/definitions/boards.ts index d8ee2fe..afa437e 100644 --- a/src/db/models/definitions/boards.ts +++ b/src/db/models/definitions/boards.ts @@ -19,6 +19,7 @@ export interface IItemCommonFields { assignedUserIds?: string[]; watchedUserIds?: string[]; notifiedUserIds?: string[]; + labelIds?: string[]; attachments?: any[]; stageId?: string; initialStageId?: string; @@ -28,6 +29,7 @@ export interface IItemCommonFields { createdAt?: Date; order?: number; searchText?: string; + priority?: string; } export interface IBoard extends ICommonFields { @@ -45,12 +47,13 @@ export interface IPipeline extends ICommonFields { memberIds?: string[]; bgColor?: string; watchedUserIds?: string[]; - // growth hack startDate?: Date; endDate?: Date; metric?: string; hackScoringType?: string; templateId?: string; + isCheckUser?: boolean; + excludeCheckUserIds?: string[]; } export interface IPipelineDocument extends IPipeline, Document { @@ -116,6 +119,7 @@ export const commonItemFieldsSchema = { description: field({ type: String, optional: true }), assignedUserIds: field({ type: [String] }), watchedUserIds: field({ type: [String] }), + labelIds: field({ type: [String] }), attachments: field({ type: [attachmentSchema] }), stageId: field({ type: String }), initialStageId: field({ type: String, optional: true }), @@ -125,6 +129,7 @@ export const commonItemFieldsSchema = { }), modifiedBy: field({ type: String }), searchText: field({ type: String, optional: true, index: true }), + priority: field({ type: String, optional: true }), }; export const boardSchema = schemaWrapper( @@ -156,6 +161,8 @@ export const pipelineSchema = new Schema({ enum: HACK_SCORING_TYPES.ALL, }), templateId: field({ type: String, optional: true }), + isCheckUser: field({ type: Boolean, optional: true }), + excludeCheckUserIds: field({ type: [String], optional: true }), ...commonFieldsSchema, }); diff --git a/src/db/models/definitions/checklists.ts b/src/db/models/definitions/checklists.ts new file mode 100644 index 0000000..0de2de6 --- /dev/null +++ b/src/db/models/definitions/checklists.ts @@ -0,0 +1,50 @@ +import { Document, Schema } from 'mongoose'; +import { ACTIVITY_CONTENT_TYPES } from './constants'; +import { field } from './utils'; + +export interface IChecklist { + contentType: string; + contentTypeId: string; + title: string; +} + +export interface IChecklistDocument extends IChecklist, Document { + _id: string; + createdUserId: string; + createdDate: Date; +} + +export interface IChecklistItem { + checklistId: string; + content: string; + isChecked: boolean; +} + +export interface IChecklistItemDocument extends IChecklistItem, Document { + _id: string; + createdUserId: string; + createdDate: Date; +} + +// Mongoose schemas ======================= + +export const checklistSchema = new Schema({ + _id: field({ pkey: true }), + contentType: field({ + type: String, + enum: ACTIVITY_CONTENT_TYPES.ALL, + }), + contentTypeId: field({ type: String }), + title: field({ type: String }), + createdUserId: field({ type: String }), + createdDate: field({ type: Date }), +}); + +export const checklistItemSchema = new Schema({ + _id: field({ pkey: true }), + checklistId: field({ type: String }), + content: field({ type: String }), + isChecked: field({ type: Boolean }), + createdUserId: field({ type: String }), + createdDate: field({ type: Date }), +}); diff --git a/src/db/models/definitions/constants.ts b/src/db/models/definitions/constants.ts index 42cf8ac..de7fbb8 100644 --- a/src/db/models/definitions/constants.ts +++ b/src/db/models/definitions/constants.ts @@ -59,9 +59,44 @@ export const KIND_CHOICES = { FACEBOOK_POST: 'facebook-post', GMAIL: 'gmail', NYLAS_GMAIL: 'nylas-gmail', + NYLAS_IMAP: 'nylas-imap', + NYLAS_OFFICE365: 'nylas-office365', + NYLAS_OUTLOOK: 'nylas-outlook', + NYLAS_YAHOO: 'nylas-yahoo', CALLPRO: 'callpro', + TWITTER_DM: 'twitter-dm', CHATFUEL: 'chatfuel', - ALL: ['messenger', 'lead', 'facebook-messenger', 'facebook-post', 'gmail', 'callpro', 'chatfuel', 'nylas-gmail'], + ALL: [ + 'messenger', + 'lead', + 'facebook-messenger', + 'facebook-post', + 'gmail', + 'callpro', + 'chatfuel', + 'nylas-gmail', + 'nylas-imap', + 'nylas-office365', + 'nylas-outlook', + 'nylas-yahoo', + 'twitter-dm', + ], +}; + +export const INTEGRATION_NAMES_MAP = { + messenger: 'Web messenger', + lead: 'Lead', + 'facebook-messenger': 'Facebook messenger', + 'facebook-post': 'Facebook post', + gmail: 'Gmail', + callpro: 'Call pro', + chatfuel: 'Chatfuel', + 'nylas-gmail': 'Gmail', + 'nylas-imap': 'Imap', + 'nylas-office365': 'Office 365', + 'nylas-outlook': 'Outook', + 'nylas-yahoo': 'Yahoo', + 'twitter-dm': 'Twitter dm', }; // messenger data availability constants @@ -94,6 +129,7 @@ export const ACTIVITY_TYPES = { CUSTOMER: 'customer', COMPANY: 'company', INTERNAL_NOTE: 'internal_note', + CHECKLIST: 'checklist', CONVERSATION: 'conversation', SEGMENT: 'segment', DEAL: 'deal', @@ -107,6 +143,7 @@ export const ACTIVITY_TYPES = { 'customer', 'company', 'internal_note', + 'checklist', 'conversation', 'segment', 'deal', diff --git a/src/db/models/definitions/customers.ts b/src/db/models/definitions/customers.ts index 1a39984..2aea5c9 100644 --- a/src/db/models/definitions/customers.ts +++ b/src/db/models/definitions/customers.ts @@ -76,6 +76,7 @@ export interface ICustomer { visitorContactInfo?: IVisitorContact; urlVisits?: any; deviceTokens?: string[]; + code?: string; } export interface ICustomerDocument extends ICustomer, Document { @@ -230,5 +231,6 @@ export const customerSchema = schemaWrapper( deviceTokens: field({ type: [String], default: [] }), searchText: field({ type: String, optional: true, index: true }), + code: field({ type: String, label: 'Code', optional: true }), }), ); diff --git a/src/db/models/definitions/growthHacks.ts b/src/db/models/definitions/growthHacks.ts index 58c5ac6..1e190af 100644 --- a/src/db/models/definitions/growthHacks.ts +++ b/src/db/models/definitions/growthHacks.ts @@ -7,7 +7,6 @@ export interface IGrowthHack extends IItemCommonFields { votedUserIds?: string[]; hackStages?: string; - priority?: string; reach?: number; impact?: number; confidence?: number; @@ -25,7 +24,6 @@ export const growthHackSchema = new Schema({ votedUserIds: field({ type: [String], optional: true }), hackStages: field({ type: [String], optional: true }), - priority: field({ type: String, optional: true }), reach: field({ type: Number, default: 0, optional: true }), impact: field({ type: Number, default: 0, optional: true }), confidence: field({ type: Number, default: 0, optional: true }), diff --git a/src/db/models/definitions/integrations.ts b/src/db/models/definitions/integrations.ts index df1a610..c933b7f 100644 --- a/src/db/models/definitions/integrations.ts +++ b/src/db/models/definitions/integrations.ts @@ -98,11 +98,14 @@ export interface IIntegration { leadData?: ILeadData; messengerData?: IMessengerData; uiOptions?: IUiOptions; + isActive?: boolean; } export interface IIntegrationDocument extends IIntegration, Document { _id: string; createdUserId: string; + // TODO remove + formData?: ILeadData; leadData?: ILeadDataDocument; messengerData?: IMessengerDataDocument; uiOptions?: IUiOptionsDocument; @@ -273,4 +276,5 @@ export const integrationSchema = new Schema({ formData: field({ type: leadDataSchema }), messengerData: field({ type: messengerDataSchema }), uiOptions: field({ type: uiOptionsSchema }), + isActive: field({ type: Boolean, optional: true, default: true }), }); diff --git a/src/db/models/definitions/pipelineLabels.ts b/src/db/models/definitions/pipelineLabels.ts new file mode 100644 index 0000000..00c878d --- /dev/null +++ b/src/db/models/definitions/pipelineLabels.ts @@ -0,0 +1,27 @@ +import { Document, Schema } from 'mongoose'; +import { field } from './utils'; + +export interface IPipelineLabel { + name: string; + colorCode: string; + pipelineId: string; + createdBy?: string; + createdDate?: Date; +} + +export interface IPipelineLabelDocument extends IPipelineLabel, Document { + _id: string; +} + +export const pipelineLabelSchema = new Schema({ + _id: field({ pkey: true }), + + name: field({ type: String }), + colorCode: field({ type: String }), + pipelineId: field({ type: String }), + createdBy: field({ type: String }), + createdAt: field({ + type: Date, + default: new Date(), + }), +}); diff --git a/src/db/models/definitions/tasks.ts b/src/db/models/definitions/tasks.ts index 1f4d866..081d812 100644 --- a/src/db/models/definitions/tasks.ts +++ b/src/db/models/definitions/tasks.ts @@ -1,18 +1,11 @@ import { Document, Schema } from 'mongoose'; import { commonItemFieldsSchema, IItemCommonFields } from './boards'; -import { field } from './utils'; -export interface ITask extends IItemCommonFields { - priority?: string; -} - -export interface ITaskDocument extends ITask, Document { +export interface ITaskDocument extends IItemCommonFields, Document { _id: string; } // Mongoose schemas ======================= export const taskSchema = new Schema({ ...commonItemFieldsSchema, - - priority: field({ type: String, optional: true }), }); diff --git a/src/db/models/definitions/tickets.ts b/src/db/models/definitions/tickets.ts index 1f26d5a..a3f0d8c 100644 --- a/src/db/models/definitions/tickets.ts +++ b/src/db/models/definitions/tickets.ts @@ -3,7 +3,6 @@ import { commonItemFieldsSchema, IItemCommonFields } from './boards'; import { field } from './utils'; export interface ITicket extends IItemCommonFields { - priority?: string; source?: string; } @@ -15,6 +14,5 @@ export interface ITicketDocument extends ITicket, Document { export const ticketSchema = new Schema({ ...commonItemFieldsSchema, - priority: field({ type: String }), source: field({ type: String }), }); diff --git a/yarn.lock b/yarn.lock index 684579a..e24bcd5 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7878,6 +7878,11 @@ strip-json-comments@^2.0.0, strip-json-comments@~2.0.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= +strip@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/strip/-/strip-3.0.0.tgz#750fc933152a7d35af0b7420e651789b914cc35e" + integrity sha1-dQ/JMxUqfTWvC3Qg5lF4m5FMw14= + subarg@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/subarg/-/subarg-1.0.0.tgz#f62cf17581e996b48fc965699f54c06ae268b8d2"