1:HL["/_next/static/css/6bd36201de36b157.css",{"as":"style"}] 0:["SjwqWs581guB4LhqFq0Oc",[[["",{"children":["sdks",{"children":["__PAGE__",{}]}]},"$undefined","$undefined",true],"$L2",[[["$","link","0",{"rel":"stylesheet","href":"/_next/static/css/6bd36201de36b157.css","precedence":"next"}]],"$L3"]]]] 2:[null,"$L4",null] 3:[["$","meta","0",{"charSet":"utf-8"}],["$","title","1",{"children":"Development - Protocol API Reference"}],["$","meta","2",{"name":"description","content":"Protocol offers fine-tuned JavaScript, Ruby, PHP, Python, and Go libraries to make your life easier and give you the best experience when consuming the API."}],["$","meta","3",{"name":"viewport","content":"width=device-width, initial-scale=1"}],["$","link","4",{"rel":"icon","href":"/favicon.ico","type":"image/x-icon","sizes":"256x256"}]] 5:I{"id":4129,"chunks":["9982:static/chunks/93854f56-9ae0c09bcc02b636.js","2420:static/chunks/9081a741-dd8e5993991f401c.js","6685:static/chunks/6685-a937dd3e2b88ed12.js","2435:static/chunks/2435-ac4b486d27c0bea1.js","6021:static/chunks/6021-3dfd29e59407678e.js","3222:static/chunks/3222-150407128078c3bc.js","1859:static/chunks/1859-ad392470f22235e6.js","5170:static/chunks/5170-d43543fb1d3902f8.js","3185:static/chunks/app/layout-0bb2a92c31dce64c.js"],"name":"Providers","async":false} 6:I{"id":7757,"chunks":["9982:static/chunks/93854f56-9ae0c09bcc02b636.js","2420:static/chunks/9081a741-dd8e5993991f401c.js","6685:static/chunks/6685-a937dd3e2b88ed12.js","2435:static/chunks/2435-ac4b486d27c0bea1.js","6021:static/chunks/6021-3dfd29e59407678e.js","3222:static/chunks/3222-150407128078c3bc.js","1859:static/chunks/1859-ad392470f22235e6.js","5170:static/chunks/5170-d43543fb1d3902f8.js","3185:static/chunks/app/layout-0bb2a92c31dce64c.js"],"name":"Layout","async":false} 7:I{"id":7767,"chunks":["2272:static/chunks/webpack-9c39eedf017990e3.js","2971:static/chunks/fd9d1056-0ec2db3eb7ac1547.js","596:static/chunks/596-aaaa00efe9da3984.js"],"name":"default","async":false} 8:I{"id":7920,"chunks":["2272:static/chunks/webpack-9c39eedf017990e3.js","2971:static/chunks/fd9d1056-0ec2db3eb7ac1547.js","596:static/chunks/596-aaaa00efe9da3984.js"],"name":"default","async":false} 9:I{"id":6685,"chunks":["6685:static/chunks/6685-a937dd3e2b88ed12.js","2435:static/chunks/2435-ac4b486d27c0bea1.js","6021:static/chunks/6021-3dfd29e59407678e.js","3222:static/chunks/3222-150407128078c3bc.js","5170:static/chunks/5170-d43543fb1d3902f8.js","3146:static/chunks/app/sdks/page-efd20d1db75d0f7e.js"],"name":"","async":false} b:I{"id":9993,"chunks":["6685:static/chunks/6685-a937dd3e2b88ed12.js","2435:static/chunks/2435-ac4b486d27c0bea1.js","6021:static/chunks/6021-3dfd29e59407678e.js","3222:static/chunks/3222-150407128078c3bc.js","5170:static/chunks/5170-d43543fb1d3902f8.js","3146:static/chunks/app/sdks/page-efd20d1db75d0f7e.js"],"name":"Developer","async":false} c:I{"id":3106,"chunks":["6685:static/chunks/6685-a937dd3e2b88ed12.js","2435:static/chunks/2435-ac4b486d27c0bea1.js","6021:static/chunks/6021-3dfd29e59407678e.js","3222:static/chunks/3222-150407128078c3bc.js","5170:static/chunks/5170-d43543fb1d3902f8.js","3146:static/chunks/app/sdks/page-efd20d1db75d0f7e.js"],"name":"Heading","async":false} d:I{"id":3457,"chunks":["6685:static/chunks/6685-a937dd3e2b88ed12.js","2435:static/chunks/2435-ac4b486d27c0bea1.js","6021:static/chunks/6021-3dfd29e59407678e.js","3222:static/chunks/3222-150407128078c3bc.js","5170:static/chunks/5170-d43543fb1d3902f8.js","3146:static/chunks/app/sdks/page-efd20d1db75d0f7e.js"],"name":"Code","async":false} e:I{"id":3222,"chunks":["6685:static/chunks/6685-a937dd3e2b88ed12.js","2435:static/chunks/2435-ac4b486d27c0bea1.js","6021:static/chunks/6021-3dfd29e59407678e.js","3222:static/chunks/3222-150407128078c3bc.js","5170:static/chunks/5170-d43543fb1d3902f8.js","3146:static/chunks/app/sdks/page-efd20d1db75d0f7e.js"],"name":"Image","async":false} f:I{"id":3457,"chunks":["6685:static/chunks/6685-a937dd3e2b88ed12.js","2435:static/chunks/2435-ac4b486d27c0bea1.js","6021:static/chunks/6021-3dfd29e59407678e.js","3222:static/chunks/3222-150407128078c3bc.js","5170:static/chunks/5170-d43543fb1d3902f8.js","3146:static/chunks/app/sdks/page-efd20d1db75d0f7e.js"],"name":"Pre","async":false} 40:I{"id":6577,"chunks":["6685:static/chunks/6685-a937dd3e2b88ed12.js","2435:static/chunks/2435-ac4b486d27c0bea1.js","6021:static/chunks/6021-3dfd29e59407678e.js","3222:static/chunks/3222-150407128078c3bc.js","5170:static/chunks/5170-d43543fb1d3902f8.js","3146:static/chunks/app/sdks/page-efd20d1db75d0f7e.js"],"name":"Feedback","async":false} 10:T52c,πŸ“¦plugin-document-api ┣ πŸ“‚src ┃ ┣ πŸ“‚graphql ┃ ┃ ┣ πŸ“‚resolvers ┃ ┃ ┃ ┣ index.ts ┃ ┃ ┃ ┣ mutations.ts ┃ ┃ ┃ β”— queries.ts ┃ ┃ ┣ index.ts ┃ ┃ β”— typeDefs.ts ┃ ┣ configs.ts ┃ ┣ messageBroker.ts ┃ β”— models.ts ┣ .env.sample ┣ package.json β”— tsconfig.json 11:T12fb,// path: ./packages/plugin-[pluginName]-api/src/configs.ts import typeDefs from './graphql/typeDefs'; import resolvers from './graphql/resolvers'; import { initBroker } from './messageBroker'; export let mainDb; export let debug; export let graphqlPubsub; export let serviceDiscovery; export default { name: '[pluginName]', graphql: async sd => { serviceDiscovery = sd; return { typeDefs: await typeDefs(sd), resolvers: await resolvers(sd) }; }, apolloServerContext: async (context) => { return context; }, onServerInit: async options => { mainDb = options.db; initBroker(options.messageBrokerClient); graphqlPubsub = options.pubsubClient; debug = options.debug; } } 12:T1722,// path: ./packages/plugin-[pluginName]-api/src/messageBroker.ts import { ISendMessageArgs, sendMessage } from "@erxes/api-utils/src/core"; import { serviceDiscovery } from "./configs"; import { Documents } from "./models"; let client; export const initBroker = async cl => { client = cl; const { consumeQueue, consumeRPCQueue } = client; consumeQueue('document:send', async ({ data }) => { Documents.send(data); return { status: 'success', }; }); consumeRPCQueue('document:find', async ({ data }) => { return { status: 'success', data: await Documents.find({}) }; }); }; export const sendCommonMessage = async ( args: ISendMessageArgs & { serviceName: string } ) => { return sendMessage({ serviceDiscovery, client, ...args }); }; export default function() { return client; } 13:T4d2, πŸ“‚src ┣ πŸ“‚graphql ┃ ┣ πŸ“‚resolvers ┃ ┃ ┣ index.ts ┃ ┃ ┣ mutations.ts ┃ ┃ β”— queries.ts ┃ ┣ index.ts ┃ β”— typeDefs.ts 14:T18b8,import { Documents, Types } from '../../models'; import { IContext } from "@erxes/api-utils/src/types" const documentMutations = { /** * Creates a new document */ async documentsAdd(_root, doc, _context: IContext) { return Documents.createDocument(doc); }, /** * Edits a new document */ async documentsEdit( _root, { _id, ...doc }, _context: IContext ) { return Documents.updateDocument(_id, doc); }, /** * Removes a single document */ async documentsRemove(_root, { _id }, _context: IContext) { return Documents.removeDocument(_id); }, /** * Creates a new type for document */ async documentTypesAdd(_root, doc, _context: IContext) { return Types.createType(doc); }, async documentTypesRemove(_root, { _id }, _context: IContext) { return Types.removeType(_id); }, async documentTypesEdit( _root, { _id, ...doc }, _context: IContext ) { return Types.updateType(_id, doc); } }; export default documentMutations; 15:Td17,import { Documents, Types } from "../../models"; import { IContext } from "@erxes/api-utils/src/types" const documentQueries = { documents( _root, { typeId }, _context: IContext ) { const selector: any = {}; if (typeId) { selector.typeId = typeId; } return Documents.find(selector).sort({ order: 1, name: 1 }); }, documentTypes(_root, _args, _context: IContext) { return Types.find({}); }, documentsTotalCount(_root, _args, _context: IContext) { return Documents.find({}).countDocuments(); } }; export default documentQueries; 16:T415,import { gql } from 'apollo-server-express'; const types = ` type Document { _id: String! name: String createdAt:Date expiryDate:Date checked:Boolean typeId: String currentType: DocumentType } type DocumentType { _id: String! name: String } `; const queries = ` documents(typeId: String): [Document] documentTypes: [DocumentType] documentsTotalCount: Int `; const params = ` name: String, expiryDate: Date, checked: Boolean, typeId:String `; const mutations = ` documentsAdd(${params}): Document documentsRemove(_id: String!): JSON documentsEdit(_id:String!, ${params}): Document documentTypesAdd(name:String):DocumentType documentTypesRemove(_id: String!):JSON documentTypesEdit(_id: String!, name:String): DocumentType `; const typeDefs = async _serviceDiscovery => { return gql` scalar JSON scalar Date ${types} extend type Query { ${queries} } extend type Mutation { ${mutations} } `; }; export default typeDefs; 17:T1c1a,import { gql } from 'apollo-server-express'; const types = ` type Document { _id: String! name: String createdAt:Date expiryDate:Date checked:Boolean typeId: String currentType: DocumentType } type DocumentType { _id: String! name: String } `; const queries = ` documents(typeId: String): [Document] documentTypes: [DocumentType] documentsTotalCount: Int `; const params = ` name: String, expiryDate: Date, checked: Boolean, typeId:String `; const mutations = ` documentsAdd(${params}): Document documentsRemove(_id: String!): JSON documentsEdit(_id:String!, ${params}): Document documentTypesAdd(name:String):DocumentType documentTypesRemove(_id: String!):JSON documentTypesEdit(_id: String!, name:String): DocumentType `; const typeDefs = async _serviceDiscovery => { return gql` scalar JSON scalar Date ${types} extend type Query { ${queries} } extend type Mutation { ${mutations} } `; }; export default typeDefs; 18:T7c9,import * as _ from 'underscore'; import { model } from 'mongoose'; import { Schema } from 'mongoose'; export const typeSchema = new Schema({ name: String }); export const documentSchema = new Schema({ name: String, createdAt: Date, expiryDate: Date, checked: Boolean, typeId: String }); export const loadTypeClass = () => { class Type { public static async getType(_id: string) { const type = await Types.findOne({ _id }); if (!type) { throw new Error('Type not found'); } return type; } // create type public static async createType(doc) { return Types.create({ ...doc }); } // remove type public static async removeType(_id: string) { return Types.deleteOne({ _id }); } public static async updateType(_id: string, doc) { return Types.updateOne({ _id }, { $set: { ...doc } }); } } typeSchema.loadClass(Type); return typeSchema; }; export const loadDocumentClass = () => { class Document { public static async getDocument(_id: string) { const document = await Documents.findOne({ _id }); if (!document) { throw new Error('Document not found'); } return document; } // create public static async createDocument(doc) { return Documents.create({ ...doc, createdAt: new Date() }); } // update public static async updateDocument (_id: string, doc) { await Documents.updateOne( { _id }, { $set: { ...doc } } ).then(err => console.error(err)); } // remove public static async removeDocument(_id: string) { return Documents.deleteOne({ _id }); } } documentSchema.loadClass(Document); return documentSchema; }; loadDocumentClass(); loadTypeClass(); // tslint:disable-next-line export const Types = model( 'document_types', typeSchema ); // tslint:disable-next-line export const Documents = model('documents', documentSchema); 19:T2ed9,import * as _ from 'underscore'; import { model } from 'mongoose'; import { Schema } from 'mongoose'; export const typeSchema = new Schema({ name: String }); export const documentSchema = new Schema({ name: String, createdAt: Date, expiryDate: Date, checked: Boolean, typeId: String }); export const loadTypeClass = () => { class Type { public static async getType(_id: string) { const type = await Types.findOne({ _id }); if (!type) { throw new Error('Type not found'); } return type; } // create type public static async createType(doc) { return Types.create({ ...doc }); } // remove type public static async removeType(_id: string) { return Types.deleteOne({ _id }); } public static async updateType(_id: string, doc) { return Types.updateOne({ _id }, { $set: { ...doc } }); } } typeSchema.loadClass(Type); return typeSchema; }; export const loadDocumentClass = () => { class Document { public static async getDocument(_id: string) { const document = await Documents.findOne({ _id }); if (!document) { throw new Error('Document not found'); } return document; } // create public static async createDocument(doc) { return Documents.create({ ...doc, createdAt: new Date() }); } // update public static async updateDocument (_id: string, doc) { await Documents.updateOne( { _id }, { $set: { ...doc } } ).then(err => console.error(err)); } // remove public static async removeDocument(_id: string) { return Documents.deleteOne({ _id }); } } documentSchema.loadClass(Document); return documentSchema; }; loadDocumentClass(); loadTypeClass(); // tslint:disable-next-line export const Types = model<any, any>( 'document_types', typeSchema ); // tslint:disable-next-line export const Documents = model<any, any>('documents', documentSchema); 1a:Tecc,πŸ“¦plugin-[pluginName]-ui ┣ πŸ“‚src ┃ ┣ πŸ“‚components ┃ ┃ ┣ Form.tsx ┃ ┃ ┣ List.tsx ┃ ┃ ┣ Row.tsx ┃ ┃ ┣ SideBar.tsx ┃ ┃ β”— TypeForm.tsx ┃ ┣ πŸ“‚containers ┃ ┃ ┣ List.tsx ┃ ┃ β”— SideBarList.tsx ┃ ┣ πŸ“‚graphql ┃ ┃ ┣ index.ts ┃ ┃ ┣ mutations.ts ┃ ┃ β”— queries.ts ┃ ┣ App.tsx ┃ ┣ configs.js ┃ ┣ generalRoutes.tsx ┃ ┣ index.js ┃ ┣ routes.tsx ┃ β”— types.ts 1b:Teaf,// path: ./packages/plugin-[pluginName]-ui/src/configs.js module.exports = { name: '[pluginName]', port: 3017, scope: '[pluginName]', exposes: { './routes': './src/routes.tsx' }, routes: { url: 'http://localhost:3017/remoteEntry.js', scope: '[pluginName]', module: './routes' }, menus:[ { "text":"[pluginName]", "url":"/[pluginUrl]", "icon":"icon-star", "location":"[mainNavigation or settings]" } ] }; 1c:T10cc,// path: ./packages/plugin-[pluginName]-ui/src/routes.tsx import asyncComponent from '@erxes/ui/src/components/AsyncComponent'; import queryString from 'query-string'; import React from 'react'; import { Route } from 'react-router-dom'; const List = asyncComponent(() => import(/* webpackChunkName: "List - Documents" */ './containers/List') ); const documents = ({ location, history }) => { const queryParams = queryString.parse(location.search); const { type } = queryParams; return <List typeId={type} history={history} />; }; const routes = () => { return <Route path="/documents/" component={documents} />; }; 1d:T9c4,// path: ./packages/plugin-[pluginName]-ui/src/App.tsx import React from 'react'; import GeneralRoutes from './generalRoutes'; import { PluginLayout } from '@erxes/ui/src/styles/main'; const App = () => { return ( <PluginLayout> <GeneralRoutes /> </PluginLayout> ); }; export default App; 1e:T46d, πŸ“‚src ┣ πŸ“‚components ┃ ┣ Form.tsx ┃ ┣ List.tsx ┃ ┣ Row.tsx ┃ ┣ SideBar.tsx ┃ β”— TypeForm.tsx 1f:T882, // path: ./packages/plugin-[pluginName]-ui/src/components/TypeForm.tsx import { __ } from '@erxes/ui/src/utils/core'; import React from 'react'; import { IType } from '../types'; import { IButtonMutateProps, IFormProps } from '@erxes/ui/src/types'; import Form from '@erxes/ui/src/components/form/Form'; import { ControlLabel, FormControl, FormGroup } from '@erxes/ui/src/components/form'; import Button from '@erxes/ui/src/components/Button'; import { ModalFooter } from '@erxes/ui/src/styles/main'; type Props = { renderButton: (props: IButtonMutateProps) => JSX.Element; closeModal?: () => void; afterSave?: () => void; remove?: (type: IType) => void; types?: IType[]; type: IType; }; const TypeForm = (props: Props) => { const { type, closeModal, renderButton, afterSave } = props; const generateDoc = (values: { _id?: string; name: string; content: string; }) => { const finalValues = values; const { type } = props; if (type) { finalValues._id = type._id; } return { ...finalValues }; }; const renderContent = (formProps: IFormProps) => { const { values, isSubmitted } = formProps; const object = type || ({} as any); return ( <> Todo Type {renderButton({ passedName: 'type', values: generateDoc(values), isSubmitted, callback: closeModal || afterSave, object: type })} ); }; return
; }; export default TypeForm; 20:T373f, // path: ./packages/plugin-[pluginName]-ui/src/components/TypeForm.tsx import { __ } from '@erxes/ui/src/utils/core'; import React from 'react'; import { IType } from '../types'; import { IButtonMutateProps, IFormProps } from '@erxes/ui/src/types'; import Form from '@erxes/ui/src/components/form/Form'; import { ControlLabel, FormControl, FormGroup } from '@erxes/ui/src/components/form'; import Button from '@erxes/ui/src/components/Button'; import { ModalFooter } from '@erxes/ui/src/styles/main'; type Props = { renderButton: (props: IButtonMutateProps) => JSX.Element; closeModal?: () => void; afterSave?: () => void; remove?: (type: IType) => void; types?: IType[]; type: IType; }; const TypeForm = (props: Props) => { const { type, closeModal, renderButton, afterSave } = props; const generateDoc = (values: { _id?: string; name: string; content: string; }) => { const finalValues = values; const { type } = props; if (type) { finalValues._id = type._id; } return { ...finalValues }; }; const renderContent = (formProps: IFormProps) => { const { values, isSubmitted } = formProps; const object = type || ({} as any); return ( <> <FormGroup> <ControlLabel required={true}>Todo Type</ControlLabel> <FormControl {...formProps} name='name' defaultValue={object.name} type='text' required={true} autoFocus={true} /> </FormGroup> <ModalFooter id={'AddTypeButtons'}> <Button btnStyle='simple' onClick={closeModal} icon='times-circle'> Cancel </Button> {renderButton({ passedName: 'type', values: generateDoc(values), isSubmitted, callback: closeModal || afterSave, object: type })} </ModalFooter> </> ); }; return <Form renderContent={renderContent} />; }; export default TypeForm; 21:Tac8, // path: ./packages/plugin-[pluginName]-ui/src/containers/SideBarList.tsx import gql from 'graphql-tag'; import * as compose from 'lodash.flowright'; import { graphql } from 'react-apollo'; import { Alert, confirm, withProps } from '@erxes/ui/src/utils'; import SideBar from '../components/SideBar'; import { EditTypeMutationResponse, RemoveTypeMutationResponse, TypeQueryResponse } from '../types'; import { mutations, queries } from '../graphql'; import React from 'react'; import { IButtonMutateProps } from '@erxes/ui/src/types'; import ButtonMutate from '@erxes/ui/src/components/ButtonMutate'; import Spinner from '@erxes/ui/src/components/Spinner'; type Props = { history: any; currentTypeId?: string; }; type FinalProps = { listTemplateTypeQuery: TypeQueryResponse; } & Props & RemoveTypeMutationResponse & EditTypeMutationResponse; const TypesListContainer = (props: FinalProps) => { const { listTemplateTypeQuery, typesEdit, typesRemove, history } = props; if (listTemplateTypeQuery.loading) { return ; } // calls gql mutation for edit/add type const renderButton = ({ passedName, values, isSubmitted, callback, object }: IButtonMutateProps) => { return ( ); }; const remove = type => { confirm('You are about to delete the item. Are you sure? ') .then(() => { typesRemove({ variables: { _id: type._id } }) .then(() => { Alert.success('Successfully deleted an item'); }) .catch(e => Alert.error(e.message)); }) .catch(e => Alert.error(e.message)); }; const updatedProps = { ...props, types: listTemplateTypeQuery.templateTypes || [], loading: listTemplateTypeQuery.loading, remove, renderButton }; return ; }; export default withProps( compose( graphql(gql(queries.listTemplateTypes), { name: 'listTemplateTypeQuery', options: () => ({ fetchPolicy: 'network-only' }) }), graphql(gql(mutations.removeType), { name: 'typesRemove', options: () => ({ refetchQueries: ['listTemplateTypeQuery'] }) }) )(TypesListContainer) ); 22:T3eac, // path: ./packages/plugin-[pluginName]-ui/src/containers/SideBarList.tsx import gql from 'graphql-tag'; import * as compose from 'lodash.flowright'; import { graphql } from 'react-apollo'; import { Alert, confirm, withProps } from '@erxes/ui/src/utils'; import SideBar from '../components/SideBar'; import { EditTypeMutationResponse, RemoveTypeMutationResponse, TypeQueryResponse } from '../types'; import { mutations, queries } from '../graphql'; import React from 'react'; import { IButtonMutateProps } from '@erxes/ui/src/types'; import ButtonMutate from '@erxes/ui/src/components/ButtonMutate'; import Spinner from '@erxes/ui/src/components/Spinner'; type Props = { history: any; currentTypeId?: string; }; type FinalProps = { listTemplateTypeQuery: TypeQueryResponse; } & Props & RemoveTypeMutationResponse & EditTypeMutationResponse; const TypesListContainer = (props: FinalProps) => { const { listTemplateTypeQuery, typesEdit, typesRemove, history } = props; if (listTemplateTypeQuery.loading) { return <Spinner />; } // calls gql mutation for edit/add type const renderButton = ({ passedName, values, isSubmitted, callback, object }: IButtonMutateProps) => { return ( <ButtonMutate mutation={object ? mutations.editType : mutations.addType} variables={values} callback={callback} isSubmitted={isSubmitted} type="submit" successMessage={`You successfully ${ object ? 'updated' : 'added' } a ${passedName}`} refetchQueries={['listTemplateTypeQuery']} /> ); }; const remove = type => { confirm('You are about to delete the item. Are you sure? ') .then(() => { typesRemove({ variables: { _id: type._id } }) .then(() => { Alert.success('Successfully deleted an item'); }) .catch(e => Alert.error(e.message)); }) .catch(e => Alert.error(e.message)); }; const updatedProps = { ...props, types: listTemplateTypeQuery.templateTypes || [], loading: listTemplateTypeQuery.loading, remove, renderButton }; return <SideBar {...updatedProps} />; }; export default withProps<Props>( compose( graphql(gql(queries.listTemplateTypes), { name: 'listTemplateTypeQuery', options: () => ({ fetchPolicy: 'network-only' }) }), graphql(gql(mutations.removeType), { name: 'typesRemove', options: () => ({ refetchQueries: ['listTemplateTypeQuery'] }) }) )(TypesListContainer) ); 23:T403,const add = ` mutation documentsAdd($name: String!, $expiryDate: Date, $typeId:String) { documentsAdd(name:$name, expiryDate: $expiryDate, typeId:$typeId) { name _id expiryDate typeId } } `; const remove = ` mutation documentsRemove($_id: String!){ documentsRemove(_id: $_id) } `; const edit = ` mutation documentsEdit($_id: String!, $name:String, $expiryDate:Date, $checked:Boolean, $typeId:String){ documentsEdit(_id: $_id, name: $name, expiryDate:$expiryDate, checked:$checked, typeId:$typeId){ _id } } `; const addType = ` mutation typesAdd($name: String!){ documentTypesAdd(name:$name){ name _id } } `; const removeType = ` mutation typesRemove($_id:String!){ documentTypesRemove(_id:$_id) } `; const editType = ` mutation typesEdit($_id: String!, $name:String){ documentTypesEdit(_id: $_id, name: $name){ _id } } `; export default { add, remove, edit, addType, removeType, editType }; 24:T18f2,const add = ` mutation documentsAdd($name: String!, $expiryDate: Date, $typeId:String) { documentsAdd(name:$name, expiryDate: $expiryDate, typeId:$typeId) { name _id expiryDate typeId } } `; const remove = ` mutation documentsRemove($_id: String!){ documentsRemove(_id: $_id) } `; const edit = ` mutation documentsEdit($_id: String!, $name:String, $expiryDate:Date, $checked:Boolean, $typeId:String){ documentsEdit(_id: $_id, name: $name, expiryDate:$expiryDate, checked:$checked, typeId:$typeId){ _id } } `; const addType = ` mutation typesAdd($name: String!){ documentTypesAdd(name:$name){ name _id } } `; const removeType = ` mutation typesRemove($_id:String!){ documentTypesRemove(_id:$_id) } `; const editType = ` mutation typesEdit($_id: String!, $name:String){ documentTypesEdit(_id: $_id, name: $name){ _id } } `; export default { add, remove, edit, addType, removeType, editType }; 25:Tdbd,const list = ` query listQuery($typeId: String) { documents(typeId: $typeId) { _id name expiryDate createdAt checked typeId currentType{ _id name } } } `; const listDocumentTypes = ` query listDocumentTypeQuery{ documentTypes{ _id name } } `; const totalCount = ` query documentsTotalCount{ documentsTotalCount } `; export default { list, totalCount, listDocumentTypes }; 26:Tdb0,module.exports = { name: 'new_plugin', port: 3017, scope: 'new_plugin', exposes: { './routes': './src/routes.tsx' }, routes: { url: 'http://localhost:3017/remoteEntry.js', scope: 'new_plugin', module: './routes' }, menus: [] }; 27:T6bb,menus: [ { text: 'New plugin', url: '/new_plugins', icon: 'icon-star', location: 'mainNavigation', } ] 28:T7d3,menus: [ { text: 'New plugin', to: '/new_plugins', image: '/images/icons/erxes-18.svg', location: 'settings', scope: 'new_plugin' } ] 29:T1a38,{ "jwt_token_secret": "token", "dashboard": {}, "client_portal_domains": "", "elasticsearch": {}, "redis": { "password": "" }, "mongo": { "username": "", "password": "" }, "rabbitmq": { "cookie": "", "user": "", "pass": "", "vhost": "" }, "plugins": [ { "name": "logs" }, { "name": "new_plugin", "ui": "local" } ] } 2a:T1761,{ "jwt_token_secret": "token", "client_portal_domains": "", "elasticsearch": {}, "redis": { "password": "pass" }, "mongo": { "username": "", "password": "" }, "rabbitmq": { "cookie": "", "user": "", "pass": "", "vhost": "" }, "plugins": [ { "name": "new_plugin", "ui": "local" } ] } 2b:Tfc2, "plugins": [ { "name": "forms", "ui": "remote" }, { "name": "contacts", "ui": "local" }, { "name": "inbox", "ui": "local" }, { "name": "imap", "ui": "local" } 2c:Tc00, inboxIntegration: { name: 'IMAP', description: 'Connect a company email address such as sales@mycompany.com or info@mycompany.com', isAvailable: true, kind: 'imap', logo: '/images/integrations/email.png', createModal: 'imap', createUrl: '/settings/integrations/imap', category: 'All integrations, For support teams, Marketing automation, Email marketing' } 2d:T15b4,consumeRPCQueue( 'imap:createIntegration', async ({ subdomain, data: { doc, integrationId } }) => { const models = await generateModels(subdomain); const integration = await models.Integrations.create({ inboxId: integrationId, ...doc }); await listenIntegration(subdomain, integration); await models.Logs.createLog({ type: 'info', message: `Started syncing ${integration.user}` }); return { status: 'success' }; } ); 2e:T8f6,const apiCustomerResponse = await sendContactsMessage({ subdomain, action: 'customers.createCustomer', data: { integrationId: integration.inboxId, primaryEmail: from }, isRPC: true }); 2f:Te64,const { _id } = await sendInboxMessage({ subdomain, action: 'integrations.receive', data: { action: 'create-or-update-conversation', payload: JSON.stringify({ integrationId: integration.inboxId, customerId, createdAt: msg.date, content: msg.subject }) }, isRPC: true }); 30:Tb68,πŸ“¦plugin-twitter-api ┣ πŸ“‚src ┃ ┣ πŸ“‚graphql ┃ ┃ ┣ πŸ“‚resolvers ┃ ┃ ┃ ┣ index.ts ┃ ┃ ┃ ┣ mutations.ts ┃ ┃ ┃ β”— queries.ts ┃ ┃ ┣ index.ts ┃ ┃ β”— typeDefs.ts ┃ ┣ configs.ts ┃ ┣ controller.ts ┃ ┣ messageBroker.ts ┃ β”— models.ts ┣ .env.sample ┣ package.json β”— tsconfig.json 31:T1ab5,// path: ./packages/plugin-[pluginName]-api/src/configs.ts import typeDefs from './graphql/typeDefs'; import resolvers from './graphql/resolvers'; import { initBroker } from './messageBroker'; import init from './controller'; export let mainDb; export let graphqlPubsub; export let serviceDiscovery; export let debug; export default { name: 'twitter', graphql: sd => { serviceDiscovery = sd; return { typeDefs, resolvers }; }, meta: { // this code will show the integration in UI settings -> integrations inboxIntegration: { kind: 'twitter', label: 'Twitter' } }, apolloServerContext: async (context) => { return context; }, onServerInit: async options => { const app = options.app; mainDb = options.db; debug = options.debug; graphqlPubsub = options.pubsubClient; initBroker(options.messageBrokerClient); // integration controller init(app); } }; 32:Te26,import { sendContactsMessage, sendInboxMessage } from './messageBroker'; import { Customers, Messages } from './models'; // util function const searchMessages = (linkedin, criteria) => { return new Promise((resolve, reject) => { const messages: any = []; }); }; // Example for save messages to inbox and create or update customer const saveMessages = async ( linkedin, integration, criteria ) => { const msgs: any = await searchMessages(linkedin, criteria); for (const msg of msgs) { const message = await Messages.findOne({ messageId: msg.messageId }); if (message) { continue; } const from = msg.from.value[0].address; const prev = await Customers.findOne({ email: from }); let customerId; if (!prev) { // search customer from contacts api const customer = await sendContactsMessage({ subdomain: 'os', action: 'customers.findOne', data: { primaryEmail: from }, isRPC: true }); if (customer) { customerId = customer._id; } else { // creating new customer const apiCustomerResponse = await sendContactsMessage({ subdomain: 'os', action: 'customers.createCustomer', data: { integrationId: integration.inboxId, primaryEmail: from }, isRPC: true }); customerId = apiCustomerResponse._id; } await Customers.create({ inboxIntegrationId: integration.inboxId, contactsId: customerId, email: from }); } else { customerId = prev.contactsId; } let conversationId; const relatedMessage = await Messages.findOne({ $or: [ { messageId: msg.inReplyTo }, { messageId: { $in: msg.references || [] } }, { references: { $in: [msg.messageId] } }, { references: { $in: [msg.inReplyTo] } } ] }); if (relatedMessage) { conversationId = relatedMessage.inboxConversationId; } else { const { _id } = await sendInboxMessage({ subdomain: 'os', action: 'integrations.receive', data: { action: 'create-or-update-conversation', payload: JSON.stringify({ integrationId: integration.inboxId, customerId, createdAt: msg.date, content: msg.subject }) }, isRPC: true }); conversationId = _id; } await Messages.create({ inboxIntegrationId: integration.inboxId, inboxConversationId: conversationId, createdAt: msg.date, messageId: msg.messageId, inReplyTo: msg.inReplyTo, references: msg.references, subject: msg.subject, body: msg.html, to: msg.to && msg.to.value, cc: msg.cc && msg.cc.value, bcc: msg.bcc && msg.bcc.value, from: msg.from && msg.from.value, }); } }; // controller for twitter const init = async app => { // write integration login method below app.get('/login', async (req, res) => { res.send("login") }); app.post('/receive', async (req, res, next) => { try { // write receive message from integration code here res.send("Successfully receiving message"); } catch (e) { return next(new Error(e)); } res.sendStatus(200); }); }; export default init; 33:T45dc,import { sendContactsMessage, sendInboxMessage } from './messageBroker'; import { Customers, Messages } from './models'; // util function const searchMessages = (linkedin, criteria) => { return new Promise((resolve, reject) => { const messages: any = []; }); }; // Example for save messages to inbox and create or update customer const saveMessages = async ( linkedin, integration, criteria ) => { const msgs: any = await searchMessages(linkedin, criteria); for (const msg of msgs) { const message = await Messages.findOne({ messageId: msg.messageId }); if (message) { continue; } const from = msg.from.value[0].address; const prev = await Customers.findOne({ email: from }); let customerId; if (!prev) { // search customer from contacts api const customer = await sendContactsMessage({ subdomain: 'os', action: 'customers.findOne', data: { primaryEmail: from }, isRPC: true }); if (customer) { customerId = customer._id; } else { // creating new customer const apiCustomerResponse = await sendContactsMessage({ subdomain: 'os', action: 'customers.createCustomer', data: { integrationId: integration.inboxId, primaryEmail: from }, isRPC: true }); customerId = apiCustomerResponse._id; } await Customers.create({ inboxIntegrationId: integration.inboxId, contactsId: customerId, email: from }); } else { customerId = prev.contactsId; } let conversationId; const relatedMessage = await Messages.findOne({ $or: [ { messageId: msg.inReplyTo }, { messageId: { $in: msg.references || [] } }, { references: { $in: [msg.messageId] } }, { references: { $in: [msg.inReplyTo] } } ] }); if (relatedMessage) { conversationId = relatedMessage.inboxConversationId; } else { const { _id } = await sendInboxMessage({ subdomain: 'os', action: 'integrations.receive', data: { action: 'create-or-update-conversation', payload: JSON.stringify({ integrationId: integration.inboxId, customerId, createdAt: msg.date, content: msg.subject }) }, isRPC: true }); conversationId = _id; } await Messages.create({ inboxIntegrationId: integration.inboxId, inboxConversationId: conversationId, createdAt: msg.date, messageId: msg.messageId, inReplyTo: msg.inReplyTo, references: msg.references, subject: msg.subject, body: msg.html, to: msg.to && msg.to.value, cc: msg.cc && msg.cc.value, bcc: msg.bcc && msg.bcc.value, from: msg.from && msg.from.value, }); } }; // controller for twitter const init = async app => { // write integration login method below app.get('/login', async (req, res) => { res.send("login") }); app.post('/receive', async (req, res, next) => { try { // write receive message from integration code here res.send("Successfully receiving message"); } catch (e) { return next(new Error(e)); } res.sendStatus(200); }); }; export default init; 34:T616,// path: ./packages/plugin-[pluginName]-api/src/messageBroker.ts import * as dotenv from 'dotenv'; import { ISendMessageArgs, sendMessage as sendCommonMessage } from '@erxes/api-utils/src/core'; import { serviceDiscovery } from './configs'; import { Customers, Integrations, Messages } from './models'; dotenv.config(); let client; export const initBroker = async cl => { client = cl; const { consumeRPCQueue } = client; consumeRPCQueue( 'twitter:createIntegration', async ({ data: { doc, integrationId } }) => { await Integrations.create({ inboxId: integrationId, ...(doc || {}) }); return { status: 'success' }; } ); consumeRPCQueue( 'twitter:removeIntegration', async ({ data: { integrationId } }) => { await Messages.remove({ inboxIntegrationId: integrationId }); await Customers.remove({ inboxIntegrationId: integrationId }); await Integrations.remove({ inboxId: integrationId }); return { status: 'success' }; } ); }; export default function() { return client; } export const sendContactsMessage = (args: ISendMessageArgs) => { return sendCommonMessage({ client, serviceDiscovery, serviceName: 'contacts', ...args }); }; export const sendInboxMessage = (args: ISendMessageArgs) => { return sendCommonMessage({ client, serviceDiscovery, serviceName: 'inbox', ...args }); }; 35:T2501,// path: ./packages/plugin-[pluginName]-api/src/messageBroker.ts import * as dotenv from 'dotenv'; import { ISendMessageArgs, sendMessage as sendCommonMessage } from '@erxes/api-utils/src/core'; import { serviceDiscovery } from './configs'; import { Customers, Integrations, Messages } from './models'; dotenv.config(); let client; export const initBroker = async cl => { client = cl; const { consumeRPCQueue } = client; consumeRPCQueue( 'twitter:createIntegration', async ({ data: { doc, integrationId } }) => { await Integrations.create({ inboxId: integrationId, ...(doc || {}) }); return { status: 'success' }; } ); consumeRPCQueue( 'twitter:removeIntegration', async ({ data: { integrationId } }) => { await Messages.remove({ inboxIntegrationId: integrationId }); await Customers.remove({ inboxIntegrationId: integrationId }); await Integrations.remove({ inboxId: integrationId }); return { status: 'success' }; } ); }; export default function() { return client; } export const sendContactsMessage = (args: ISendMessageArgs) => { return sendCommonMessage({ client, serviceDiscovery, serviceName: 'contacts', ...args }); }; export const sendInboxMessage = (args: ISendMessageArgs) => { return sendCommonMessage({ client, serviceDiscovery, serviceName: 'inbox', ...args }); }; 36:T4d2, πŸ“‚src ┣ πŸ“‚graphql ┃ ┣ πŸ“‚resolvers ┃ ┃ ┣ index.ts ┃ ┃ ┣ mutations.ts ┃ ┃ β”— queries.ts ┃ ┣ index.ts ┃ β”— typeDefs.ts 37:T65d, import { Accounts } from '../../models'; import { IContext } from "@erxes/api-utils/src/types" const twitterMutations = { async twitterAccountRemove(_root, {_id}: {_id: string}, _context: IContext) { await Accounts.removeAccount(_id); return 'deleted'; } }; export default twitterMutations; 38:T404, import { IContext } from '@erxes/api-utils/src/types'; import { Accounts, Messages } from '../../models'; const queries = { async twitterConversationDetail( _root, { conversationId }, _context: IContext ) { const messages = await Messages.find({ inboxConversationId: conversationId }); const convertEmails = emails => (emails || []).map(item => ({ name: item.name, email: item.address })); return messages.map(message => { return { _id: message._id, mailData: { messageId: message.messageId, from: convertEmails(message.from), to: convertEmails(message.to), cc: convertEmails(message.cc), bcc: convertEmails(message.bcc), subject: message.subject, body: message.body, } }; }); }, async twitterAccounts(_root, _args, _context: IContext) { return Accounts.getAccounts(); } }; export default queries; 39:T1290, import { IContext } from '@erxes/api-utils/src/types'; import { Accounts, Messages } from '../../models'; const queries = { async twitterConversationDetail( _root, { conversationId }, _context: IContext ) { const messages = await Messages.find({ inboxConversationId: conversationId }); const convertEmails = emails => (emails || []).map(item => ({ name: item.name, email: item.address })); return messages.map(message => { return { _id: message._id, mailData: { messageId: message.messageId, from: convertEmails(message.from), to: convertEmails(message.to), cc: convertEmails(message.cc), bcc: convertEmails(message.bcc), subject: message.subject, body: message.body, } }; }); }, async twitterAccounts(_root, _args, _context: IContext) { return Accounts.getAccounts(); } }; export default queries; 3a:Tfad,import { gql } from 'apollo-server-express'; const types = ` type Twitter { _id: String! title: String mailData: JSON } `; const queries = ` twitterConversationDetail(conversationId: String!): [Twitter] twitterAccounts: JSON `; const mutations = ` twitterAccountRemove(_id: String!): String `; const typeDefs = gql` scalar JSON scalar Date ${types} extend type Query { ${queries} } extend type Mutation { ${mutations} } `; export default typeDefs; 3b:T5d0,module.exports = { name: 'twitter', scope: 'twitter', port: 3024, exposes: { './routes': './src/routes.tsx', // below components will work dynamically. In our case we call this components in inbox-ui. './inboxIntegrationSettings': './src/components/IntegrationSettings.tsx', './inboxIntegrationForm': './src/components/IntegrationForm.tsx', './inboxConversationDetail': './src/components/ConversationDetail.tsx' }, routes: { url: 'http://localhost:3024/remoteEntry.js', scope: 'twitter', module: './routes' }, // calling exposed components inboxIntegrationSettings: './inboxIntegrationSettings', inboxIntegrationForm: './inboxIntegrationForm', inboxConversationDetail: './inboxConversationDetail', inboxIntegration: { name: 'Twitter', // integration desciption will show in integration box. description: 'Please write integration description on plugin config file', // this variable shows that integration available in production mode. In development mode we always set true value. isAvailable: true, // integration kind will useful for call integration form dynamically kind: 'twitter', // integration logo logo: '/images/integrations/twitter.png', // if you choose with detail page when creating an integration plugin using cli. When you click add button in integration box, integration will link to this url. createUrl: '/settings/integrations/twitter', } }; 3c:T25f9,module.exports = { name: 'twitter', scope: 'twitter', port: 3024, exposes: { './routes': './src/routes.tsx', // below components will work dynamically. In our case we call this components in inbox-ui. './inboxIntegrationSettings': './src/components/IntegrationSettings.tsx', './inboxIntegrationForm': './src/components/IntegrationForm.tsx', './inboxConversationDetail': './src/components/ConversationDetail.tsx' }, routes: { url: 'http://localhost:3024/remoteEntry.js', scope: 'twitter', module: './routes' }, // calling exposed components inboxIntegrationSettings: './inboxIntegrationSettings', inboxIntegrationForm: './inboxIntegrationForm', inboxConversationDetail: './inboxConversationDetail', inboxIntegration: { name: 'Twitter', // integration desciption will show in integration box. description: 'Please write integration description on plugin config file', // this variable shows that integration available in production mode. In development mode we always set true value. isAvailable: true, // integration kind will useful for call integration form dynamically kind: 'twitter', // integration logo logo: '/images/integrations/twitter.png', // if you choose with detail page when creating an integration plugin using cli. When you click add button in integration box, integration will link to this url. createUrl: '/settings/integrations/twitter', } }; 3d:T482,{loadDynamicComponent( 'inboxIntegrationSettings', { renderItem: this.renderItem }, true )} 3e:T1a38,{ "jwt_token_secret": "token", "dashboard": {}, "client_portal_domains": "", "elasticsearch": {}, "redis": { "password": "" }, "mongo": { "username": "", "password": "" }, "rabbitmq": { "cookie": "", "user": "", "pass": "", "vhost": "" }, "plugins": [ { "name": "logs" }, { "name": "new_plugin", "ui": "local" } ] } 3f:T2302,{ "jwt_token_secret": "token", "client_portal_domains": "", "elasticsearch": {}, "redis": { "password": "pass" }, "mongo": { "username": "", "password": "" }, "rabbitmq": { "cookie": "", "user": "", "pass": "", "vhost": "" }, "plugins": [ { "name": "forms", "ui": "remote" }, { "name": "contacts", "ui": "local" }, { "name": "inbox", "ui": "local" }, { "name": "twitter", "ui": "local" }, ] } 4:["$","html",null,{"lang":"en","className":"h-full","suppressHydrationWarning":true,"children":["$","body",null,{"className":"flex min-h-full bg-white antialiased dark:bg-zinc-900","children":["$","$L5",null,{"children":["$","div",null,{"className":"w-full","children":["$","$L6",null,{"allSections":{"/":[{"title":"Architecture","id":"architecture"},{"title":"Frequently Asked Questions","id":"faq"}],"/authentication":[{"title":"GraphQL API","id":"graphql"},{"title":"Postman Collection","id":"postman"},{"title":"Code reference","id":"reference"}],"/contacts":[{"title":"Panel","id":"panel"},{"title":"Marketplace","id":"marketplace"}],"/conversations":[{"title":"System configuration","id":"system-configuration"},{"title":"Permission","id":"permission"},{"title":"Team member","id":"team-member"},{"title":"Brands","id":"brands"},{"title":"Import/Export","id":"import-export"},{"title":"Channel","id":"channel"},{"title":"Script Installation","id":"script-installation"}],"/employee":[],"/errors":[{"title":"Status codes","id":"status-codes"},{"title":"Error types","id":"error-types"}],"/attachments":[{"title":"The attachment model","id":"the-attachment-model"},{"title":"List all attachments","id":"list-all-attachments","tag":"GET","label":"/v1/attachments"},{"title":"Create an attachment","id":"create-an-attachment","tag":"POST","label":"/v1/attachments"},{"title":"Retrieve an attachment","id":"retrieve-an-attachment","tag":"GET","label":"/v1/attachments/:id"},{"title":"Update an attachment","id":"update-an-attachment","tag":"PUT","label":"/v1/attachments/:id"},{"title":"Delete an attachment","id":"delete-an-attachment","tag":"DELETE","label":"/v1/attachments/:id"}],"/customer":[{"title":"XM for B2B order","id":"xm-for-b2-b-order"},{"title":"XM for B2B Sales","id":"xm-for-b2-b-sales"},{"title":"XM for Catering","id":"xm-for-catering"},{"title":"XM for Ecommerce","id":"xm-for-ecommerce"},{"title":"XM for POS","id":"xm-for-pos"},{"title":"XM for Scheduling","id":"xm-for-scheduling"}],"/frontline":[{"title":"XM for CallCenter","id":"xm-for-call-center"},{"title":"XM for Chatbot","id":"xm-for-chatbot"},{"title":"XM for Help Center","id":"xm-for-help-center"},{"title":"XM for Ticket Management","id":"xm-for-ticket-management"}],"/marketing":[{"title":"XM for Marketing automation","id":"xm-for-marketing-automation"},{"title":"XM for Newsletter","id":"xm-for-newsletter"},{"title":"XM for Survey","id":"xm-for-survey"}],"/messages":[{"title":"Team Inbox","id":"team-inbox"},{"title":"Introduction to add-ons","id":"introduction-to-add-ons"},{"title":"Introduction to services","id":"introduction-to-services"},{"title":"Introduction to power-ups","id":"introduction-to-power-ups"}],"/pagination":[{"title":"Contribute to codebase","id":"contribute-to-codebase"},{"title":"Contribute to documentation","id":"contribute-to-documentation"},{"title":"Documentation style guide","id":"documentation-style-guide"}],"/quickstart":[{"title":"Local installation","id":"local-installation"},{"title":"Server installation","id":"server-installation"}],"/intro":[{"title":"Architecture","id":"architecture"},{"title":"Frequently Asked Questions","id":"frequently-asked-questions"}],"/groups":[{"title":"Products","id":"products"},{"title":"Script Installation","id":"script-installation"},{"title":"Employee Experience Management","id":"employee-experience-management"},{"title":"Industries","id":"industries"},{"title":"Teams","id":"teams"},{"title":"Employee Experience Management","id":"employee-experience-management-2"}],"/finance":[{"title":"XM for Leasing","id":"xm-for-leasing"},{"title":"XM for Payment","id":"xm-for-payment"},{"title":"XM for Rental","id":"xm-for-rental"}],"/sdks":[{"title":"Create General Plugin","id":"create-general-plugin"},{"title":"Create Integration plugin","id":"create-integration-plugin"}],"/requirement":[],"/authentication/postman":[],"/webhooks":[{"title":"Registering webhooks","id":"registering-webhooks"},{"title":"Consuming webhooks","id":"consuming-webhooks"},{"title":"Event types","id":"event-types"},{"title":"Security","id":"security"}],"/contacts/market":[],"/conversations/brands":[{"title":"Brands","id":"brands"}],"/authentication/graphql":[],"/authentication/reference":[{"title":"UI","id":"ui"},{"title":"API","id":"api"}],"/conversations/permission":[{"title":"Permission","id":"permission"}],"/conversations/channel":[{"title":"Channel","id":"channel"}],"/contacts/panel":[{"title":"Admin Panel","id":"panel"},{"title":"Marketplace","id":"market"}],"/conversations/impexp":[{"title":"Import/Export","id":"import-export"}],"/conversations/script":[{"title":"Script Installation","id":"script-installation"}],"/conversations/system":[{"title":"System configuration","id":"system-configuration"}],"/messages/add":[],"/conversations/team":[{"title":"Team member","id":"team-member"}],"/messages/channels":[{"title":"With Channels, you can do...","id":"with-channels-you-can-do"},{"title":"Creating your channels","id":"creating-your-channels"}],"/messages/services":[],"/messages/power":[],"/pagination/codebase":[{"title":"Prerequisites","id":"prerequisites"},{"title":"Issues before PRs","id":"issues-before-prs"}],"/messages/team":[{"title":"With Inbox, you can do...","id":"with-inbox-you-can-do"},{"title":"Features works with Inbox","id":"features-works-with-inbox"}],"/messages/skills":[{"title":"To set up Skills","id":"to-set-up-skills"}],"/messages/response":[{"title":"With Response Template, you can do...","id":"with-response-template-you-can-do"},{"title":"Create your response template","id":"create-your-response-template"}],"/quickstart/groups":[],"/pagination/contributedoc":[],"/quickstart/deployment":[],"/groups/products":[],"/sdks/creategen":[{"title":"GraphQL development","id":"graph-ql-development"},{"title":"UI file structure","id":"ui-file-structure"},{"title":"Main files","id":"main-files"},{"title":"UI development","id":"ui-development"},{"title":"Running port for plugin","id":"running-port-for-plugin"},{"title":"Location for plugin","id":"location-for-plugin"},{"title":"Enabling plugins","id":"enabling-plugins"}],"/groups/team":[{"title":"Employee Experience Management","id":"employee-experience-management"},{"title":"Centralized complete HR database","id":"centralized-complete-hr-database"},{"title":"Knowledge, onboarding & training campaign","id":"knowledge-onboarding-and-training-campaign"},{"title":"Automatically generated HR templates","id":"automatically-generated-hr-templates"},{"title":"Fully customizable recruitment pipeline","id":"fully-customizable-recruitment-pipeline"},{"title":"The next generation of time-tracking system","id":"the-next-generation-of-time-tracking-system"},{"title":"Clear view of employee productivity","id":"clear-view-of-employee-productivity"},{"title":"Employee feed & Communication","id":"employee-feed-and-communication"},{"title":"Quick and efficient asset record","id":"quick-and-efficient-asset-record"}],"/requirement/customer":[],"/pagination/docstyle":[{"title":"Markdown Syntax","id":"markdown-syntax"},{"title":"Headers","id":"headers"},{"title":"H2 - Create the best documentation","id":"h2-create-the-best-documentation"},{"title":"H3 - Create the best documentation","id":"h3-create-the-best-documentation"},{"title":"Reference-style: ","id":"reference-style"},{"title":"Quote break.","id":"quote-break"}],"/authentication/graphql/input":[{"title":"ActionInput","id":"action-input"},{"title":"AttachmentInput","id":"attachment-input"},{"title":"BookingStyleInput","id":"booking-style-input"},{"title":"ClientPortalUserUpdate","id":"client-portal-user-update"},{"title":"ConversationMessageParams","id":"conversation-message-params"},{"title":"CoordinateInput","id":"coordinate-input"},{"title":"EmailSignature","id":"email-signature"},{"title":"EngageMessageSmsInput","id":"engage-message-sms-input"},{"title":"EngageMessageEmail","id":"engage-message-email"},{"title":"EngageMessageMessenger","id":"engage-message-messenger"},{"title":"EngageScheduleDateInput","id":"engage-schedule-date-input"},{"title":"EventAttributeFilter","id":"event-attribute-filter"},{"title":"ExmAppearanceInput","id":"exm-appearance-input"},{"title":"ExmEventDataInput","id":"exm-event-data-input"},{"title":"ExmFeatureInput","id":"exm-feature-input"},{"title":"ExmWelcomeContentInput","id":"exm-welcome-content-input"},{"title":"FieldItem","id":"field-item"},{"title":"FieldValueInput","id":"field-value-input"},{"title":"FormCodeInput","id":"form-code-input"},{"title":"FormSubmissionInput","id":"form-submission-input"},{"title":"IMovementItem","id":"i-movement-item"},{"title":"InputRule","id":"input-rule"},{"title":"IntegrationBookingData","id":"integration-booking-data"},{"title":"IntegrationLinks","id":"integration-links"},{"title":"IntegrationLeadData","id":"integration-lead-data"},{"title":"IntegrationMessengerData","id":"integration-messenger-data"},{"title":"Interval","id":"interval"},{"title":"ItemDate","id":"item-date"},{"title":"InvitationEntry","id":"invitation-entry"},{"title":"KnowledgeBaseArticleDoc","id":"knowledge-base-article-doc"},{"title":"KnowledgeBaseCategoryDoc","id":"knowledge-base-category-doc"},{"title":"KnowledgeBaseMessengerAppInput","id":"knowledge-base-messenger-app-input"},{"title":"KnowledgeBaseTopicDoc","id":"knowledge-base-topic-doc"},{"title":"LeadMessengerAppInput","id":"lead-messenger-app-input"},{"title":"LocationOptionInput","id":"location-option-input"},{"title":"LogicInput","id":"logic-input"},{"title":"MailConfigInput","id":"mail-config-input"},{"title":"MessengerAppsInput","id":"messenger-apps-input"},{"title":"MessengerOnlineHoursSchema","id":"messenger-online-hours-schema"},{"title":"MessengerUiOptions","id":"messenger-ui-options"},{"title":"NotificationConfigInput","id":"notification-config-input"},{"title":"objectListConfigInput","id":"object-list-config-input"},{"title":"OrderItem","id":"order-item"},{"title":"OTPConfigInput","id":"otp-config-input"},{"title":"PipelineTemplateStageInput","id":"pipeline-template-stage-input"},{"title":"ProductField","id":"product-field"},{"title":"SegmentCondition","id":"segment-condition"},{"title":"StylesParams","id":"styles-params"},{"title":"ShiftsRequestInput","id":"shifts-request-input"},{"title":"SubmissionFilter","id":"submission-filter"},{"title":"SubSegment","id":"sub-segment"},{"title":"TriggerInput","id":"trigger-input"},{"title":"UserDetails","id":"user-details"},{"title":"WebSiteMessengerAppInput","id":"web-site-messenger-app-input"}],"/authentication/graphql/intro":[],"/requirement/employee":[{"title":"Assumptions","id":"assumptions"},{"title":"Scalability","id":"scalability"},{"title":"Security","id":"security"},{"title":"Usability","id":"usability"},{"title":"Performance","id":"performance"}],"/sdks/createinte":[{"title":"Create an integration","id":"create-an-integration"},{"title":"Receive data from your desired APIs","id":"receive-data-from-your-desired-apis"},{"title":"Store the data","id":"store-the-data"},{"title":"Conversation detail","id":"conversation-detail"},{"title":"API file structure","id":"api-file-structure"},{"title":"Main files","id":"main-files"},{"title":"configs.ts","id":"configs-ts"},{"title":"controller.ts","id":"controller-ts"},{"title":"messageBroker.ts","id":"message-broker-ts"},{"title":"GraphQL development","id":"graph-ql-development"},{"title":"GraphQL resolvers","id":"graph-ql-resolvers"},{"title":"GraphQL typeDefs","id":"graph-ql-type-defs"},{"title":"Database development","id":"database-development"},{"title":"Mongoose schema and model","id":"mongoose-schema-and-model"},{"title":"UI file structure","id":"ui-file-structure"},{"title":"Running port for plugin","id":"running-port-for-plugin"},{"title":"Enabling plugins","id":"enabling-plugins"}],"/authentication/graphql/enu":[{"title":"CacheControlScope","id":"cache-control-scope"},{"title":"ChatMemberModifyType","id":"chat-member-modify-type"},{"title":"ChatType","id":"chat-type"},{"title":"ChatVisibilityType","id":"chat-visibility-type"},{"title":"ClientPortalUserVerificationStatus","id":"client-portal-user-verification-status"},{"title":"ContentType","id":"content-type"},{"title":"ExmGoingOrInterested","id":"exm-going-or-interested"},{"title":"FilterType","id":"filter-type"},{"title":"NotificationType","id":"notification-type"},{"title":"ReactionContentType","id":"reaction-content-type"},{"title":"RecipientType","id":"recipient-type"},{"title":"SourceType","id":"source-type"}],"/authentication/graphql/mutations":[],"/authentication/reference/api":[{"title":"Common functions","id":"common-functions"}],"/authentication/graphql/scalars":[{"title":"Boolean","id":"boolean"},{"title":"Date","id":"date"},{"title":"Float","id":"float"},{"title":"Int","id":"int"},{"title":"JSON","id":"json"},{"title":"String","id":"string"}],"/authentication/graphql/queries":[{"title":"absenceDetail","id":"absence-detail"},{"title":"ActionInput","id":"action-input"},{"title":"absenceTypes","id":"absence-types"},{"title":"activeMe","id":"active-me"},{"title":"activityLogs","id":"activity-logs"},{"title":"activityLogsByAction","id":"activity-logs-by-action"},{"title":"allBrands","id":"all-brands"},{"title":"allLeadIntegrations","id":"all-lead-integrations"},{"title":"allUsers","id":"all-users"},{"title":"BookingStyleInput","id":"booking-style-input"},{"title":"ClientPortalUserUpdate","id":"client-portal-user-update"},{"title":"ConversationMessageParams","id":"conversation-message-params"},{"title":"CoordinateInput","id":"coordinate-input"},{"title":"EmailSignature","id":"email-signature"},{"title":"EngageMessageSmsInput","id":"engage-message-sms-input"},{"title":"EngageMessageEmail","id":"engage-message-email"},{"title":"EngageMessageMessenger","id":"engage-message-messenger"},{"title":"EngageScheduleDateInput","id":"engage-schedule-date-input"},{"title":"EventAttributeFilter","id":"event-attribute-filter"},{"title":"ExmAppearanceInput","id":"exm-appearance-input"},{"title":"ExmEventDataInput","id":"exm-event-data-input"},{"title":"ExmFeatureInput","id":"exm-feature-input"},{"title":"ExmWelcomeContentInput","id":"exm-welcome-content-input"},{"title":"FieldItem","id":"field-item"},{"title":"FieldValueInput","id":"field-value-input"},{"title":"FormCodeInput","id":"form-code-input"},{"title":"FormSubmissionInput","id":"form-submission-input"},{"title":"IMovementItem","id":"i-movement-item"},{"title":"InputRule","id":"input-rule"},{"title":"IntegrationBookingData","id":"integration-booking-data"},{"title":"IntegrationLinks","id":"integration-links"},{"title":"IntegrationLeadData","id":"integration-lead-data"},{"title":"IntegrationMessengerData","id":"integration-messenger-data"},{"title":"Interval","id":"interval"},{"title":"ItemDate","id":"item-date"},{"title":"InvitationEntry","id":"invitation-entry"},{"title":"KnowledgeBaseArticleDoc","id":"knowledge-base-article-doc"},{"title":"KnowledgeBaseCategoryDoc","id":"knowledge-base-category-doc"},{"title":"KnowledgeBaseMessengerAppInput","id":"knowledge-base-messenger-app-input"},{"title":"KnowledgeBaseTopicDoc","id":"knowledge-base-topic-doc"},{"title":"LeadMessengerAppInput","id":"lead-messenger-app-input"},{"title":"LocationOptionInput","id":"location-option-input"},{"title":"LogicInput","id":"logic-input"},{"title":"MailConfigInput","id":"mail-config-input"},{"title":"MessengerAppsInput","id":"messenger-apps-input"},{"title":"MessengerOnlineHoursSchema","id":"messenger-online-hours-schema"},{"title":"MessengerUiOptions","id":"messenger-ui-options"},{"title":"NotificationConfigInput","id":"notification-config-input"},{"title":"objectListConfigInput","id":"object-list-config-input"},{"title":"OrderItem","id":"order-item"},{"title":"OTPConfigInput","id":"otp-config-input"},{"title":"PipelineTemplateStageInput","id":"pipeline-template-stage-input"},{"title":"ProductField","id":"product-field"},{"title":"SegmentCondition","id":"segment-condition"},{"title":"StylesParams","id":"styles-params"},{"title":"ShiftsRequestInput","id":"shifts-request-input"},{"title":"SubmissionFilter","id":"submission-filter"},{"title":"SubSegment","id":"sub-segment"},{"title":"TriggerInput","id":"trigger-input"},{"title":"UserDetails","id":"user-details"},{"title":"WebSiteMessengerAppInput","id":"web-site-messenger-app-input"}],"/authentication/reference/ui":[],"/quickstart/groups/mac":[{"title":"If your browser don't automatically jump to localhost:3000, you should check logs by using these commands.","id":"if-your-browser-dont-automatically-jump-to-localhost-3000-you-should-check-logs-by-using-these-commands"},{"title":"If you see this screen, you have successfully install erxes XOS. Congratulations πŸŽ‰πŸŽ‰πŸŽ‰","id":"if-you-see-this-screen-you-have-successfully-install-erxes-xos-congratulations"}],"/quickstart/deployment/deploymentDocker":[{"title":"Create an erxes user on the instance","id":"create-an-erxes-user-on-the-instance"},{"title":"Installation steps","id":"installation-steps"}],"/authentication/graphql/object":[{"title":"Absence","id":"absence"},{"title":"AbsenceType","id":"absence-type"},{"title":"Action","id":"action"},{"title":"ActivityLog","id":"activity-log"},{"title":"ActivityLogByAction","id":"activity-log-by-action"},{"title":"ActivityLogByActionResponse","id":"activity-log-by-action-response"},{"title":"App","id":"app"},{"title":"Asset","id":"asset"},{"title":"AssetCategory","id":"asset-category"},{"title":"Assignment","id":"assignment"},{"title":"AssignmentCampaign","id":"assignment-campaign"},{"title":"AssignmentMain","id":"assignment-main"},{"title":"Attachment","id":"attachment"},{"title":"Automation","id":"automation"},{"title":"AutomationHistory","id":"automation-history"},{"title":"AutomationNote","id":"automation-note"},{"title":"AutomationsListResponse","id":"automations-list-response"},{"title":"automationsTotalCountResponse","id":"automations-total-count-response"},{"title":"AvgEmailStats","id":"avg-email-stats"},{"title":"Board","id":"board"},{"title":"BoardCount","id":"board-count"},{"title":"BookingData","id":"booking-data"},{"title":"BookingProduct","id":"booking-product"},{"title":"Branch","id":"branch"},{"title":"BranchListQueryResponse","id":"branch-list-query-response"},{"title":"Brand","id":"brand"},{"title":"Callout","id":"callout"},{"title":"Channel","id":"channel"},{"title":"Chat","id":"chat"},{"title":"ChatMessage","id":"chat-message"},{"title":"ChatMessageResponse","id":"chat-message-response"},{"title":"ChatResponse","id":"chat-response"},{"title":"ChatTypingStatusChangedResponse","id":"chat-typing-status-changed-response"},{"title":"ChatUser","id":"chat-user"},{"title":"ChatUserDetails","id":"chat-user-details"},{"title":"Checklist","id":"checklist"},{"title":"ChecklistItem","id":"checklist-item"},{"title":"CheckReport","id":"check-report"},{"title":"ClientPortal","id":"client-portal"},{"title":"ClientPortalComment","id":"client-portal-comment"},{"title":"ClientPortalNotification","id":"client-portal-notification"},{"title":"ClientPortalUser","id":"client-portal-user"},{"title":"clientPortalUsersListResponse","id":"client-portal-users-list-response"},{"title":"ColumnConfigItem","id":"column-config-item"},{"title":"Comment","id":"comment"},{"title":"CommentResponse","id":"comment-response"},{"title":"CompaniesListResponse","id":"companies-list-response"},{"title":"Company","id":"company"},{"title":"Config","id":"config"},{"title":"ConfigDay","id":"config-day"},{"title":"Conformity","id":"conformity"},{"title":"Conversation","id":"conversation"},{"title":"ConversationAdminMessageInsertedResponse","id":"conversation-admin-message-inserted-response"},{"title":"ConversationChangedResponse","id":"conversation-changed-response"},{"title":"ConversationClientTypingStatusChangedResponse","id":"conversation-client-typing-status-changed-response"},{"title":"ConversationDetailResponse","id":"conversation-detail-response"},{"title":"ConversationMessage","id":"conversation-message"},{"title":"ConvertTo","id":"convert-to"},{"title":"Coordinate","id":"coordinate"},{"title":"Customer","id":"customer"},{"title":"CustomerConnectionChangedResponse","id":"customer-connection-changed-response"},{"title":"CustomersListResponse","id":"customers-list-response"},{"title":"Dashboard","id":"dashboard"},{"title":"DashboardItem","id":"dashboard-item"},{"title":"DashboardListResponse","id":"dashboard-list-response"},{"title":"Deal","id":"deal"},{"title":"DealListItem","id":"deal-list-item"},{"title":"DealTotalCurrency","id":"deal-total-currency"},{"title":"DeliveryList","id":"delivery-list"},{"title":"DeliveryReport","id":"delivery-report"},{"title":"Department","id":"department"},{"title":"DepartmentListQueryResponse","id":"department-list-query-response"},{"title":"DeviceConfig","id":"device-config"},{"title":"DeviceConfigsListResponse","id":"device-configs-list-response"},{"title":"Document","id":"document"},{"title":"DocumentEditorAttribute","id":"document-editor-attribute"},{"title":"Donate","id":"donate"},{"title":"DonateCampaign","id":"donate-campaign"},{"title":"DonateMain","id":"donate-main"},{"title":"Email","id":"email"},{"title":"EmailDelivery","id":"email-delivery"},{"title":"EmailDeliveryList","id":"email-delivery-list"},{"title":"EmailTemplate","id":"email-template"},{"title":"EngageData","id":"engage-data"},{"title":"EngageDeliveryReport","id":"engage-delivery-report"},{"title":"EngageLog","id":"engage-log"},{"title":"EngageMessage","id":"engage-message"},{"title":"EngageMessageSms","id":"engage-message-sms"},{"title":"EngageScheduleDate","id":"engage-schedule-date"},{"title":"ENV","id":"env"},{"title":"Error","id":"error"},{"title":"Exm","id":"exm"},{"title":"ExmAppearance","id":"exm-appearance"},{"title":"ExmCeremonyData","id":"exm-ceremony-data"},{"title":"ExmEventData","id":"exm-event-data"},{"title":"ExmFeature","id":"exm-feature"},{"title":"ExmFeed","id":"exm-feed"},{"title":"ExmFeedResponse","id":"exm-feed-response"},{"title":"ExmList","id":"exm-list"},{"title":"ExmThank","id":"exm-thank"},{"title":"ExmThankResponse","id":"exm-thank-response"},{"title":"ExmWelcomeContent","id":"exm-welcome-content"},{"title":"ExportHistory","id":"export-history"},{"title":"ExportHistoryList","id":"export-history-list"},{"title":"FacebookComment","id":"facebook-comment"},{"title":"FacebookConversationMessage","id":"facebook-conversation-message"},{"title":"FacebookCustomer","id":"facebook-customer"},{"title":"FacebookPost","id":"facebook-post"},{"title":"Field","id":"field"},{"title":"FieldsGroup","id":"fields-group"},{"title":"Form","id":"form"},{"title":"FormCode","id":"form-code"},{"title":"FormConnectResponse","id":"form-connect-response"},{"title":"FormSubmission","id":"form-submission"},{"title":"GrowthHack","id":"growth-hack"},{"title":"IMap","id":"i-map"},{"title":"IMapIntegration","id":"i-map-integration"},{"title":"ImportHistory","id":"import-history"},{"title":"ImportHistoryList","id":"import-history-list"},{"title":"InboxField","id":"inbox-field"},{"title":"Integration","id":"integration"},{"title":"integrationsGetUsedTypes","id":"integrations-get-used-types"},{"title":"integrationsTotalCount","id":"integrations-total-count"},{"title":"InternalNote","id":"internal-note"},{"title":"InternalNotesByAction","id":"internal-notes-by-action"},{"title":"Invoice","id":"invoice"},{"title":"invoicesTotalCount","id":"invoices-total-count"},{"title":"ItemSourceLocation","id":"item-source-location"},{"title":"IUserAbsenceInfo","id":"iuser-absence-info"},{"title":"KnowledgebaseApp","id":"knowledgebase-app"},{"title":"KnowledgeBaseArticle","id":"knowledge-base-article"},{"title":"KnowledgeBaseCategory","id":"knowledge-base-category"},{"title":"KnowledgeBaseLoader","id":"knowledge-base-loader"},{"title":"KnowledgeBaseParentCategory","id":"knowledge-base-parent-category"},{"title":"KnowledgeBaseTopic","id":"knowledge-base-topic"},{"title":"LeadApp","id":"lead-app"},{"title":"List","id":"list"},{"title":"LocationOption","id":"location-option"},{"title":"Log","id":"log"},{"title":"Logic","id":"logic"},{"title":"LogList","id":"log-list"},{"title":"Lottery","id":"lottery"},{"title":"LotteryCampaign","id":"lottery-campaign"},{"title":"LotteryMain","id":"lottery-main"},{"title":"Loyalty","id":"loyalty"},{"title":"LoyaltyConfig","id":"loyalty-config"},{"title":"MailAttachment","id":"mail-attachment"},{"title":"MailConfig","id":"mail-config"},{"title":"MailData","id":"mail-data"},{"title":"ManualVerificationConfig","id":"manual-verification-config"},{"title":"MessengerApp","id":"messenger-app"},{"title":"MessengerAppsResponse","id":"messenger-apps-response"},{"title":"MessengerConnectResponse","id":"messenger-connect-response"},{"title":"MessengerSupportersResponse","id":"messenger-supporters-response"},{"title":"ModifiedNote","id":"modified-note"},{"title":"Movement","id":"movement"},{"title":"MovementItem","id":"movement-item"},{"title":"Notification","id":"notification"},{"title":"NotificationConfig","id":"notification-config"},{"title":"NotificationConfiguration","id":"notification-configuration"},{"title":"ObjectListConfig","id":"object-list-config"},{"title":"OnboardingGetAvailableFeaturesResponse","id":"onboarding-get-available-features-response"},{"title":"OnboardingHistory","id":"onboarding-history"},{"title":"OnboardingNotification","id":"onboarding-notification"},{"title":"OTPConfig","id":"otp-config"},{"title":"PayDate","id":"pay-date"},{"title":"Payment","id":"payment"},{"title":"PaymentConfig","id":"payment-config"},{"title":"PaymentConfigList","id":"payment-config-list"},{"title":"paymentsTotalCount","id":"payments-total-count"},{"title":"Permission","id":"permission"},{"title":"PermissionAction","id":"permission-action"},{"title":"PermissionModule","id":"permission-module"},{"title":"Pipeline","id":"pipeline"},{"title":"PipelineChangeResponse","id":"pipeline-change-response"},{"title":"PipelineLabel","id":"pipeline-label"},{"title":"PipelineTemplate","id":"pipeline-template"},{"title":"PipelineTemplateStage","id":"pipeline-template-stage"},{"title":"Product","id":"product"},{"title":"ProductCategory","id":"product-category"},{"title":"ProductsConfig","id":"products-config"},{"title":"Report","id":"report"},{"title":"ReportsListResponse","id":"reports-list-response"},{"title":"RequestsListResponse","id":"requests-list-response"},{"title":"ResponseTemplate","id":"response-template"},{"title":"RobotEntry","id":"robot-entry"},{"title":"Rule","id":"rule"},{"title":"SaveFormResponse","id":"save-form-response"},{"title":"Schedule","id":"schedule"},{"title":"ScheduleConfig","id":"schedule-config"},{"title":"ScheduleReport","id":"schedule-report"},{"title":"SchedulesListResponse","id":"schedules-list-response"},{"title":"SchemaField","id":"schema-field"},{"title":"ScoreLog","id":"score-log"},{"title":"Script","id":"script"},{"title":"SeenInfo","id":"seen-info"},{"title":"Segment","id":"segment"},{"title":"ShiftsRequest","id":"shifts-request"},{"title":"Skill","id":"skill"},{"title":"SkillType","id":"skill-type"},{"title":"SmsDelivery","id":"sms-delivery"},{"title":"SmsStatus","id":"sms-status"},{"title":"Spin","id":"spin"},{"title":"SpinCampaign","id":"spin-campaign"},{"title":"SpinMain","id":"spin-main"},{"title":"Stage","id":"stage"},{"title":"Structure","id":"structure"},{"title":"Styles","id":"styles"},{"title":"Submission","id":"submission"},{"title":"SuccessResult","id":"success-result"},{"title":"Tag","id":"tag"},{"title":"Task","id":"task"},{"title":"TaskListItem","id":"task-list-item"},{"title":"Ticket","id":"ticket"},{"title":"TicketListItem","id":"ticket-list-item"},{"title":"Timeclock","id":"timeclock"},{"title":"TimeClocksListResponse","id":"time-clocks-list-response"},{"title":"Timelog","id":"timelog"},{"title":"TimelogListResponse","id":"timelog-list-response"},{"title":"TimeTrack","id":"time-track"},{"title":"TotalForType","id":"total-for-type"},{"title":"Trigger","id":"trigger"},{"title":"Unit","id":"unit"},{"title":"UnitListQueryResponse","id":"unit-list-query-response"},{"title":"Uom","id":"uom"},{"title":"User","id":"user"},{"title":"UserConversationListResponse","id":"user-conversation-list-response"},{"title":"UserDetailsType","id":"user-details-type"},{"title":"UserMovement","id":"user-movement"},{"title":"UserNotificationSettings","id":"user-notification-settings"},{"title":"UserReport","id":"user-report"},{"title":"UsersGroup","id":"users-group"},{"title":"UserStatus","id":"user-status"},{"title":"VerificationRequest","id":"verification-request"},{"title":"VideoCallData","id":"video-call-data"},{"title":"Voucher","id":"voucher"},{"title":"VoucherCampaign","id":"voucher-campaign"},{"title":"VoucherMain","id":"voucher-main"},{"title":"WebbuilderContentType","id":"webbuilder-content-type"},{"title":"WebbuilderContentTypesList","id":"webbuilder-content-types-list"},{"title":"WebbuilderEntriesList","id":"webbuilder-entries-list"},{"title":"WebbuilderEntry","id":"webbuilder-entry"},{"title":"WebbuilderPage","id":"webbuilder-page"},{"title":"WebbuilderPagesList","id":"webbuilder-pages-list"},{"title":"WebbuilderSite","id":"webbuilder-site"},{"title":"WebbuilderTemplate","id":"webbuilder-template"},{"title":"WebSiteApp","id":"web-site-app"},{"title":"Zalo","id":"zalo"},{"title":"ZaloAccount","id":"zalo-account"},{"title":"ZaloAttachmentPayload","id":"zalo-attachment-payload"},{"title":"ZaloAttachments","id":"zalo-attachments"},{"title":"ZaloConversationMessage","id":"zalo-conversation-message"}],"/quickstart/groups/ubuntu":[{"title":"Installing dependencies using docker","id":"installing-dependencies-using-docker"},{"title":"If your browser don't automatically jump to localhost:3000, you should check logs by using these commands.","id":"if-your-browser-dont-automatically-jump-to-localhost-3000-you-should-check-logs-by-using-these-commands"},{"title":"If you see this screen, you have successfully install erxes XOS. Congratulations πŸŽ‰πŸŽ‰πŸŽ‰","id":"if-you-see-this-screen-you-have-successfully-install-erxes-xos-congratulations"}],"/quickstart/deployment/deploymentDockerCompose":[{"title":"Perquisite","id":"perquisite"},{"title":"Summarize of the progress","id":"summarize-of-the-progress"}],"/groups/products/exm":[{"title":"Employee Experience Management","id":"employee-experience-management"},{"title":"Centralized complete HR database","id":"centralized-complete-hr-database"},{"title":"Knowledge, onboarding & training campaign","id":"knowledge-onboarding-and-training-campaign"},{"title":"Automatically generated HR templates","id":"automatically-generated-hr-templates"},{"title":"Fully customizable recruitment pipeline","id":"fully-customizable-recruitment-pipeline"},{"title":"The next generation of time-tracking system","id":"the-next-generation-of-time-tracking-system"},{"title":"Clear view of employee productivity","id":"clear-view-of-employee-productivity"},{"title":"Employee feed & Communication","id":"employee-feed-and-communication"},{"title":"Quick and efficient asset record","id":"quick-and-efficient-asset-record"}],"/groups/products/script":[{"title":"Script Installation","id":"script-installation"}],"/requirement/customer/finance":[],"/requirement/customer/sales":[],"/requirement/employee/attendance":[],"/requirement/customer/frontline":[],"/requirement/customer/marketing":[],"/requirement/employee/reports":[{"title":"Architecture","id":"architecture"},{"title":"Frequently Asked Questions","id":"faq"}],"/requirement/employee/learning":[{"title":"Architecture","id":"architecture"},{"title":"Frequently Asked Questions","id":"faq"}],"/requirement/employee/database":[],"/authentication/reference/ui/animated":[{"title":"Height and width","id":"height-and-width"},{"title":"Color","id":"color"},{"title":"Round","id":"round"},{"title":"Box","id":"box"},{"title":"With image","id":"with-image"},{"title":"API","id":"api"}],"/requirement/employee/structure":[{"title":"Architecture","id":"architecture"},{"title":"Frequently Asked Questions","id":"faq"}],"/requirement/employee/engagement":[{"title":"Architecture","id":"architecture"},{"title":"Frequently Asked Questions","id":"faq"}],"/requirement/employee/user":[{"title":"Architecture","id":"architecture"},{"title":"Frequently Asked Questions","id":"faq"}],"/authentication/reference/ui/attachment":[{"title":"Example","id":"example"},{"title":"Size","id":"size"},{"title":"Additional item","id":"additional-item"},{"title":"Image","id":"image"},{"title":"Video","id":"video"},{"title":"Audio","id":"audio"},{"title":"Multiple attachments","id":"multiple-attachments"},{"title":"Index","id":"index"},{"title":"API","id":"api"}],"/authentication/reference/ui/avatar":[{"title":"Default avatar","id":"default-avatar"},{"title":"Avatar","id":"avatar"},{"title":"API","id":"api"}],"/authentication/reference/ui/box":[{"title":"Title","id":"title"},{"title":"State","id":"state"},{"title":"Collapsible","id":"collapsible"},{"title":"Extra buttons","id":"extra-buttons"},{"title":"API","id":"api"}],"/authentication/reference/ui/breadcrumb":[{"title":"Example","id":"example"},{"title":"API","id":"api"}],"/authentication/reference/ui/buttons":[{"title":"Examples","id":"examples"},{"title":"API","id":"api"}],"/authentication/reference/ui/chip":[{"title":"Examples","id":"examples"},{"title":"Capitalized","id":"capitalized"},{"title":"Front Content","id":"front-content"},{"title":"API","id":"api"}],"/authentication/reference/ui/collapse":[{"title":"Simple","id":"simple"},{"title":"Description","id":"description"},{"title":"Image","id":"image"},{"title":"Image background color","id":"image-background-color"},{"title":"Before title","id":"before-title"},{"title":"ContendId","id":"contend-id"},{"title":"Compact","id":"compact"},{"title":"Open","id":"open"},{"title":"** API**","id":"api"}],"/authentication/reference/ui/data":[{"title":"Loading","id":"loading"},{"title":"Count","id":"count"},{"title":"Empty content","id":"empty-content"},{"title":"Empty state","id":"empty-state"},{"title":"Image","id":"image"},{"title":"API","id":"api"}],"/authentication/reference/ui/econtent":[{"title":"Steps","id":"steps"},{"title":"Button","id":"button"},{"title":"Button text","id":"button-text"},{"title":"isOutside","id":"is-outside"},{"title":"Icon","id":"icon"},{"title":"Title","id":"title"},{"title":"Url","id":"url"},{"title":"Vertical","id":"vertical"},{"title":"Max width","id":"max-width"},{"title":"API","id":"api"}],"/authentication/reference/ui/divider":[{"title":"Example","id":"example"},{"title":"API","id":"api"}],"/authentication/reference/ui/error":[{"title":"Example","id":"example"},{"title":"API","id":"api"}],"/authentication/reference/ui/estate":[{"title":"Light","id":"light"},{"title":"Icon size","id":"icon-size"},{"title":"Image","id":"image"},{"title":"Extra","id":"extra"},{"title":"API","id":"api"}],"/authentication/reference/ui/filterable":[{"title":"Example","id":"example"},{"title":"Blank filterable list","id":"blank-filterable-list"},{"title":"Loading","id":"loading"},{"title":"Custom style","id":"custom-style"},{"title":"Avatar","id":"avatar"},{"title":"Additional icon","id":"additional-icon"},{"title":"Links","id":"links"},{"title":"Show Checkmark","id":"show-checkmark"},{"title":"Tree view","id":"tree-view"},{"title":"API","id":"api"}],"/authentication/reference/ui/help":[{"title":"API","id":"api"}],"/authentication/reference/ui/filter":[{"title":"Example","id":"example"},{"title":"Empty","id":"empty"},{"title":"Loading","id":"loading"},{"title":"Count","id":"count"},{"title":"Multiple","id":"multiple"},{"title":"Searchable","id":"searchable"},{"title":"Icon","id":"icon"},{"title":"Color","id":"color"},{"title":"Tree view","id":"tree-view"},{"title":"Related","id":"related"},{"title":"API","id":"api"}],"/authentication/reference/ui/header":[{"title":"Example","id":"example"},{"title":"API","id":"api"}],"/authentication/reference/ui/file":[{"title":"Default file preview","id":"default-file-preview"},{"title":"File preview","id":"file-preview"},{"title":"Picture and Video preview","id":"picture-and-video-preview"},{"title":"API","id":"api"}],"/authentication/reference/ui/modifiable":[{"title":"Options","id":"options"},{"title":"Add button label","id":"add-button-label"},{"title":"API","id":"api"}],"/authentication/reference/ui/info":[{"title":"Examples","id":"examples"},{"title":"Icon","id":"icon"},{"title":"API","id":"api"}],"/authentication/reference/ui/icon":[{"title":"Color","id":"color"},{"title":"Size","id":"size"},{"title":"Active","id":"active"},{"title":"API","id":"api"}],"/authentication/reference/ui/label":[{"title":"Style","id":"style"},{"title":"Color","id":"color"},{"title":"API","id":"api"}],"/authentication/reference/ui/spinner":[{"title":"Examples","id":"examples"},{"title":"Position","id":"position"},{"title":"Objective","id":"objective"},{"title":"API","id":"api"}],"/authentication/reference/ui/namecard":[{"title":"Username","id":"username"},{"title":"Full name","id":"full-name"},{"title":"Avatar size","id":"avatar-size"},{"title":"User E-mail","id":"user-e-mail"},{"title":"Single Line","id":"single-line"},{"title":"Second line","id":"second-line"},{"title":"API","id":"api"}],"/authentication/reference/ui/progress":[{"title":"Percentage","id":"percentage"},{"title":"Color","id":"color"},{"title":"Height","id":"height"},{"title":"**Close","id":"close"},{"title":"API","id":"api"}],"/authentication/reference/ui/table":[{"title":"Bordered","id":"bordered"},{"title":"Striped","id":"striped"},{"title":"Table hover","id":"table-hover"},{"title":"White-space","id":"white-space"},{"title":"API","id":"api"}],"/authentication/reference/ui/sort":[{"title":"Label","id":"label"},{"title":"Field","id":"field"},{"title":"API","id":"api"}],"/authentication/reference/ui/steps":[{"title":"Example","id":"example"},{"title":"Active","id":"active"},{"title":"Step","id":"step"},{"title":"Title","id":"title"},{"title":"No button","id":"no-button"},{"title":"API","id":"api"},{"title":"Step","id":"step-2"}],"/authentication/reference/ui/sub":[{"title":"Example","id":"example"},{"title":"Additional item","id":"additional-item"},{"title":"API","id":"api"}],"/authentication/reference/ui/tags":[{"title":"Color","id":"color"},{"title":"Limit","id":"limit"},{"title":"API","id":"api"}],"/authentication/reference/ui/tip":[{"title":"Tip text","id":"tip-text"},{"title":"Placement","id":"placement"},{"title":"API","id":"api"}],"/authentication/reference/ui/tabs":[{"title":"Dynamic tabbed interfaces.","id":"dynamic-tabbed-interfaces"},{"title":"Example","id":"example"},{"title":"Full","id":"full"},{"title":"Border","id":"border"},{"title":"API","id":"api"},{"title":"TabTitle","id":"tab-title"}],"/authentication/reference/ui/text":[{"title":"Types","id":"types"},{"title":"Size","id":"size"},{"title":"API","id":"api"}],"/requirement/customer/finance/leasing":[],"/authentication/reference/ui/uploader":[{"title":"Multiple","id":"multiple"},{"title":"Limit","id":"limit"},{"title":"Single","id":"single"},{"title":"Default files","id":"default-files"},{"title":"API","id":"api"}],"/requirement/customer/finance/payment":[],"/authentication/reference/ui/timer":[{"title":"Completed time tracking","id":"completed-time-tracking"},{"title":"Started time tracking","id":"started-time-tracking"},{"title":"Paused time tracking","id":"paused-time-tracking"},{"title":"Stopped time tracking","id":"stopped-time-tracking"},{"title":"API","id":"api"}],"/requirement/customer/finance/rental":[],"/authentication/reference/ui/toggle":[{"title":"Example","id":"example"},{"title":"Icons","id":"icons"},{"title":"Checked","id":"checked"},{"title":"Always checked","id":"always-checked"},{"title":"Disabled toggle switch","id":"disabled-toggle-switch"},{"title":"API","id":"api"}],"/requirement/customer/sales/catering":[],"/requirement/customer/sales/ecommerce":[{"title":"Architecture","id":"architecture"},{"title":"Frequently Asked Questions","id":"faq"}],"/requirement/customer/sales/pos":[{"title":"Architecture","id":"architecture"},{"title":"Frequently Asked Questions","id":"faq"}],"/requirement/customer/sales/order":[],"/requirement/customer/sales/b2bs":[],"/requirement/customer/frontline/help":[],"/requirement/customer/frontline/callcenter":[],"/requirement/customer/sales/schedule":[],"/requirement/customer/frontline/chatbot":[],"/requirement/customer/frontline/ticket":[],"/requirement/customer/marketing/newsletter":[],"/requirement/customer/marketing/survey":[],"/requirement/customer/sales/ecommerce/customer":[{"title":"Architecture","id":"architecture"},{"title":"Frequently Asked Questions","id":"faq"}],"/requirement/customer/sales/ecommerce/order":[{"title":"Architecture","id":"architecture"},{"title":"Frequently Asked Questions","id":"faq"}],"/requirement/customer/sales/ecommerce/product":[{"title":"Architecture","id":"architecture"},{"title":"Frequently Asked Questions","id":"faq"}],"/requirement/customer/sales/ecommerce/promotions":[{"title":"Architecture","id":"architecture"},{"title":"Frequently Asked Questions","id":"faq"}],"/requirement/customer/sales/ecommerce/shopping":[{"title":"Architecture","id":"architecture"},{"title":"Frequently Asked Questions","id":"faq"}],"/requirement/customer/sales/ecommerce/payment":[{"title":"Architecture","id":"architecture"},{"title":"Frequently Asked Questions","id":"faq"}],"/requirement/customer/sales/ecommerce/user":[{"title":"Architecture","id":"architecture"},{"title":"Frequently Asked Questions","id":"faq"}],"/requirement/customer/sales/pos/inventory":[{"title":"Architecture","id":"architecture"},{"title":"Frequently Asked Questions","id":"faq"}],"/requirement/customer/sales/pos/management":[{"title":"Architecture","id":"architecture"},{"title":"Frequently Asked Questions","id":"faq"}],"/requirement/customer/sales/pos/coffeeshop":[],"/requirement/customer/sales/pos/erkhet":[],"/requirement/customer/sales/pos/payment":[{"title":"Architecture","id":"architecture"},{"title":"Frequently Asked Questions","id":"faq"}],"/requirement/customer/sales/pos/processing":[{"title":"Architecture","id":"architecture"},{"title":"Frequently Asked Questions","id":"faq"}],"/requirement/customer/sales/pos/menu":[{"title":"Architecture","id":"architecture"},{"title":"Frequently Asked Questions","id":"faq"}],"/requirement/customer/sales/pos/reporting":[{"title":"Architecture","id":"architecture"},{"title":"Frequently Asked Questions","id":"faq"}],"/requirement/customer/sales/pos/restaurant":[{"title":"Architecture","id":"architecture"},{"title":"Frequently Asked Questions","id":"faq"}],"/requirement/customer/sales/pos/retail":[{"title":"Architecture","id":"architecture"},{"title":"Frequently Asked Questions","id":"faq"}]},"children":["$","$L7",null,{"parallelRouterKey":"children","segmentPath":["children"],"error":"$undefined","errorStyles":"$undefined","loading":"$undefined","loadingStyles":"$undefined","hasLoading":false,"template":["$","$L8",null,{}],"templateStyles":"$undefined","notFound":[["$","div",null,{"className":"absolute inset-0 -z-10 mx-0 max-w-none overflow-hidden","children":["$","div",null,{"className":"absolute left-1/2 top-0 ml-[-38rem] h-[25rem] w-[81.25rem] dark:[mask-image:linear-gradient(white,transparent)]","children":[["$","div",null,{"className":"absolute inset-0 bg-gradient-to-r from-[#4c1d95] to-[#c7d2fe] opacity-40 [mask-image:radial-gradient(farthest-side_at_top,gray,transparent)] dark:from-[#4c1d95]/30 dark:to-[#c7d2fe]/30 dark:opacity-100","children":["$","svg",null,{"aria-hidden":"true","className":"absolute inset-x-0 inset-y-[-50%] h-[200%] w-full skew-y-[-18deg] fill-black/40 stroke-black/50 mix-blend-overlay dark:fill-white/2.5 dark:stroke-white/5","children":[["$","defs",null,{"children":["$","pattern",null,{"id":":S1:","width":72,"height":56,"patternUnits":"userSpaceOnUse","x":-12,"y":4,"children":["$","path",null,{"d":"M.5 56V.5H72","fill":"none"}]}]}],["$","rect",null,{"width":"100%","height":"100%","strokeWidth":0,"fill":"url(#:S1:)"}],["$","svg",null,{"x":-12,"y":4,"className":"overflow-visible","children":[["$","rect","4-3",{"strokeWidth":"0","width":73,"height":57,"x":288,"y":168}],["$","rect","2-1",{"strokeWidth":"0","width":73,"height":57,"x":144,"y":56}],["$","rect","7-3",{"strokeWidth":"0","width":73,"height":57,"x":504,"y":168}],["$","rect","10-6",{"strokeWidth":"0","width":73,"height":57,"x":720,"y":336}]]}]]}]}],["$","svg",null,{"viewBox":"0 0 1113 440","aria-hidden":"true","className":"absolute left-1/2 top-0 ml-[-19rem] w-[69.5625rem] fill-white blur-[26px] dark:hidden","children":["$","path",null,{"d":"M.016 439.5s-9.5-300 434-300S882.516 20 882.516 20V0h230.004v439.5H.016Z"}]}]]}]}],["$","div",null,{"className":"mx-auto flex h-full max-w-xl flex-col items-center justify-center py-16 text-center","children":[["$","p",null,{"className":"text-sm font-semibold text-zinc-900 dark:text-white","children":"404"}],["$","h1",null,{"className":"mt-2 text-2xl font-bold text-zinc-900 dark:text-white","children":"Page not found"}],["$","p",null,{"className":"mt-2 text-base text-zinc-600 dark:text-zinc-400","children":"Sorry, we couldn’t find the page you’re looking for."}],["$","$L9",null,{"className":"inline-flex gap-0.5 justify-center overflow-hidden text-sm font-medium transition rounded-full bg-zinc-900 py-1 px-3 text-white hover:bg-zinc-700 dark:bg-violet-400/10 dark:text-violet-400 dark:ring-1 dark:ring-inset dark:ring-violet-400/20 dark:hover:bg-violet-400/10 dark:hover:text-violet-300 dark:hover:ring-violet-300 mt-8","href":"/","children":[false,"Back to docs",["$","svg",null,{"viewBox":"0 0 20 20","fill":"none","aria-hidden":"true","className":"mt-0.5 h-5 w-5 -mr-1","children":["$","path",null,{"stroke":"currentColor","strokeLinecap":"round","strokeLinejoin":"round","d":"m11.5 6.5 3 3.5m0 0-3 3.5m3-3.5h-9"}]}]]}]]}]],"notFoundStyles":[],"childProp":{"current":["$","$L7",null,{"parallelRouterKey":"children","segmentPath":["children","sdks","children"],"error":"$undefined","errorStyles":"$undefined","loading":"$undefined","loadingStyles":"$undefined","hasLoading":false,"template":["$","$L8",null,{}],"templateStyles":"$undefined","notFound":"$undefined","notFoundStyles":"$undefined","childProp":{"current":["$La",["$","article",null,{"className":"flex h-full flex-col pb-10 pt-16","children":[["$","div",null,{"className":"flex-auto prose dark:prose-invert [html_:where(&>*)]:mx-auto [html_:where(&>*)]:max-w-2xl [html_:where(&>*)]:lg:mx-[calc(50%-min(50%,theme(maxWidth.lg)))] [html_:where(&>*)]:lg:max-w-3xl","children":[["$","div",null,{"className":"absolute inset-0 -z-10 mx-0 max-w-none overflow-hidden","children":["$","div",null,{"className":"absolute left-1/2 top-0 ml-[-38rem] h-[25rem] w-[81.25rem] dark:[mask-image:linear-gradient(white,transparent)]","children":[["$","div",null,{"className":"absolute inset-0 bg-gradient-to-r from-[#4c1d95] to-[#c7d2fe] opacity-40 [mask-image:radial-gradient(farthest-side_at_top,gray,transparent)] dark:from-[#4c1d95]/30 dark:to-[#c7d2fe]/30 dark:opacity-100","children":["$","svg",null,{"aria-hidden":"true","className":"absolute inset-x-0 inset-y-[-50%] h-[200%] w-full skew-y-[-18deg] fill-black/40 stroke-black/50 mix-blend-overlay dark:fill-white/2.5 dark:stroke-white/5","children":[["$","defs",null,{"children":["$","pattern",null,{"id":":S2:","width":72,"height":56,"patternUnits":"userSpaceOnUse","x":-12,"y":4,"children":["$","path",null,{"d":"M.5 56V.5H72","fill":"none"}]}]}],["$","rect",null,{"width":"100%","height":"100%","strokeWidth":0,"fill":"url(#:S2:)"}],["$","svg",null,{"x":-12,"y":4,"className":"overflow-visible","children":[["$","rect","4-3",{"strokeWidth":"0","width":73,"height":57,"x":288,"y":168}],["$","rect","2-1",{"strokeWidth":"0","width":73,"height":57,"x":144,"y":56}],["$","rect","7-3",{"strokeWidth":"0","width":73,"height":57,"x":504,"y":168}],["$","rect","10-6",{"strokeWidth":"0","width":73,"height":57,"x":720,"y":336}]]}]]}]}],["$","svg",null,{"viewBox":"0 0 1113 440","aria-hidden":"true","className":"absolute left-1/2 top-0 ml-[-19rem] w-[69.5625rem] fill-white blur-[26px] dark:hidden","children":["$","path",null,{"d":"M.016 439.5s-9.5-300 434-300S882.516 20 882.516 20V0h230.004v439.5H.016Z"}]}]]}]}],"\n",["$","h1",null,{"children":"Development"}],"\n",["$","$Lb",null,{}],"\n",["$","$Lc",null,{"level":2,"id":"create-general-plugin","children":"Create General Plugin"}],"\n",["$","p",null,{"children":["With erxes, you can create your own plugins or extend the existing ones, which would help you to enhance your experience and increase your revenue by adding the value on your products/services or selling it on our ",["$","$L9",null,{"href":"https://erxes.io/marketplace","children":"our marketplace"}],["$","script",null,{"src":"https://cdn.jsdelivr.net/gh/eddymens/markdown-external-link-script@v2.0.0/main.min.js"}],". This guideline will help you to develop your own plugins."]}],"\n",["$","div",null,{"className":"text-base my-3 flex flex-col border-l-4 border-amber-500 bg-amber-100 p-4 leading-6 text-amber-950 dark:border-amber-500 dark:bg-amber-200 dark:text-amber-950 dark:[--tw-prose-links-hover:theme(colors.amber.300)] dark:[--tw-prose-links:theme(colors.amber)]","children":[["$","p",null,{"className":"text-lg p-0 mt-0 mb-2 font-bold text-amber-500 dark:text-amber-500","children":"βœ‹ Caution"}],["$","div",null,{"className":"[&>:first-child]:mt-0 [&>:last-child]:mb-0","children":["$","ul",null,{"children":["\n",["$","li",null,{"children":["Before you start developing your own plugins, ensure there is no plugins with the same name or similar name in our marketplace that would bring any confusion as the name would be used many places starting from your ",["$","$Ld",null,{"children":"API"}],", ",["$","$Ld",null,{"children":"GraphQL"}],",",["$","$Ld",null,{"children":"query"}],", ",["$","$Ld",null,{"children":"mutation"}],", etc."]}],"\n",["$","li",null,{"children":"Name must be in small letters with no symbols and space in between."}],"\n",["$","li",null,{"children":["Name of All your ",["$","$Ld",null,{"children":"GraphQL"}]," type, ",["$","$Ld",null,{"children":"query"}],", ",["$","$Ld",null,{"children":"mutation"}]," must start with your plugin name."]}],"\n",["$","li",null,{"children":"Names of your database collection also must start with your plugin name."}],"\n",["$","li",null,{"children":["Name of your ",["$","strong",null,{"children":"UIroutes"}]," or ",["$","$Ld",null,{"children":"url"}],"-s also must be start with you pluging name."]}],"\n"]}]}]]}],"\n",["$","h1",null,{"children":"Installing erxes"}],"\n",["$","hr",null,{}],"\n",["$","p",null,{"children":["Please go to ",["$","$L9",null,{"href":"/quickstart/groups","children":"the installation"}]," guideline to install erxes XOS, but no need to run the erxes with the same direction."]}],"\n",["$","div",null,{"className":"text-base my-3 flex flex-col border-l-4 border-red-600 bg-red-100 p-4 leading-6 text-zinc-950 dark:border-red-500/30 dark:bg-red-500/5 dark:text-red-200 dark:[--tw-prose-links-hover:theme(colors.red.300)] dark:[--tw-prose-links:theme(colors.red)]","children":[["$","p",null,{"className":"text-lg p-0 mt-0 mb-2 font-bold text-red-600 dark:text-red-200","children":"❌ Danger"}],["$","div",null,{"className":"[&>:first-child]:mt-0 [&>:last-child]:mb-0","children":["$","p",null,{"children":["We assume you've already installed erxes XOS on your device. Otherwise the guideline below would not work out properly. Please make sure you should be back after you install erxes XOS using ",["$","$L9",null,{"href":"/quickstart/groups","children":"the installation guideline."}]]}]}]]}],"\n",["$","div",null,{"className":"my-16 xl:max-w-none","children":[["$","$Lc",null,{"level":2,"id":"official-libraries","children":"Plugin API"}],["$","p",null,{"children":"Plugin development in API part requires the following software prerequisites to be already installed on your computer."}],["$","div",null,{"className":"not-prose mt-4 grid grid-cols-1 gap-x-6 gap-y-10 border-t border-zinc-900/5 pt-10 dark:border-white/5 sm:grid-cols-2 xl:max-w-none xl:grid-cols-3","children":[["$","div","Typescript",{"className":"flex flex-row-reverse gap-6","children":[["$","div",null,{"className":"flex-auto","children":[["$","h3",null,{"className":"text-sm font-semibold text-zinc-900 dark:text-white","children":"Typescript"}],["$","p",null,{"className":"mt-1 text-sm text-zinc-600 dark:text-zinc-400","children":"TypeScript is a popular choice for programmers accustomed to other languages with static typing, such as C# and Java."}],["$","p",null,{"className":"mt-4","children":["$","$L9",null,{"className":"inline-flex gap-0.5 justify-center overflow-hidden text-sm font-medium transition text-violet-500 hover:text-violet-600 dark:text-violet-400 dark:hover:text-violet-500","href":"#","children":[false,"Read more",["$","svg",null,{"viewBox":"0 0 20 20","fill":"none","aria-hidden":"true","className":"mt-0.5 h-5 w-5 relative top-px -mr-1","children":["$","path",null,{"stroke":"currentColor","strokeLinecap":"round","strokeLinejoin":"round","d":"m11.5 6.5 3 3.5m0 0-3 3.5m3-3.5h-9"}]}]]}]}]]}],["$","$Le",null,{"src":{"src":"/_next/static/media/typescript.27df170d.svg","height":2500,"width":2500,"blurWidth":0,"blurHeight":0},"alt":"","className":"h-12 w-12","unoptimized":true}]]}],["$","div","Graphql",{"className":"flex flex-row-reverse gap-6","children":[["$","div",null,{"className":"flex-auto","children":[["$","h3",null,{"className":"text-sm font-semibold text-zinc-900 dark:text-white","children":"Graphql"}],["$","p",null,{"className":"mt-1 text-sm text-zinc-600 dark:text-zinc-400","children":"GraphQL is a query language for your API, and a server-side runtime for executing queries using a type system you define for your data. "}],["$","p",null,{"className":"mt-4","children":["$","$L9",null,{"className":"inline-flex gap-0.5 justify-center overflow-hidden text-sm font-medium transition text-violet-500 hover:text-violet-600 dark:text-violet-400 dark:hover:text-violet-500","href":"#","children":[false,"Read more",["$","svg",null,{"viewBox":"0 0 20 20","fill":"none","aria-hidden":"true","className":"mt-0.5 h-5 w-5 relative top-px -mr-1","children":["$","path",null,{"stroke":"currentColor","strokeLinecap":"round","strokeLinejoin":"round","d":"m11.5 6.5 3 3.5m0 0-3 3.5m3-3.5h-9"}]}]]}]}]]}],["$","$Le",null,{"src":{"src":"/_next/static/media/graphql.6701d035.svg","height":64,"width":64,"blurWidth":0,"blurHeight":0},"alt":"","className":"h-12 w-12","unoptimized":true}]]}],["$","div","Express.js",{"className":"flex flex-row-reverse gap-6","children":[["$","div",null,{"className":"flex-auto","children":[["$","h3",null,{"className":"text-sm font-semibold text-zinc-900 dark:text-white","children":"Express.js"}],["$","p",null,{"className":"mt-1 text-sm text-zinc-600 dark:text-zinc-400","children":"Express is a minimal and flexible Node.js web application framework that provides a robust set of features for web and mobile applications. "}],["$","p",null,{"className":"mt-4","children":["$","$L9",null,{"className":"inline-flex gap-0.5 justify-center overflow-hidden text-sm font-medium transition text-violet-500 hover:text-violet-600 dark:text-violet-400 dark:hover:text-violet-500","href":"#","children":[false,"Read more",["$","svg",null,{"viewBox":"0 0 20 20","fill":"none","aria-hidden":"true","className":"mt-0.5 h-5 w-5 relative top-px -mr-1","children":["$","path",null,{"stroke":"currentColor","strokeLinecap":"round","strokeLinejoin":"round","d":"m11.5 6.5 3 3.5m0 0-3 3.5m3-3.5h-9"}]}]]}]}]]}],["$","$Le",null,{"src":{"src":"/_next/static/media/expressjs.136224ed.svg","height":64,"width":64,"blurWidth":0,"blurHeight":0},"alt":"","className":"h-12 w-12","unoptimized":true}]]}],["$","div","Mongodb",{"className":"flex flex-row-reverse gap-6","children":[["$","div",null,{"className":"flex-auto","children":[["$","h3",null,{"className":"text-sm font-semibold text-zinc-900 dark:text-white","children":"Mongodb"}],["$","p",null,{"className":"mt-1 text-sm text-zinc-600 dark:text-zinc-400","children":"MongoDB empowers innovators to create, transform, and disrupt industries by unleashing the power of software and data."}],["$","p",null,{"className":"mt-4","children":["$","$L9",null,{"className":"inline-flex gap-0.5 justify-center overflow-hidden text-sm font-medium transition text-violet-500 hover:text-violet-600 dark:text-violet-400 dark:hover:text-violet-500","href":"#","children":[false,"Read more",["$","svg",null,{"viewBox":"0 0 20 20","fill":"none","aria-hidden":"true","className":"mt-0.5 h-5 w-5 relative top-px -mr-1","children":["$","path",null,{"stroke":"currentColor","strokeLinecap":"round","strokeLinejoin":"round","d":"m11.5 6.5 3 3.5m0 0-3 3.5m3-3.5h-9"}]}]]}]}]]}],["$","$Le",null,{"src":{"src":"/_next/static/media/mongodb.514fe038.svg","height":64,"width":64,"blurWidth":0,"blurHeight":0},"alt":"","className":"h-12 w-12","unoptimized":true}]]}],["$","div","Redis",{"className":"flex flex-row-reverse gap-6","children":[["$","div",null,{"className":"flex-auto","children":[["$","h3",null,{"className":"text-sm font-semibold text-zinc-900 dark:text-white","children":"Redis"}],["$","p",null,{"className":"mt-1 text-sm text-zinc-600 dark:text-zinc-400","children":"Redis is an open source (BSD licensed), in-memory data structure store used as a database, cache, message broker, and streaming engine."}],["$","p",null,{"className":"mt-4","children":["$","$L9",null,{"className":"inline-flex gap-0.5 justify-center overflow-hidden text-sm font-medium transition text-violet-500 hover:text-violet-600 dark:text-violet-400 dark:hover:text-violet-500","href":"#","children":[false,"Read more",["$","svg",null,{"viewBox":"0 0 20 20","fill":"none","aria-hidden":"true","className":"mt-0.5 h-5 w-5 relative top-px -mr-1","children":["$","path",null,{"stroke":"currentColor","strokeLinecap":"round","strokeLinejoin":"round","d":"m11.5 6.5 3 3.5m0 0-3 3.5m3-3.5h-9"}]}]]}]}]]}],["$","$Le",null,{"src":{"src":"/_next/static/media/redis.3283c2ea.svg","height":64,"width":64,"blurWidth":0,"blurHeight":0},"alt":"","className":"h-12 w-12","unoptimized":true}]]}],["$","div","RabbitMQ",{"className":"flex flex-row-reverse gap-6","children":[["$","div",null,{"className":"flex-auto","children":[["$","h3",null,{"className":"text-sm font-semibold text-zinc-900 dark:text-white","children":"RabbitMQ"}],["$","p",null,{"className":"mt-1 text-sm text-zinc-600 dark:text-zinc-400","children":"RabbitMQ is a message-queueing software also known as a message broker or queue manager. "}],["$","p",null,{"className":"mt-4","children":["$","$L9",null,{"className":"inline-flex gap-0.5 justify-center overflow-hidden text-sm font-medium transition text-violet-500 hover:text-violet-600 dark:text-violet-400 dark:hover:text-violet-500","href":"#","children":[false,"Read more",["$","svg",null,{"viewBox":"0 0 20 20","fill":"none","aria-hidden":"true","className":"mt-0.5 h-5 w-5 relative top-px -mr-1","children":["$","path",null,{"stroke":"currentColor","strokeLinecap":"round","strokeLinejoin":"round","d":"m11.5 6.5 3 3.5m0 0-3 3.5m3-3.5h-9"}]}]]}]}]]}],["$","$Le",null,{"src":{"src":"/_next/static/media/rabbitmq.d84fde2a.svg","height":800,"width":800,"blurWidth":0,"blurHeight":0},"alt":"","className":"h-12 w-12","unoptimized":true}]]}]]}]]}],"\n",["$","div",null,{"className":"my-16 xl:max-w-none","children":[["$","$Lc",null,{"level":2,"id":"official-libraries","children":"Plugin UI"}],["$","p",null,{"children":"Plugin development in UI part requires the following software prerequisites to be already installed on your computer."}],["$","div",null,{"className":"not-prose mt-4 grid grid-cols-1 gap-x-6 gap-y-10 border-t border-zinc-900/5 pt-10 dark:border-white/5 sm:grid-cols-2 xl:max-w-none xl:grid-cols-3","children":[["$","div","Typescript",{"className":"flex flex-row-reverse gap-6","children":[["$","div",null,{"className":"flex-auto","children":[["$","h3",null,{"className":"text-sm font-semibold text-zinc-900 dark:text-white","children":"Typescript"}],["$","p",null,{"className":"mt-1 text-sm text-zinc-600 dark:text-zinc-400","children":"TypeScript is a popular choice for programmers accustomed to other languages with static typing, such as C# and Java."}],["$","p",null,{"className":"mt-4","children":["$","$L9",null,{"className":"inline-flex gap-0.5 justify-center overflow-hidden text-sm font-medium transition text-violet-500 hover:text-violet-600 dark:text-violet-400 dark:hover:text-violet-500","href":"#","children":[false,"Read more",["$","svg",null,{"viewBox":"0 0 20 20","fill":"none","aria-hidden":"true","className":"mt-0.5 h-5 w-5 relative top-px -mr-1","children":["$","path",null,{"stroke":"currentColor","strokeLinecap":"round","strokeLinejoin":"round","d":"m11.5 6.5 3 3.5m0 0-3 3.5m3-3.5h-9"}]}]]}]}]]}],["$","$Le",null,{"src":{"src":"/_next/static/media/typescript.27df170d.svg","height":2500,"width":2500,"blurWidth":0,"blurHeight":0},"alt":"","className":"h-12 w-12","unoptimized":true}]]}],["$","div","Webpack",{"className":"flex flex-row-reverse gap-6","children":[["$","div",null,{"className":"flex-auto","children":[["$","h3",null,{"className":"text-sm font-semibold text-zinc-900 dark:text-white","children":"Webpack"}],["$","p",null,{"className":"mt-1 text-sm text-zinc-600 dark:text-zinc-400","children":"Webpack is a module bundler. Webpack can take care of bundling alongside a separate task runner."}],["$","p",null,{"className":"mt-4","children":["$","$L9",null,{"className":"inline-flex gap-0.5 justify-center overflow-hidden text-sm font-medium transition text-violet-500 hover:text-violet-600 dark:text-violet-400 dark:hover:text-violet-500","href":"#","children":[false,"Read more",["$","svg",null,{"viewBox":"0 0 20 20","fill":"none","aria-hidden":"true","className":"mt-0.5 h-5 w-5 relative top-px -mr-1","children":["$","path",null,{"stroke":"currentColor","strokeLinecap":"round","strokeLinejoin":"round","d":"m11.5 6.5 3 3.5m0 0-3 3.5m3-3.5h-9"}]}]]}]}]]}],["$","$Le",null,{"src":{"src":"/_next/static/media/webpack.c815151d.svg","height":48,"width":48,"blurWidth":0,"blurHeight":0},"alt":"","className":"h-12 w-12","unoptimized":true}]]}],["$","div","ReactJS",{"className":"flex flex-row-reverse gap-6","children":[["$","div",null,{"className":"flex-auto","children":[["$","h3",null,{"className":"text-sm font-semibold text-zinc-900 dark:text-white","children":"ReactJS"}],["$","p",null,{"className":"mt-1 text-sm text-zinc-600 dark:text-zinc-400","children":"React lets you build user interfaces out of individual pieces called components."}],["$","p",null,{"className":"mt-4","children":["$","$L9",null,{"className":"inline-flex gap-0.5 justify-center overflow-hidden text-sm font-medium transition text-violet-500 hover:text-violet-600 dark:text-violet-400 dark:hover:text-violet-500","href":"#","children":[false,"Read more",["$","svg",null,{"viewBox":"0 0 20 20","fill":"none","aria-hidden":"true","className":"mt-0.5 h-5 w-5 relative top-px -mr-1","children":["$","path",null,{"stroke":"currentColor","strokeLinecap":"round","strokeLinejoin":"round","d":"m11.5 6.5 3 3.5m0 0-3 3.5m3-3.5h-9"}]}]]}]}]]}],["$","$Le",null,{"src":{"src":"/_next/static/media/react.d60a0182.svg","height":2500,"width":2194,"blurWidth":0,"blurHeight":0},"alt":"","className":"h-12 w-12","unoptimized":true}]]}]]}]]}],"\n",["$","h1",null,{"children":"Creating New Plugin"}],"\n",["$","p",null,{"children":["Each plugin is composed of two parts, ",["$","$Ld",null,{"children":"API"}]," and ",["$","$Ld",null,{"children":"UI"}]]}],"\n",["$","ul",null,{"children":["\n",["$","li",null,{"children":"Create new folders for both using the following command."}],"\n"]}],"\n",["$","$Lf",null,{"language":"bash","code":" cd erxes\n yarn create-plugin \n","children":["$","$Ld",null,{"className":"language-bash","children":" cd erxes\n yarn create-plugin \n"}],"title":""}],"\n",["$","p",null,{"children":["The command above starts CLI, prompting for few questions to create a new plugin as shown below. In this example we create plugin named document.\n",["$","img",null,{"src":"https://imagedelivery.net/5m26Aj-CutMXPPNacMs_yQ/6e30c8b7-5396-48a2-3b4a-8b2223080400/public","alt":""}]]}],"\n",["$","p",null,{"children":"The example below is a new plugin, created from an example template, placed at the main navigation."}],"\n",["$","p",null,{"children":["$","img",null,{"src":"https://imagedelivery.net/5m26Aj-CutMXPPNacMs_yQ/88dccd76-b921-441f-e903-7ff31b278900/public","alt":""}]}],"\n",["$","p",null,{"children":["Creating from an empty template will result in as shown below, as we give you the freedom and space to develop your own plugin on erxes.\n",["$","img",null,{"src":"https://imagedelivery.net/5m26Aj-CutMXPPNacMs_yQ/fe16b5f8-e361-4194-87ff-772616413300/public","alt":""}]]}],"\n",["$","h1",null,{"children":"API file structure"}],"\n",["$","p",null,{"children":"After creating a plugin, the following files are generated automatically in your new plugin API."}],"\n",["$","$Lf",null,{"language":"bash","code":"πŸ“¦plugin-document-api\n ┣ πŸ“‚src\n ┃ ┣ πŸ“‚graphql\n ┃ ┃ ┣ πŸ“‚resolvers \n ┃ ┃ ┃ ┣ index.ts\n ┃ ┃ ┃ ┣ mutations.ts\n ┃ ┃ ┃ β”— queries.ts\n ┃ ┃ ┣ index.ts\n ┃ ┃ β”— typeDefs.ts\n ┃ ┣ configs.ts\n ┃ ┣ messageBroker.ts\n ┃ β”— models.ts\n ┣ .env.sample\n ┣ package.json\n β”— tsconfig.json\n","children":["$","$Ld",null,{"className":"language-bash","children":"$10"}],"title":""}],"\n",["$","h3",null,{"children":"Main files"}],"\n",["$","p",null,{"children":"Following files are generated automatically in plugin-[pluginName]-api/src."}],"\n",["$","h3",null,{"children":"configs.ts"}],"\n",["$","p",null,{"children":"This file contains main configuration of a plugin."}],"\n",["$","$Lf",null,{"language":"bash","code":"// path: ./packages/plugin-[pluginName]-api/src/configs.ts \nimport typeDefs from './graphql/typeDefs';\nimport resolvers from './graphql/resolvers';\nimport { initBroker } from './messageBroker';\nexport let mainDb;\nexport let debug;\nexport let graphqlPubsub;\nexport let serviceDiscovery;\nexport default {\n name: '[pluginName]',\n graphql: async sd => {\n serviceDiscovery = sd;\n return {\n typeDefs: await typeDefs(sd),\n resolvers: await resolvers(sd)\n };\n },\n apolloServerContext: async (context) => {\n return context;\n },\n onServerInit: async options => {\n mainDb = options.db;\n initBroker(options.messageBrokerClient);\n graphqlPubsub = options.pubsubClient;\n debug = options.debug;\n }\n}\n","children":["$","$Ld",null,{"className":"language-bash","children":"$11"}],"title":"configs.ts file:"}],"\n",["$","h3",null,{"children":"messageBroker.ts"}],"\n",["$","p",null,{"children":"This file uses for connect with other plugins. You can see message broker functions from Common functions."}],"\n",["$","$Lf",null,{"language":"bash","code":"// path: ./packages/plugin-[pluginName]-api/src/messageBroker.ts \nimport { ISendMessageArgs, sendMessage } from \"@erxes/api-utils/src/core\";\nimport { serviceDiscovery } from \"./configs\";\nimport { Documents } from \"./models\";\nlet client;\nexport const initBroker = async cl => {\n client = cl;\n const { consumeQueue, consumeRPCQueue } = client;\n consumeQueue('document:send', async ({ data }) => {\n Documents.send(data);\n return {\n status: 'success',\n };\n });\n consumeRPCQueue('document:find', async ({ data }) => {\n return {\n status: 'success',\n data: await Documents.find({})\n };\n });\n};\nexport const sendCommonMessage = async (\n args: ISendMessageArgs & { serviceName: string }\n) => {\n return sendMessage({\n serviceDiscovery,\n client,\n ...args\n });\n};\nexport default function() {\n return client;\n}\n","children":["$","$Ld",null,{"className":"language-bash","children":"$12"}],"title":" messageBroker.ts file:"}],"\n",["$","h1",null,{"children":"GraphQL development"}],"\n",["$","p",null,{"children":["Inside ",["$","$Ld",null,{"children":"packages/plugin--api/src"}],", we have a ",["$","$Ld",null,{"children":"graphql"}]," folder. The folder contains code related to GraphQL."]}],"\n",["$","$Lf",null,{"language":"tsx","code":" πŸ“‚src\n ┣ πŸ“‚graphql\n ┃ ┣ πŸ“‚resolvers \n ┃ ┃ ┣ index.ts\n ┃ ┃ ┣ mutations.ts\n ┃ ┃ β”— queries.ts\n ┃ ┣ index.ts\n ┃ β”— typeDefs.ts\n","children":["$","$Ld",null,{"className":"language-tsx","children":"$13"}]}],"\n",["$","h3",null,{"children":"GraphQL resolvers"}],"\n",["$","p",null,{"children":["Inside ",["$","$Ld",null,{"children":"/graphql/resolvers/mutations"}]," GraphQL mutation codes."]}],"\n",["$","$Lf",null,{"language":"bash","code":"import { Documents, Types } from '../../models';\nimport { IContext } from \"@erxes/api-utils/src/types\"\nconst documentMutations = {\n/**\n * Creates a new document\n */\nasync documentsAdd(_root, doc, _context: IContext) {\n return Documents.createDocument(doc);\n},\n/**\n * Edits a new document\n */\nasync documentsEdit(\n _root,\n { _id, ...doc },\n _context: IContext\n) {\n return Documents.updateDocument(_id, doc);\n},\n/**\n * Removes a single document\n */\nasync documentsRemove(_root, { _id }, _context: IContext) {\n return Documents.removeDocument(_id);\n},\n/**\n * Creates a new type for document\n */\nasync documentTypesAdd(_root, doc, _context: IContext) {\n return Types.createType(doc);\n},\nasync documentTypesRemove(_root, { _id }, _context: IContext) {\n return Types.removeType(_id);\n},\nasync documentTypesEdit(\n _root,\n { _id, ...doc },\n _context: IContext\n) {\nreturn Types.updateType(_id, doc);\n}\n};\nexport default documentMutations;\n","children":["$","$Ld",null,{"className":"language-bash","children":"$14"}],"title":" mutation examples:"}],"\n",["$","h3",null,{"children":["Inside ",["$","$Ld",null,{"children":"/graphql/resolvers/queries"}]," folder contains GraphQL query codes."]}],"\n",["$","$Lf",null,{"language":"bash","code":"import { Documents, Types } from \"../../models\";\nimport { IContext } from \"@erxes/api-utils/src/types\"\nconst documentQueries = {\n documents(\n _root,\n {\n typeId\n },\n _context: IContext\n ) {\n const selector: any = {};\n if (typeId) {\n selector.typeId = typeId;\n }\n return Documents.find(selector).sort({ order: 1, name: 1 });\n },\n documentTypes(_root, _args, _context: IContext) {\n return Types.find({});\n },\n documentsTotalCount(_root, _args, _context: IContext) {\n return Documents.find({}).countDocuments();\n }\n};\nexport default documentQueries;\n","children":["$","$Ld",null,{"className":"language-bash","children":"$15"}],"title":" query examples:"}],"\n",["$","h3",null,{"children":"GraphQL typeDefs"}],"\n",["$","p",null,{"children":["Inside ",["$","$Ld",null,{"children":"/graphql/typeDefs.ts"}]," file contains GraphQL typeDefs."]}],"\n",["$","$Lf",null,{"language":"bash","code":"$16","children":["$","$Ld",null,{"className":"language-bash","children":"$17"}],"title":" typeDefs:"}],"\n",["$","p",null,{"children":["Database development\nInside ",["$","$Ld",null,{"children":"packages/plugin--api/src"}],", we have a ",["$","$Ld",null,{"children":"models"}]," file. The file contains code related to MongoDB and mongoose."]}],"\n",["$","$Lf",null,{"language":"tsx","code":"πŸ“‚src\nβ”— models.ts\n","children":["$","$Ld",null,{"className":"language-tsx","children":"πŸ“‚src\nβ”— models.ts\n"}]}],"\n",["$","h3",null,{"children":"Mongoose schema and model"}],"\n",["$","p",null,{"children":["Inside ",["$","$Ld",null,{"children":"src/models.ts"}],", file contains Mongoose schema and models."]}],"\n",["$","$Lf",null,{"language":"bash","code":"$18","children":["$","$Ld",null,{"className":"language-bash","children":"$19"}],"title":" Mongoose schema and model example:"}],"\n",["$","h1",null,{"children":"UI file structure"}],"\n",["$","p",null,{"children":["After creating new plugin using ",["$","$Ld",null,{"children":"yarn-create-plugin"}]," command, the following files are generated automatically in your new plugin UI."]}],"\n",["$","$Lf",null,{"language":"tsx","code":"πŸ“¦plugin-[pluginName]-ui\n ┣ πŸ“‚src\n ┃ ┣ πŸ“‚components\n ┃ ┃ ┣ Form.tsx\n ┃ ┃ ┣ List.tsx\n ┃ ┃ ┣ Row.tsx\n ┃ ┃ ┣ SideBar.tsx\n ┃ ┃ β”— TypeForm.tsx\n ┃ ┣ πŸ“‚containers\n ┃ ┃ ┣ List.tsx\n ┃ ┃ β”— SideBarList.tsx\n ┃ ┣ πŸ“‚graphql\n ┃ ┃ ┣ index.ts\n ┃ ┃ ┣ mutations.ts\n ┃ ┃ β”— queries.ts\n ┃ ┣ App.tsx\n ┃ ┣ configs.js\n ┃ ┣ generalRoutes.tsx\n ┃ ┣ index.js\n ┃ ┣ routes.tsx\n ┃ β”— types.ts\n","children":["$","$Ld",null,{"className":"language-tsx","children":"$1a"}]}],"\n",["$","h1",null,{"children":"Main files"}],"\n",["$","p",null,{"children":["Following files are generated automatically in ",["$","$Ld",null,{"children":"plugin-[pluginName]-ui/src."}]]}],"\n",["$","h3",null,{"children":"configs.js"}],"\n",["$","p",null,{"children":"Following file contains the main configs of plugin."}],"\n",["$","$Lf",null,{"language":"bash","code":"// path: ./packages/plugin-[pluginName]-ui/src/configs.js \n module.exports = {\n name: '[pluginName]',\n port: 3017,\n scope: '[pluginName]',\n exposes: {\n './routes': './src/routes.tsx'\n },\n routes: {\n url: 'http://localhost:3017/remoteEntry.js',\n scope: '[pluginName]',\n module: './routes'\n },\n menus:[\n {\n \"text\":\"[pluginName]\",\n \"url\":\"/[pluginUrl]\",\n \"icon\":\"icon-star\",\n \"location\":\"[mainNavigation or settings]\"\n }\n ]\n };\n","children":["$","$Ld",null,{"className":"language-bash","children":"$1b"}],"title":"configs.js file:"}],"\n",["$","h3",null,{"children":"routes.tsx"}],"\n",["$","p",null,{"children":"Following file contains routes of plugin UI."}],"\n",["$","$Lf",null,{"language":"bash","code":"// path: ./packages/plugin-[pluginName]-ui/src/routes.tsx \n import asyncComponent from '@erxes/ui/src/components/AsyncComponent';\n import queryString from 'query-string';\n import React from 'react';\n import { Route } from 'react-router-dom';\n const List = asyncComponent(() =>\n import(/* webpackChunkName: \"List - Documents\" */ './containers/List')\n );\n const documents = ({ location, history }) => {\n const queryParams = queryString.parse(location.search);\n const { type } = queryParams;\n return ;\n };\n const routes = () => {\n return ;\n };\n","children":["$","$Ld",null,{"className":"language-bash","children":"$1c"}],"title":"routes.tsx file:"}],"\n",["$","h3",null,{"children":"App.tsx"}],"\n",["$","p",null,{"children":"This file contains main component of application."}],"\n",["$","$Lf",null,{"language":"bash","code":"// path: ./packages/plugin-[pluginName]-ui/src/App.tsx \n import React from 'react';\n import GeneralRoutes from './generalRoutes';\n import { PluginLayout } from '@erxes/ui/src/styles/main';\n const App = () => {\n return (\n \n \n \n );\n };\n export default App;\n","children":["$","$Ld",null,{"className":"language-bash","children":"$1d"}],"title":"App.tsx file:"}],"\n",["$","h1",null,{"children":"UI development"}],"\n",["$","h3",null,{"children":"Components"}],"\n",["$","p",null,{"children":["Inside ",["$","$Ld",null,{"children":".src"}]," folder, we have a components folder. The folder contains main components of plugin."]}],"\n",["$","$Lf",null,{"language":"tsx","code":" πŸ“‚src\n ┣ πŸ“‚components\n ┃ ┣ Form.tsx\n ┃ ┣ List.tsx\n ┃ ┣ Row.tsx\n ┃ ┣ SideBar.tsx\n ┃ β”— TypeForm.tsx\n","children":["$","$Ld",null,{"className":"language-tsx","children":"$1e"}]}],"\n",["$","$Lf",null,{"language":"bash","code":"$1f","children":["$","$Ld",null,{"className":"language-bash","children":"$20"}],"title":"components example:"}],"\n",["$","h3",null,{"children":"Containers"}],"\n",["$","p",null,{"children":["Inside ",["$","$Ld",null,{"children":".src"}]," folder, we have a containers folder. The folder contains a component that contains codes related to API."]}],"\n",["$","$Lf",null,{"language":"tsx","code":" πŸ“‚src\n ┣ πŸ“‚containers\n ┃ ┣ List.tsx\n ┃ β”— SideBarList.tsx\n","children":["$","$Ld",null,{"className":"language-tsx","children":" πŸ“‚src\n ┣ πŸ“‚containers\n ┃ ┣ List.tsx\n ┃ β”— SideBarList.tsx\n"}]}],"\n",["$","$Lf",null,{"language":"bash","code":"$21","children":["$","$Ld",null,{"className":"language-bash","children":"$22"}],"title":"containers example:"}],"\n",["$","h3",null,{"children":"GraphQL"}],"\n",["$","p",null,{"children":["Inside ",["$","$Ld",null,{"children":".src"}]," folder, we have a ",["$","$Ld",null,{"children":"graphql"}]," folder. The folder contains code related to GraphQL."]}],"\n",["$","$Lf",null,{"language":"tsx","code":"πŸ“‚src\n ┣ πŸ“‚graphql\n ┃ ┣ index.ts\n ┃ ┣ mutations.ts\n ┃ β”— queries.ts\n","children":["$","$Ld",null,{"className":"language-tsx","children":"πŸ“‚src\n ┣ πŸ“‚graphql\n ┃ ┣ index.ts\n ┃ ┣ mutations.ts\n ┃ β”— queries.ts\n"}]}],"\n",["$","p",null,{"children":["Inside ",["$","$Ld",null,{"children":"/graphql/mutations.ts"}]," GraphQL mutation codes."]}],"\n",["$","$Lf",null,{"language":"bash","code":"$23","children":["$","$Ld",null,{"className":"language-bash","children":"$24"}],"title":"GraphQL mutation examples:"}],"\n",["$","h3",null,{"children":["Inside ",["$","$Ld",null,{"children":"/graphql/queries.ts"}]," GraphQL query codes."]}],"\n",["$","$Lf",null,{"language":"bash","code":"const list = `\n query listQuery($typeId: String) {\n documents(typeId: $typeId) {\n _id\n name\n expiryDate\n createdAt\n checked\n typeId\n currentType{\n _id\n name\n }\n }\n }\n`;\nconst listDocumentTypes = `\n query listDocumentTypeQuery{\n documentTypes{\n _id\n name\n }\n }\n`;\nconst totalCount = `\n query documentsTotalCount{\n documentsTotalCount\n }\n`;\nexport default {\n list,\n totalCount,\n listDocumentTypes\n};\n","children":["$","$Ld",null,{"className":"language-bash","children":"$25"}],"title":"GraphQL query examples:"}],"\n",["$","h1",null,{"children":"Configuring UI"}],"\n",["$","h1",null,{"children":"Running port for plugin"}],"\n",["$","p",null,{"children":["Inside ",["$","$Ld",null,{"children":"packages/plugin--ui/src/configs.js"}],", running port for plugin UI is set as shown below. Default value is 3017. Please note that each plugin has to have its UI running on an unique port. You may need to change the port manually (inside ",["$","$Ld",null,{"children":"configs.js"}],") if developing multiple plugins."]}],"\n",["$","$Lf",null,{"language":"tsx","code":"module.exports = {\n name: 'new_plugin',\n port: 3017,\n scope: 'new_plugin',\n exposes: {\n './routes': './src/routes.tsx'\n },\n routes: {\n url: 'http://localhost:3017/remoteEntry.js',\n scope: 'new_plugin',\n module: './routes'\n },\n menus: []\n};\n","children":["$","$Ld",null,{"className":"language-tsx","children":"$26"}]}],"\n",["$","h1",null,{"children":"Location for plugin"}],"\n",["$","p",null,{"children":["Inside ",["$","$Ld",null,{"children":"packages/plugin--ui/src/configs.js"}],", we have a configuration section. The example below places new plugin at the main navigation menu."]}],"\n",["$","$Lf",null,{"language":"tsx","code":"menus: [\n {\n text: 'New plugin',\n url: '/new_plugins',\n icon: 'icon-star',\n location: 'mainNavigation',\n }\n]\n","children":["$","$Ld",null,{"className":"language-tsx","children":"$27"}]}],"\n",["$","p",null,{"children":"If you want to place it only inside settings, example is illustrated below."}],"\n",["$","$Lf",null,{"language":"tsx","code":"menus: [\n {\n text: 'New plugin',\n to: '/new_plugins',\n image: '/images/icons/erxes-18.svg',\n location: 'settings',\n scope: 'new_plugin'\n }\n]\n","children":["$","$Ld",null,{"className":"language-tsx","children":"$28"}]}],"\n",["$","h1",null,{"children":"Enabling plugins"}],"\n",["$","p",null,{"children":["\"plugins\" section inside ",["$","$Ld",null,{"children":"cli/configs.json"}]," contains plugin names that run when erxes starts. Please note to configure this section if you decide to enable other plugins, remove or recreate plugins."]}],"\n",["$","$Lf",null,{"language":"tsx","code":"{\n \"jwt_token_secret\": \"token\",\n \"dashboard\": {},\n \"client_portal_domains\": \"\",\n \"elasticsearch\": {},\n \"redis\": {\n \"password\": \"\"\n },\n \"mongo\": {\n \"username\": \"\",\n \"password\": \"\"\n },\n \"rabbitmq\": {\n \"cookie\": \"\",\n \"user\": \"\",\n \"pass\": \"\",\n \"vhost\": \"\"\n },\n \"plugins\": [\n {\n \"name\": \"logs\"\n },\n {\n \"name\": \"new_plugin\",\n \"ui\": \"local\"\n }\n ]\n}\n","children":["$","$Ld",null,{"className":"language-tsx","children":"$29"}]}],"\n",["$","h1",null,{"children":"Running erxes"}],"\n",["$","p",null,{"children":["Please note that ",["$","$Ld",null,{"children":"create-plugin"}]," command automatically adds a new line inside ",["$","$Ld",null,{"children":"cli/configs.json"}],", as well as installs the dependencies necessary."]}],"\n",["$","$Lf",null,{"language":"tsx","code":"{\n \"jwt_token_secret\": \"token\",\n \"client_portal_domains\": \"\",\n \"elasticsearch\": {},\n \"redis\": {\n \"password\": \"pass\"\n },\n \"mongo\": {\n \"username\": \"\",\n \"password\": \"\"\n },\n \"rabbitmq\": {\n \"cookie\": \"\",\n \"user\": \"\",\n \"pass\": \"\",\n \"vhost\": \"\"\n },\n \"plugins\": [\n {\n \"name\": \"new_plugin\",\n \"ui\": \"local\"\n }\n ]\n}\n","children":["$","$Ld",null,{"className":"language-tsx","children":"$2a"}]}],"\n",["$","ol",null,{"start":"2","children":["\n",["$","li",null,{"children":"Run the following command"}],"\n"]}],"\n",["$","$Lf",null,{"language":"tsx","code":"cd erxes/cli\nyarn install\n","children":["$","$Ld",null,{"className":"language-tsx","children":"cd erxes/cli\nyarn install\n"}]}],"\n",["$","ol",null,{"start":"3","children":["\n",["$","li",null,{"children":"Then run the following command to start erxes with your newly installed plugin"}],"\n"]}],"\n",["$","$Lf",null,{"language":"tsx","code":"./bin/erxes.js dev\n","children":["$","$Ld",null,{"className":"language-tsx","children":"./bin/erxes.js dev\n"}]}],"\n",["$","$Lc",null,{"level":2,"id":"create-integration-plugin","children":"Create Integration plugin"}],"\n",["$","p",null,{"children":"Integration is the extent of the Inbox plugin, which allows third party softwares to be integrated to your shared Inbox."}],"\n",["$","div",null,{"className":"text-base my-3 flex flex-col border-l-4 border-amber-500 bg-amber-100 p-4 leading-6 text-amber-950 dark:border-amber-500 dark:bg-amber-200 dark:text-amber-950 dark:[--tw-prose-links-hover:theme(colors.amber.300)] dark:[--tw-prose-links:theme(colors.amber)]","children":[["$","p",null,{"className":"text-lg p-0 mt-0 mb-2 font-bold text-amber-500 dark:text-amber-500","children":"βœ‹ Caution"}],["$","div",null,{"className":"[&>:first-child]:mt-0 [&>:last-child]:mb-0","children":["$","p",null,{"children":["Before you're moving forward, please have a read the ",["$","$L9",null,{"href":"/sdks/creategen","children":"guideline"}]," how you create your own plugin and check out one of our existing integrations available at the ",["$","$L9",null,{"href":"https://erxes.io/marketplace","children":"marketplace"}],["$","script",null,{"src":"https://cdn.jsdelivr.net/gh/eddymens/markdown-external-link-script@v2.0.0/main.min.js"}]," called IMAP which can be found ",["$","$L9",null,{"href":"https://github.com/erxes/erxes-community/tree/dev/packages","children":"here"}],["$","script",null,{"src":"https://cdn.jsdelivr.net/gh/eddymens/markdown-external-link-script@v2.0.0/main.min.js"}]," as we're going to use IMAP integration as an example."]}]}]]}],"\n",["$","p",null,{"children":"So let's assume, you've already created your plugin by using the above guideline and the name of your plugin is IMAP."}],"\n",["$","p",null,{"children":"Add the following Inbox-related plugins to configs.json and start the services."}],"\n",["$","$Lf",null,{"language":"tsx","code":" \"plugins\": [\n {\n \"name\": \"forms\",\n \"ui\": \"remote\"\n },\n {\n \"name\": \"contacts\",\n \"ui\": \"local\"\n },\n {\n \"name\": \"inbox\",\n \"ui\": \"local\"\n },\n {\n \"name\": \"imap\",\n \"ui\": \"local\"\n }\n","children":["$","$Ld",null,{"className":"language-tsx","children":"$2b"}]}],"\n",["$","h1",null,{"children":"These are the core concepts of the inbox integration"}],"\n",["$","ol",null,{"children":["\n",["$","li",null,{"children":[["$","strong",null,{"children":"Brand"}]," - Biggest level of data seperation. Let's assume your company is a group company that consists of 3 child companies. In that case each brand will represent each child companies."]}],"\n",["$","li",null,{"children":[["$","strong",null,{"children":"Channel"}]," - Group of integrations and team members, which represents who is responsible for which integrations."]}],"\n",["$","li",null,{"children":[["$","strong",null,{"children":"Integration"}]," - In IMAP's case, a set of configs that includes email address, password, smtp host etc."]}],"\n",["$","li",null,{"children":[["$","strong",null,{"children":"Customer"}]," - In IMAP's case, the person to sent the email."]}],"\n",["$","li",null,{"children":[["$","strong",null,{"children":"Conversation"}]," - In IMAP's case, whole email thread."]}],"\n",["$","li",null,{"children":[["$","strong",null,{"children":"Conversation Messages"}]," - In IMAP's case, each email entry in single email thread."]}],"\n"]}],"\n",["$","h1",null,{"children":"Lifecycle of integration"}],"\n",["$","ol",null,{"children":["\n",["$","li",null,{"children":"Create an integration instance with corresponing configs, which will be store in inbox's database and later you will use these to work with the apis you want to connect."}],"\n",["$","li",null,{"children":"Receive data from your desired apis using integration configs in plugin-\"integration-name\"-api."}],"\n",["$","li",null,{"children":"Store the data as conversations, conversation messages, and customers. You have to store conversations in inbox's and customers in contacts's database and you have to store conversation messages in your plugin's database."}],"\n",["$","li",null,{"children":"Once you stored the conversations and customers. It will show up in inbox's sidebar. But you will be responsive for the conversation detail in inbox's UI."}],"\n",["$","li",null,{"children":"Since you can show anything in conversation detail will also be responsible for further actions like sending response to customer."}],"\n"]}],"\n",["$","h1",null,{"children":"Let's demonstrate above steps using IMAP as an example"}],"\n",["$","h1",null,{"children":"Create an integration"}],"\n",["$","p",null,{"children":"Let's look at configs.js in plugin-imap-ui"}],"\n",["$","$Lf",null,{"language":"tsx","code":" inboxIntegration: {\n name: 'IMAP',\n description:\n 'Connect a company email address such as sales@mycompany.com or info@mycompany.com',\n isAvailable: true,\n kind: 'imap',\n logo: '/images/integrations/email.png',\n createModal: 'imap',\n createUrl: '/settings/integrations/imap',\n category:\n 'All integrations, For support teams, Marketing automation, Email marketing'\n }\n","children":["$","$Ld",null,{"className":"language-tsx","children":"$2c"}]}],"\n",["$","p",null,{"children":"It will create following in block in /settings/integrations location"}],"\n",["$","p",null,{"children":["$","img",null,{"src":"https://imagedelivery.net/5m26Aj-CutMXPPNacMs_yQ/5e5d8967-e17a-45ac-5bac-f25fe6351300/public","alt":""}]}],"\n",["$","$Lf",null,{"language":"tsx","code":"\"./inboxIntegrationForm\": \"./src/components/IntegrationForm.tsx\",\n","children":["$","$Ld",null,{"className":"language-tsx","children":""./inboxIntegrationForm": "./src/components/IntegrationForm.tsx",\n"}]}],"\n",["$","p",null,{"children":"and"}],"\n",["$","$Lf",null,{"language":"tsx","code":"inboxIntegrationForm: './inboxIntegrationForm',\n","children":["$","$Ld",null,{"className":"language-tsx","children":"inboxIntegrationForm: './inboxIntegrationForm',\n"}]}],"\n",["$","p",null,{"children":["these lines will show ",["$","$Ld",null,{"children":"./src/components/IntegrationForm.tsx"}]," component when you click on the add link in the above picture"]}],"\n",["$","p",null,{"children":["$","img",null,{"src":"https://imagedelivery.net/5m26Aj-CutMXPPNacMs_yQ/e4ed1a7b-874c-4b1a-201b-ce0c7a14e300/public","alt":""}]}],"\n",["$","p",null,{"children":["When you click on the \"Save\" button, it will send the message to ",["$","$Ld",null,{"children":"plugin-imap-api."}]," So you have to write a consumer like the following"]}],"\n",["$","$Lf",null,{"language":"tsx","code":"consumeRPCQueue(\n 'imap:createIntegration',\n async ({ subdomain, data: { doc, integrationId } }) => {\n const models = await generateModels(subdomain);\n\n const integration = await models.Integrations.create({\n inboxId: integrationId,\n ...doc\n });\n\n await listenIntegration(subdomain, integration);\n\n await models.Logs.createLog({\n type: 'info',\n message: `Started syncing ${integration.user}`\n });\n\n return {\n status: 'success'\n };\n }\n);\n","children":["$","$Ld",null,{"className":"language-tsx","children":"$2d"}]}],"\n",["$","p",null,{"children":[["$","$L9",null,{"href":"https://github.com/erxes/erxes-community/blob/dev/packages/plugin-imap-api/src/messageBroker.ts","children":"here is the example"}],["$","script",null,{"src":"https://cdn.jsdelivr.net/gh/eddymens/markdown-external-link-script@v2.0.0/main.min.js"}]]}],"\n",["$","h1",null,{"children":"Receive data from your desired APIs"}],"\n",["$","p",null,{"children":[["$","$L9",null,{"href":"https://github.com/erxes/erxes-community/blob/dev/packages/plugin-imap-api/src/utils.ts","children":"here is the code example"}],["$","script",null,{"src":"https://cdn.jsdelivr.net/gh/eddymens/markdown-external-link-script@v2.0.0/main.min.js"}]]}],"\n",["$","h1",null,{"children":"Store the data"}],"\n",["$","ol",null,{"children":["\n",["$","li",null,{}],"\n"]}],"\n",["$","$Lf",null,{"language":"tsx","code":"const apiCustomerResponse = await sendContactsMessage({\n subdomain,\n action: 'customers.createCustomer',\n data: {\n integrationId: integration.inboxId,\n primaryEmail: from\n },\n isRPC: true\n});\n","children":["$","$Ld",null,{"className":"language-tsx","children":"$2e"}]}],"\n",["$","p",null,{"children":"it will send a createCustomer message to contacts plugin and contact plugin will store it in it's database."}],"\n",["$","ol",null,{"start":"2","children":["\n",["$","li",null,{}],"\n"]}],"\n",["$","$Lf",null,{"language":"tsx","code":"const { _id } = await sendInboxMessage({\n subdomain,\n action: 'integrations.receive',\n data: {\n action: 'create-or-update-conversation',\n payload: JSON.stringify({\n integrationId: integration.inboxId,\n customerId,\n createdAt: msg.date,\n content: msg.subject\n })\n },\n isRPC: true\n});\n","children":["$","$Ld",null,{"className":"language-tsx","children":"$2f"}]}],"\n",["$","p",null,{"children":"it will send a create or update conversation message to inbox plugin and inbox plugin will store it in it's database."}],"\n",["$","h1",null,{"children":"Conversation detail"}],"\n",["$","p",null,{"children":"in configs.js of plugin-imap-ui"}],"\n",["$","$Lf",null,{"language":"tsx","code":"\"./inboxConversationDetail\": \"./src/components/ConversationDetail.tsx\",\n","children":["$","$Ld",null,{"className":"language-tsx","children":""./inboxConversationDetail": "./src/components/ConversationDetail.tsx",\n"}]}],"\n",["$","p",null,{"children":"and"}],"\n",["$","$Lf",null,{"language":"tsx","code":"inboxConversationDetail: './inboxConversationDetail',\n","children":["$","$Ld",null,{"className":"language-tsx","children":"inboxConversationDetail: './inboxConversationDetail',\n"}]}],"\n",["$","p",null,{"children":["will render ",["$","$Ld",null,{"children":"./src/components/ConversationDetail.tsx"}]," component in conversation detail section of inbox ui\n",["$","img",null,{"src":"https://imagedelivery.net/5m26Aj-CutMXPPNacMs_yQ/3cd9edd4-bf11-412a-d4c4-c89127a33000/public","alt":""}]]}],"\n",["$","h1",null,{"children":"Creating new integration plugin"}],"\n",["$","p",null,{"children":["Each plugin is composed of two parts, ",["$","$Ld",null,{"children":"API"}]," and ",["$","$Ld",null,{"children":"UI"}]]}],"\n",["$","ol",null,{"children":["\n",["$","li",null,{"children":"Create new folders for both using the following command."}],"\n"]}],"\n",["$","$Lf",null,{"language":"tsx","code":"cd erxes\nyarn create-plugin\n","children":["$","$Ld",null,{"className":"language-tsx","children":"cd erxes\nyarn create-plugin\n"}]}],"\n",["$","p",null,{"children":["The command above starts CLI, prompting for few questions to create a new integration plugin as shown below. In this example we create twitter integration.\n",["$","img",null,{"src":"https://imagedelivery.net/5m26Aj-CutMXPPNacMs_yQ/95a9c88c-1c84-4f08-ce28-75170897fc00/public","alt":""}]]}],"\n",["$","p",null,{"children":["The example below is a new integration plugin, created from an example template, placed at the settings integration.\n",["$","img",null,{"src":"https://imagedelivery.net/5m26Aj-CutMXPPNacMs_yQ/add9ac3a-0d02-44e6-a9c6-5a1a0ae3fb00/public","alt":""}]]}],"\n",["$","p",null,{"children":["The example below is a new integration plugins configuration, created from an example template, placed at the settings integrations config.\n",["$","img",null,{"src":"https://imagedelivery.net/5m26Aj-CutMXPPNacMs_yQ/08761770-ac2e-4ce2-cddf-dfdfef8daa00/public","alt":""}]]}],"\n",["$","p",null,{"children":["The example below is a creating new example integration using the form.\n",["$","img",null,{"src":"https://imagedelivery.net/5m26Aj-CutMXPPNacMs_yQ/23337484-540c-4cdc-cbb5-ce25ce22f300/public","alt":""}]]}],"\n",["$","p",null,{"children":["The example below is a creating new integration with detail page. If you choose ",["$","strong",null,{"children":"with detail"}]," choice in integration plugin UI template. You will link to detail page when clicking add button.\n",["$","img",null,{"src":"https://imagedelivery.net/5m26Aj-CutMXPPNacMs_yQ/9c4e7e6a-cac5-49c4-d2c8-4c0aa1925500/public","alt":""}]]}],"\n",["$","h1",null,{"children":"API file structure"}],"\n",["$","p",null,{"children":"After creating an integration plugin, the following files are generated automatically in your integration plugin API."}],"\n",["$","$Lf",null,{"language":"tsx","code":"πŸ“¦plugin-twitter-api\n ┣ πŸ“‚src\n ┃ ┣ πŸ“‚graphql\n ┃ ┃ ┣ πŸ“‚resolvers\n ┃ ┃ ┃ ┣ index.ts\n ┃ ┃ ┃ ┣ mutations.ts\n ┃ ┃ ┃ β”— queries.ts\n ┃ ┃ ┣ index.ts\n ┃ ┃ β”— typeDefs.ts\n ┃ ┣ configs.ts\n ┃ ┣ controller.ts\n ┃ ┣ messageBroker.ts\n ┃ β”— models.ts\n ┣ .env.sample\n ┣ package.json\n β”— tsconfig.json\n","children":["$","$Ld",null,{"className":"language-tsx","children":"$30"}]}],"\n",["$","h1",null,{"children":"Main files"}],"\n",["$","p",null,{"children":"Following files are generated automatically in plugin-[pluginName]-api/src."}],"\n",["$","h1",null,{"children":"configs.ts"}],"\n",["$","p",null,{"children":"This file contains main configuration of an integration plugin."}],"\n",["$","$Lf",null,{"language":"bash","code":"// path: ./packages/plugin-[pluginName]-api/src/configs.ts \n import typeDefs from './graphql/typeDefs';\n import resolvers from './graphql/resolvers';\n import { initBroker } from './messageBroker';\n import init from './controller';\n export let mainDb;\n export let graphqlPubsub;\n export let serviceDiscovery;\n export let debug;\n export default {\n name: 'twitter',\n graphql: sd => {\n serviceDiscovery = sd;\n return {\n typeDefs,\n resolvers\n };\n },\n meta: {\n // this code will show the integration in UI settings -> integrations\n inboxIntegration: {\n kind: 'twitter',\n label: 'Twitter'\n }\n },\n apolloServerContext: async (context) => {\n return context;\n },\n onServerInit: async options => {\n const app = options.app;\n mainDb = options.db;\n debug = options.debug;\n graphqlPubsub = options.pubsubClient;\n initBroker(options.messageBrokerClient);\n // integration controller\n init(app);\n }\n };\n","children":["$","$Ld",null,{"className":"language-bash","children":"$31"}],"title":"configs.ts file:"}],"\n",["$","h1",null,{"children":"controller.ts"}],"\n",["$","p",null,{"children":"This file contains integration controllers such as listening integration, connecting integration with erxes, and create conversation and customer. In this example shows message savings."}],"\n",["$","$Lf",null,{"language":"bash","code":"$32","children":["$","$Ld",null,{"className":"language-bash","children":"$33"}],"title":"controller.ts file:"}],"\n",["$","h1",null,{"children":"messageBroker.ts"}],"\n",["$","p",null,{"children":["This file uses for connect with other plugins. You can see message broker functions from ",["$","$L9",null,{"href":"#","children":"Common functions."}]]}],"\n",["$","$Lf",null,{"language":"bash","code":"$34","children":["$","$Ld",null,{"className":"language-bash","children":"$35"}],"title":"messageBroker.ts file:"}],"\n",["$","h1",null,{"children":"GraphQL development"}],"\n",["$","p",null,{"children":["Inside ",["$","$Ld",null,{"children":"packages/plugin--api/src"}],", we have a ",["$","$Ld",null,{"children":"graphql"}]," folder. The folder contains code related to GraphQL."]}],"\n",["$","$Lf",null,{"language":"tsx","code":" πŸ“‚src\n ┣ πŸ“‚graphql\n ┃ ┣ πŸ“‚resolvers \n ┃ ┃ ┣ index.ts\n ┃ ┃ ┣ mutations.ts\n ┃ ┃ β”— queries.ts\n ┃ ┣ index.ts\n ┃ β”— typeDefs.ts\n","children":["$","$Ld",null,{"className":"language-tsx","children":"$36"}]}],"\n",["$","h1",null,{"children":"GraphQL resolvers"}],"\n",["$","p",null,{"children":["Inside ",["$","$Ld",null,{"children":"/graphql/resolvers/mutations"}]," GraphQL mutation codes."]}],"\n",["$","$Lf",null,{"language":"bash","code":" import { Accounts } from '../../models';\n import { IContext } from \"@erxes/api-utils/src/types\"\n const twitterMutations = {\n async twitterAccountRemove(_root, {_id}: {_id: string}, _context: IContext) {\n await Accounts.removeAccount(_id);\n return 'deleted';\n }\n };\n export default twitterMutations;\n","children":["$","$Ld",null,{"className":"language-bash","children":"$37"}],"title":"mutation examples:"}],"\n",["$","p",null,{"children":["Inside ",["$","$Ld",null,{"children":"/graphql/resolvers/queries"}]," folder contains GraphQL query codes."]}],"\n",["$","$Lf",null,{"language":"bash","code":"$38","children":["$","$Ld",null,{"className":"language-bash","children":"$39"}],"title":"query examples:"}],"\n",["$","h1",null,{"children":"GraphQL typeDefs"}],"\n",["$","p",null,{"children":["Inside ",["$","$Ld",null,{"children":"/graphql/typeDefs.ts"}]," file contains GraphQL typeDefs."]}],"\n",["$","$Lf",null,{"language":"bash","code":"import { gql } from 'apollo-server-express';\n const types = `\n type Twitter {\n _id: String!\n title: String\n mailData: JSON\n }\n `;\n const queries = `\n twitterConversationDetail(conversationId: String!): [Twitter]\n twitterAccounts: JSON\n `;\n const mutations = `\n twitterAccountRemove(_id: String!): String\n `;\n const typeDefs = gql`\n scalar JSON\n scalar Date\n ${types}\n extend type Query {\n ${queries}\n }\n extend type Mutation {\n ${mutations}\n }\n `;\n export default typeDefs;\n","children":["$","$Ld",null,{"className":"language-bash","children":"$3a"}],"title":"typeDefs:"}],"\n",["$","h1",null,{"children":"Database development"}],"\n",["$","p",null,{"children":["Inside ",["$","$Ld",null,{"children":"packages/plugin--api/src"}],", we have a ",["$","$Ld",null,{"children":"models"}]," file. The file contains code related to MongoDB and mongoose."]}],"\n",["$","$Lf",null,{"language":"tsx","code":"πŸ“‚src\nβ”— models.ts\n","children":["$","$Ld",null,{"className":"language-tsx","children":"πŸ“‚src\nβ”— models.ts\n"}]}],"\n",["$","h1",null,{"children":"Mongoose schema and model"}],"\n",["$","p",null,{"children":["Inside ",["$","$Ld",null,{"children":"src/models.ts"}],", file contains Mongoose schema and models."]}],"\n",["$","$Lf",null,{"language":"bash","code":"1\n","children":["$","$Ld",null,{"className":"language-bash","children":"1\n"}],"title":"Mongoose schema and model example:"}],"\n",["$","h1",null,{"children":"UI file structure"}],"\n",["$","p",null,{"children":["Automatically generated integration plugin UI's file structure same as general plugin. Only difference is configuring UI. If you want to see general plugin UI file structure ",["$","$L9",null,{"href":"#","children":"click here."}]]}],"\n",["$","h1",null,{"children":"Configuring UI"}],"\n",["$","h1",null,{"children":"Running port for plugin"}],"\n",["$","p",null,{"children":["Inside ",["$","$Ld",null,{"children":"packages/plugin--ui/src/configs.js"}],", running port for plugin UI is set as shown below. Default value is 3024. Please note that each plugin has to have its UI running on an unique port. You may need to change the port manually (inside ",["$","$Ld",null,{"children":"configs.js"}],") if developing multiple plugins."]}],"\n",["$","$Lf",null,{"language":"tsx","code":"$3b","children":["$","$Ld",null,{"className":"language-tsx","children":"$3c"}]}],"\n",["$","h1",null,{"children":"Plugins dynamic components"}],"\n",["$","p",null,{"children":"Files where exposed components are called dynamically in plugin inbox UI."}],"\n",["$","p",null,{"children":[["$","strong",null,{"children":"inboxIntegrationSettings.tsx component"}],"\nInside ",["$","$Ld",null,{"children":"packages/plugin-inbox-ui/src/settings/integrationsConfig/components/IntegrationConfigs.tsx"}],", we have a ",["$","$Ld",null,{"children":"loadDynamicComponent"}]," function. The code below shows the integrationConfigs component being called dynamically in the plugin inbox UI."]}],"\n",["$","$Lf",null,{"language":"tsx","code":"{loadDynamicComponent(\n 'inboxIntegrationSettings',\n {\n renderItem: this.renderItem\n },\n true\n )}\n","children":["$","$Ld",null,{"className":"language-tsx","children":"$3d"}]}],"\n",["$","p",null,{"children":[["$","strong",null,{"children":"inboxIntegrationForm.tsx component"}],"\nInside ",["$","$Ld",null,{"children":"packages/plugin-inbox-ui/src/settings/integrations/containers/common/IntegrationForm.tsx"}],", we have a ",["$","$Ld",null,{"children":"loadDynamicComponent"}]," function. The code below shows the integrationForm component being called dynamically in the plugin inbox UI."]}],"\n",["$","$Lf",null,{"language":"tsx","code":"return loadDynamicComponent(\n 'inboxIntegrationForm',\n updatedProps,\n false,\n type\n);\n","children":["$","$Ld",null,{"className":"language-tsx","children":"return loadDynamicComponent(\n 'inboxIntegrationForm',\n updatedProps,\n false,\n type\n);\n"}]}],"\n",["$","p",null,{"children":[["$","strong",null,{"children":"inboxConversationDetail.tsx component"}],"\nInside ",["$","$Ld",null,{"children":"packages/plugin-inbox-ui/src/inbox/components/conversationDetail/workarea/WorkArea.tsx"}],", we have a ",["$","$Ld",null,{"children":"loadDynamicComponent"}]," function. The code below shows the inboxConversationDetail component being called dynamically in the plugin inbox UI."]}],"\n",["$","$Lf",null,{"language":"tsx","code":"content = loadDynamicComponent('inboxConversationDetail', {\n ...this.props\n});\n","children":["$","$Ld",null,{"className":"language-tsx","children":"content = loadDynamicComponent('inboxConversationDetail', {\n ...this.props\n});\n"}]}],"\n",["$","h1",null,{"children":"Enabling plugins"}],"\n",["$","p",null,{"children":["\"plugins\" section inside ",["$","$Ld",null,{"children":"cli/configs.json"}]," contains plugin names that run when erxes starts. Please note to configure this section if you decide to enable other plugins, remove or recreate plugins."]}],"\n",["$","$Lf",null,{"language":"tsx","code":"{\n \"jwt_token_secret\": \"token\",\n \"dashboard\": {},\n \"client_portal_domains\": \"\",\n \"elasticsearch\": {},\n \"redis\": {\n \"password\": \"\"\n },\n \"mongo\": {\n \"username\": \"\",\n \"password\": \"\"\n },\n \"rabbitmq\": {\n \"cookie\": \"\",\n \"user\": \"\",\n \"pass\": \"\",\n \"vhost\": \"\"\n },\n \"plugins\": [\n {\n \"name\": \"logs\"\n },\n {\n \"name\": \"new_plugin\",\n \"ui\": \"local\"\n }\n ]\n}\n","children":["$","$Ld",null,{"className":"language-tsx","children":"$3e"}]}],"\n",["$","h1",null,{"children":"Running erxes"}],"\n",["$","p",null,{"children":["Please note that ",["$","$Ld",null,{"children":"create-plugin"}]," command automatically adds a new line inside ",["$","$Ld",null,{"children":"cli/configs.json"}],", as well as installs the dependencies necessary."]}],"\n",["$","$Lf",null,{"language":"tsx","code":"{\n \"jwt_token_secret\": \"token\",\n \"client_portal_domains\": \"\",\n \"elasticsearch\": {},\n \"redis\": {\n \"password\": \"pass\"\n },\n \"mongo\": {\n \"username\": \"\",\n \"password\": \"\"\n },\n \"rabbitmq\": {\n \"cookie\": \"\",\n \"user\": \"\",\n \"pass\": \"\",\n \"vhost\": \"\"\n },\n \"plugins\": [\n {\n \"name\": \"forms\",\n \"ui\": \"remote\"\n },\n {\n \"name\": \"contacts\",\n \"ui\": \"local\"\n },\n {\n \"name\": \"inbox\",\n \"ui\": \"local\"\n },\n {\n \"name\": \"twitter\",\n \"ui\": \"local\"\n },\n ]\n}\n","children":["$","$Ld",null,{"className":"language-tsx","children":"$3f"}]}],"\n",["$","ol",null,{"start":"2","children":["\n",["$","li",null,{"children":"Run the following command"}],"\n"]}],"\n",["$","$Lf",null,{"language":"tsx","code":"cd erxes/cli\nyarn install\n","children":["$","$Ld",null,{"className":"language-tsx","children":"cd erxes/cli\nyarn install\n"}]}],"\n",["$","ol",null,{"start":"3","children":["\n",["$","li",null,{"children":"Then run the following command to start erxes with your newly installed plugin"}],"\n"]}],"\n",["$","$Lf",null,{"language":"tsx","code":"./bin/erxes.js dev\n","children":["$","$Ld",null,{"className":"language-tsx","children":"./bin/erxes.js dev\n"}]}]]}],["$","footer",null,{"className":"mx-auto mt-16 w-full max-w-2xl lg:max-w-5xl","children":["$","$L40",null,{}]}]]}],null],"segment":"__PAGE__"},"styles":[]}],"segment":"sdks"},"styles":[]}]}]}]}]}]}] a:null