SmartHub: Editor SmartHub y Librería de Códigos

Bienvenido a  SmartHub en Outbound by Enreach.

Personalice la apariencia de Outbound by Enreach con su propio código y consulte nuestra biblioteca de fragmentos de código.

Lea el artículo o haga clic en un enlace de los siguientes para comenzar.

En este articulo: 



Qué es SmartHub?

SmartHub le permite aplicar su propia programación personalizada dentro de Outbound by Enreach, proporcionando los medios para ampliar, automatizar e integrar con el flujo de la  página de Contacto. Le brinda una biblioteca completa para escribir código que interactúa con la Página de Contacto, el Lead en ella y los datos fuera de Outbound by Enreach.
Para habilitar SmartHub, necesita acceso al módulo adicional SmartHub. Si no tiene acceso a SmartHub, contacte con  Soporte o su CX Manager.

## Ejemplos de cosas posibles con SmartHub
  • Validación personalizada de campos.
  • Asegurar de que los campos individuales y las ofertas registradas se alineen completamente con los formatos y las reglas requeridas por sus sistemas backend.
  • Buscar Leads en un sistema externo antes de la marcacion.
  • Omitir un Lead si está en la lista negra en sistemas externos.
  • Obtener los datos más recientes sobre el Lead antes de la marcación.
  • Filtrar el contenido de los campos desplegables.
  • Mostrar sólo los días de entrega que coincidan con el producto ofrecido y el código postal.
  • Buscar dinámicamente sugerencias de productos y precios en sistemas externos.
  • Búsqueda y validación de direcciones.
  • Usar los servicios de dirección estándar.
  • Utilizar su propio sistema.
  • Registrar las ventas directamente en un sistema externo antes de cerrar el Lead.
  • Integrar directamente su sistema CRM desde la Página de Contacto.
  • Proporcionar o filtrar datos para la búsqueda de grupos de entrada.

Empezando con SmartHub

Para comenzar a crear sus extensiones, expanda el menú desplegable Administración y seleccione el Editor SmartHub.
Cuando esté en el editor, estará listo para comenzar a codificar.
Las extensiones de SmartHub están escritas en [ TypeScript], que es un superconjunto de JavaScript fuertemente tipado.
El editor de código proporciona el resaltado de sintaxis, sugerencias automáticas y documentación al pasar el cursor por encima.
Cualquier script SmartHub consiste en el código que manipula tres o cuatro (las sugerencias son opcionales) de los siguientes elementos:
  • Config object
  • Event handlers
  • Commands
  • Sugerencias
El propósito de estos elementos se discutirá en las siguientes secciones.

Config Objects

Config Objets es el contenedor de sus extensiones. Proporciona un filtrado de alto nivel de cuándo la extensión está activa.
Puede agregar tantos Config Objets como necesite.
Config Objets expone una [ Fluent api], que da acceso a todos los puntos de extensión posibles, como los event handlers.

Event Handlers

Los event handlers representan las cosas a las que puede reaccionar su extensión. Por ejemplo:
  • onEntryChange: el valor/validez/visibilidad/etc. de que un campo ha cambiado.
  • onLeadReady: el Lead se carga e inicializa.
  • onPreLeadSave: el Lead está a punto de guardarse, realice una validación de último momento.

Commands

Commands son solicitudes para que la página de Contacto realice algunas acciones. Por ejemplo:
  • SaveLead: inicia una acción de guardado.
  • AbortSave: cancela una acción de guardado pendiente.
  • ChangeEntry: cambie el valor de un campo, configúrelo como no válido u oculte el campo.
  • Message: Informa al usuario sobre algo.
  • Nop: No hagas nada.
No todos los Commands están permitidos en un momento determinado. Estas restricciones están codificadas en el sistema de tipos, de modo que para un Event Handler determinado, solo los Commands permitidos sean aceptados como valores de retorno. El editor de código/verificador de tipos mostrará errores, con mensajes de error como `El tipo 'AbortSave' no se puede asignar al tipo 'HookEventHandlerResult <ChangeEntry>' .`, lo que indica que en esa instancia solo se permiten ChangeEntry.

Sugerencias

A menudo, es útil mostrar una lista de sugerencias debajo del campo que se esté editando, como en la búsqueda de Google. Esta funcionalidad también está presente y es totalmente programable en SmartHub.
Las sugerencias son excelentes para:
  • Búsqueda de direcciones.
  • Búsqueda de producto/precio.
  • Como un control super desplegable mutado.
La configuración de Sugerencias se explica más abajo en este documento en Configuración de Sugerencias.

La Estructura Básica de una Extensión

Esta sección analiza y estudia una pequeña extensión de ejemplo. El ejemplo es una extensión que escribe en mayúsculas el primer nombre del Lead.
new SmartHubConfig(ca => ca.name === 'Test campaign')<br>  .onEntryChange(<br>     e => e.importName === 'firstName' && e.value.length > 0,<br>  e => HookResult.changeEntry({<br>    entryId: e.id,<br>    newValue: e.value.substr(0,1).toUpperCase() + e.value.substr(1)<br>}));
Analicemos este ejemplo:
Primero hay una instanciación de un nuevo config object:
new SmartHubConfig(ca => ca.name === 'Test campaign')
Se debe crear una instancia de un config object con una función de filtro, que se utiliza para determinar para qué Campañas es válida una configuración. Aquí, el filtro se basa en el nombre para facilitar la comprensión, pero normalmente sería mejor usar ID o alguna otra propiedad inmutable.
Si la configuración debe usarse en todos los casos, el filtro podría verse así:
new SmartHubConfig(()) => true)
Lo siguiente es conectarse al event onEntryChange:
.onEntryChange(<br>  e => e.importName === 'firstName' && e.value.length > 0,
  e => HookResult.nop());
Todos los event handlers toman dos parámetros: una función de filtro y una función de controlador. En este caso, la función de filtro se asegura de que el controlador solo se invoque, si el nombre del campo es 'firstName' y hay un valor no vacío.
Un controlador **debe** devolver algún comando, y en este caso devuelve Nop (sin operación) para indicar que no requiere ninguna acción desde la página de Contacto.
Finalmente, mirando el controlador del ejemplo completo:
.onEntryChange( <br>  e => e.importName === 'firstName' && e.value.length > 0,<br>  e => HookResult.changeEntry({<br>  entryId: e.id,
  newValue: e.value.substr(0,1).toUpperCase() + e.value.substr(1)<br>}));
Este controlador devuelve un comando ChangeEntry, pidiendo que la misma entrada que invocó al controlador cambie su valor para comenzar con una letra mayúscula.

Fluent api

Fluent api hace posible encadenar configuraciones de event handler
function CapitalizeFirstLetter (e: Contracts.LeadEntry) {return HookResult.changeEntry({<br>      entryId: e.id,<br>      newValue: e.value.substr(0,1).toUpperCase() + e.value.substr(1)<br>});<br>}<br><br>new SmartHubConfig(ca => ca.name === 'Test campaign')<br>.onEntryChange(<br>e => e.importName === 'firstName' && e.value.length > 0,<br>e => CapitalizeFirstLetter(e))<br><br>// onEntryChange returs a reference to the SmartHubConfig object,<br>// making it possible to chain configurations.<br><br>.onEntryChange(<br>e => e.importName === 'lastName' && e.value.length > 0,<br>e => CapitalizeFirstLetter(e));

Event Handlers

Event handlers son los puntos de extensión que permiten la integración con la página de Contacto. Son invocados por la lógica interna cuando ocurren varias cosas y, a su vez, pueden afectar la forma en que la página de Contacto debería reaccionar ante esas cosas.
Cada controlador viene con un filtro que determina si el controlador debe ejecutarse para el evento actual.
Un controlador debe devolver uno o más comandos, un solo objeto o una matriz de resultados. Ciertos eventos también permiten devolver promesas de comandos, o incluso una combinación de promesas y resultados directos.
Actualmente se admiten los siguientes resultados:
Message | ChangeEntry | AbortSave | SaveLead | Error | Nop
Nop means "No OPeration" y debe devolverse cuando el controlador no desea hacer nada.
Los resultados permitidos dependen del tipo de evento.

Entry Events

Entry events se invocan cuando algo cambia en relación con una entrada/campo.
Por ahora solo hay un Entry events único: onEntryChange
onEntryChange se invoca siempre que valor/validez/visibilidad/etc. de un campo ha cambiado, pero también una vez cuando el Lead se carga por primera vez, después de que se haya inicializado la entrada.
onEntryChange se configura mediante el siguiente método:
export type EntryEventResults = Message | ChangeEntry;<br>export type HookEventHandlerResult<T extends HookResult.Executable> = T | HookResult.Nop | HookResult.Error | ReadonlyArray<T | HookResult.Nop | HookResult.Error>;<br>export type EntryEventFilter = (<br>entry: Contracts.LeadEntry,<br>lead: Contracts.Lead<br>  ) => boolean;
  export type EntryEventHandler = (<br>    entry: Contracts.LeadEntry,<br>    lead: Contracts.Lead<br>  ) => HookEventHandlerResult<HookResult.EntryEventResults>;<br><br>onEntryChange(filter: EntryEventFilter, handler: EntryEventHandler): SmartHubConfig;
A continuación, se muestra un ejemplo de un entry handler que solo se invoca en entradas denominadas 'código_producto'.
Si el valor de entrada tiene una longitud de cadena> 4, la entrada se considera válida.
  config.onEntryChange(<br>  e => e.importName == 'product_code',<br>  e => HookResult.changeEntry({<br>        entryId: e.id,<br>        isValid: e.value.length > 4<br>}))
La interfaz de configuración se amplía aún más con utilidades. Una extensión es:
onEntryChangeValidate(<br>filter: (entry: Contracts.LeadEntry, lead: Contracts.Lead) => boolean,<br>  handler: (entry: Contracts.LeadEntry, lead: Contracts.Lead) => (boolean | undefined)<br>): SmartHubConfig;
Con esta extensión, la validación simple anterior se puede configurar como: 
config.onEntryChangeValidate(<br>  e => e.importName == 'product_code',<br>  e => e.value.length > 4<br>)
Entry handlers pueden afectar a otras entradas. En este ejemplo, se muestra una entrada denominada `sub_producto` y se hace necesaria si el valor de la entrada` product_code` termina con "_abstract".
config.onEntryChange(<br>e => e.importName == 'product_code',<br>    (e,l) => {<br>      const hasSubProduct = e.value.endsWith('_abstract');<br>      return HookResult.changeEntry({<br>        entryId: l.entriesByImportName.get('sub_product')!.id,<br>        isRequired: hasSubProduct,<br>        isVisible: hasSubProduct<br>      });<br>})

Lead Events

Entry events se invocan cuando algo cambia en relación con el Lead.
Actualmente hay cuatro eventos:
export type LeadEventFilter = (<br>lead: Contracts.Lead & Contracts.LeadState,<br>  ) => boolean;<br>  onLeadLoad(<br>      filter: LeadEventFilter,<br>      handler: (lead: Contracts.Lead) => AsyncHookEventHandlerResult<HookResult.SaveLead>): SmartHubConfig;<br>  onLeadReady(<br>      filter: LeadEventFilter,<br>      handler: (lead: Contracts.Lead) => HookEventHandlerResult<HookResult.EntryEventResults>): SmartHubConfig;<br>  onPreLeadSave(<br>      filter: LeadEventFilter,<br>      handler: (lead: Contracts.Lead) => AsyncHookEventHandlerResult<HookResult.AbortSave | HookResult.Message>): SmartHubConfig;<br>  onPostLeadSave(<br>      filter: LeadEventFilter,<br>handler: (lead: Contracts.Lead) => HookResult.Nop): SmartHubConfig;
En este ejemplo, un Lead se verifica en un sistema externo inmediatamente después de la carga. La verificación debe realizarse para todos los Leads, por lo tanto, la función de filtro solo devuelve verdadero. Si el Lead está en la lista negra, se guarda con un estado apropiado. Después de ejecutar el comando de guardado, la página de Contacto continuará automáticamente con el siguiente Lead.
Tenga en cuenta el uso de async/ await. Dado que onLeadLoad devuelve AsyncHookEventHandlerResult, es legal devolver una promesa y también es obligatorio cuando las acciones asíncronas como las solicitudes http están involucradas en el procesamiento del evento.
async function isBlackListed(l: Contracts.Lead) {<br> // Do lookup in external system to check for blacklisting<br> // in this example everything is blacklisted<br>  return true;<br>  }<br>  config.onLeadLoad(l => true,<br>      // In this case the result of isBlackListed is required to continue processing, so it must be awaited.<br>      async l => await isBlackListed(l)<br>      ? HookResult.saveLead({status: Contracts.LeadSaveStatus.Removed })<br>: HookResult.nop())
En este ejemplo, el Lead se valida mediante una lógica personalizada, justo antes de guardar. Si no es válido, el guardado se cancela por algún motivo.
async function validateOffer(l: Contracts.Lead) {<br>// Custom validation logic to make sure that all fields are correctly filled<br>// Possibly lookup in external system to make sure that the sold product is actually in stock.<br>// In this example it's always valid<br>    const isValid = true;
    return isValid<br>      ? HookResult.nop()<br>      : HookResult.abortSave({<br>        reason: "Product not in stock"<br>      });<br>  }<br>  config.onPreLeadSave(<br>      l => l.newStatus === Contracts.LeadStatus.UserProcessed<br>        && l.newClosure === Contracts.LeadClosure.Success,<br>      // validateOffer returns a promise of a HookResult, which is a valid return value, so no need to await it here.<br>      l => validateOffer(l)<br>    )

Configuración de Sugerencias

La sugerencia automática se admite de forma genérica y solo asume un campo de entrada y una lista de datos para elegir.
La forma en que se consultan los datos de sugerencia en función de la entrada es configurable, y también lo es la acción a tomar cuando se selecciona una sugerencia.
El campo de entrada puede ser de tres formas:
  • in
  • virtual
  • hover
Para ' in ', la entrada ocurre en el campo de entrada. Esto es adecuado para campos independientes editables, como FullAddress.
Para ' virtual ', la entrada ocurre en un campo virtual encima de un campo de entrada especificado. Esto es adecuado para campos ocultos o múltiples campos en un grupo de entrada, por ejemplo, campos de dirección.
Para ' hover ', la entrada ocurre en un campo virtual que se sitúa sobre un campo de entrada especificado. Esto es adecuado para campos independientes no editables.
Las sugerencias se configuran mediante este método:
export interface IDialerHooksSuggestionsResult {<br>    text: string;<br>    changes: ReadonlyArray<HookResult.ChangeEntry>;<br>  }<br>  export interface IDialerHooksSuggestionsConfig {<br>    leadDefinitionEntryId: Contracts.LeadDefinitionEntryId;<br>    title: string;<br>    placement: "in" | "hover" | "virtual";<br>    initialSearchText: (lead: Contracts.Lead) => string;<br>    search: (search: string) => PromiseLike<ReadonlyArray<IDialerHooksSuggestionsResult>>;<br>  }<br>  export type SuggestionConfig = (lead: Contracts.Lead) => ReadonlyArray<IDialerHooksSuggestionsConfig>;<br>addSuggestions(configure: SuggestionConfig): SmartHubConfig;
Esto puede parecer desalentador, pero descompuesto no está tan mal, y en el uso real es bastante simple. Consulte Sugerencias de precios personalizados para obtener un tutorial sobre cómo se usa.

Búsqueda de Direcciones DAWA

Otro conjunto de extensiones para una fácil configuración de la búsqueda de direcciones  DAWA son:
export interface SmartHubConfig {<br>  addSuggestionForAddressEntryGroups(<br>      configurator: ConfigureAddressLookupForEntries,<br>  filter?: ((groupName: string) => boolean)): SmartHubConfig;<br>    addDAWASuggestionForAddressEntryGroups(<br>      filter?: ((groupName: string) => boolean)): SmartHubConfig;<br>    addDAWASuggestionForEntries(<br>      filter: ((e: Contracts.LeadDefinitionEntry) => boolean) ): SmartHubConfig;<br>    addDAWASuggestionForStandaloneAddressEntries(): SmartHubConfig;<br>    // Calls addDAWASuggestionForAddressEntryGroups and then addDAWASuggestionForStandaloneAddressEntries<br>    addDAWASuggestionForAddressEntries(): SmartHubConfig;<br>}
Estas extensiones asumen que se utilizan los tipos de dirección semántica adecuados para los campos y que los campos están agrupados de forma adecuada en grupos de entrada.
Así es como simplemente se puede configurar la búsqueda de direcciones:
config.addDAWASuggestionForAddressEntries();<br>
Eso es todo. La sugerencia automática aparecerá ahora en todas las entradas de dirección, o para todos los grupos de entrada que representan direcciones, siempre que esas entradas se creen con los tipos correctos en la definición de Lead.
Para agregar solo la búsqueda de direcciones a las entradas en los grupos de entradas donde el nombre comienza con Invoice, introduzca lo siguiente:
config.addDAWASuggestionForAddressEntryGroups( groupName => groupName.startsWith( 'Invoice' ) );<br>

Sugerencias de Precios Personalizados

Este ejemplo demuestra cómo proporcionar datos personalizados de productos y precios en un menú desplegable de sugerencias.
// A custom function that looks up prices based on a search string<br>async function priceLookup(search: string): Promise<{<br>    code: string,<br>    price: string<br>  }[]><br>  {<br>    // In this example the product data is hardcoded.<br>    // In actual use, the price data could be requested from a service live, or once<br>    // on lead load and then just filtered here.<br>    return [<br>      { code: 'Prod A', price: '100' },<br>      { code: 'Prod B', price: '200' }<br>    ];<br>  }<br>  // In this example suggestions are configured for products, each represented by a set of<br>  // entries grouped in an entryGroup.<br>  // This function returns the full configuration for such an entryGroup.<br>  function configurePriceLookupForGroup({ group, entries }: Contracts.LeadDefinitionEntryGroupWithEntries): IDialerHooksSuggestionsConfig {<br>    // Find the id of some entries in the group. These are the entries that should<br>    // be updated when a sugggestion is picked.<br>    const codeId = entries.find(e => e.keyInEntryGroup === 'Code')!.id;<br>    const priceId = entries.find(e => e.keyInEntryGroup === 'Price')!.id;<br>    // The function to execute when the search text in the suggestion box changes<br>    var doPriceLookup = async (search) => {<br>      // Call the search function<br>      const matchingPrices = await priceLookup(search)<br>      // For each price returned<br>      return matchingPrices.map(p => ({<br>        // Some display text for this suggestion entry<br>        text: 'Some text for:' + p.code,<br>        // The change actions to execute when the suggestion is selected<br>        changes: [<br>          HookResult.changeEntry({ entryId: codeId, newValue: p.code }),<br>          HookResult.changeEntry({ entryId: priceId, newValue: p.price })<br>        ]<br>        }));<br>      };<br>    // This is how a single suggestion box is configured<br>    return {<br>      // A description for debug puposes. Will be logged to console when suggestion is configured.<br>      description: 'A description',<br>      // Where to place the suggestion<br>      placement: 'in',<br>      // How to label it if needed<br>      title: 'Product price lookup',<br>      // Which entry the suggestion is attached to<br>      leadDefinitionEntryId: codeId,<br>      // A function specifying the initial search text<br>      initialSearchText: lead => '',<br>      // The function to perform the search whenever the user enters more text.<br>      // The search returns the rows to display.<br>      // Each row is represented by the text to display and he ChangeEntry commands<br>      // to execute when the row is selected.<br>      search: doPriceLookup<br>    };<br>  };<br>  // Adding suggestions for product entry groups<br>  // The function passed in here is run each time a new lead is loaded<br>  config.addSuggestions(l => {<br>    // CommonUtils.getEntryGroupsWithEntries is a helper function that returns entry groups,<br>    // along with the entries it contains that matches the given filter.<br>    // If no entries in a group matches the filter the group is ignored.<br>    // In this case we filter on the group name thus returning all groups named Product*, along<br>    // with the entries in those groups.<br>    var groupsToAddSuggestionsTo = CommonUtils.getEntryGroupsWithEntries(<br>      l.campaign.leadDefinition, _ => _.entryGroup?.name.startsWith('Product') ?? false);<br>    // Run the configuration for each group<br>    return groupsToAddSuggestionsTo.map(configurePriceLookupForGroup);<br>})


Funciones Auxiliares

Asignación de un Lead a una Estructura Personalizada

Cuando se llama a servicios externos, a menudo se requiere transformar (mapear) el Lead de una forma diferente.
Manualmente, esto se puede hacer de la siguiente manera:
      const manuallyMapped = {<br>	leadId: l.id,<br>	  data: {<br>	    a: l.getEntryValueByImportName('x'),<br>	    b: l.getEntryValueByImportName('y'),<br>    // or<br>            c: (l.entriesByImportName.get('z') || { value: undefined }).value,<br>            d: (l.entriesByImportName.get('w') || { value: undefined }).value<br>          }<br>  };
Sin embargo, la función CommonUtils.mapLeadData proporciona una forma más sencilla.
CommonUtils.mapLeadData toma un Lead y un objeto de mapeo y devuelve un objeto que coincide con la estructura de los objetos de mapeo, poblado con datos del Lead.
Usando este ayudante, el ejemplo anterior sería el siguiente:
      const mapLeadDataEx1  = {<br>	leadId: l.id,<br>	  data: CommonUtils.mapLeadData( l, {<br>	    a: 'x',<br>	    b: 'y',<br>	    c: 'z',<br>	    d: 'w'<br>  } )<br>  };
Algunas notas sobre el objeto mapeador:
  • Si el valor de una clave es una cadena, entonces el valor se usa para buscar por importName.
  • Si el valor de una clave no está definido, entonces la clave en sí se usa para buscar por importName.
  • Si el valor de una clave es una función, se asume que toma una ventaja como parámetro y el resultado se asigna a la propiedad de salida.
  • Si el valor de una clave es un objeto, se asignará directamente a la propiedad de salida.
CommonUtils.mapGroupData es otra función auxiliar. Permite un mapeo similar a mapLeadData, pero opera a nivel de grupo de entrada en lugar de a nivel de Lead, y es útil para mapear estructuras planas a estructuras anidadas.
En este ejemplo, se asignan varios productos a una matriz:
       const productMapping = {<br>	code: 'Code',<br>	 unitsSold: '# Units',<br>	  totalPrice: (g) => g['# Units'] * g['Unit price']<br>		}<br>	const productGroups = l.campaign.leadDefinition.entryGroups.filter(g => g.name.startsWith('Product')).map(g => g.name);<br>	const mapLeadDataEx2 = {<br>	  leadId: l.id,<br>	  data: CommonUtils.mapLeadData(l, {<br>	    a: 'x',<br>	    b: 'y',<br>	    products: productGroups.map(groupName => CommonUtils.mapGroupData(l, groupName, productMapping))<br>  })<br> };

Contratos

export enum SemanticEntryType {

  ShortText = 'ShortText',
  LongText = 'LongText',
  Time = 'Time',
  Date = 'Date',
  DateAndTime = 'DateAndTime',
  Integer = 'Integer',
  Decimal = 'Decimal',
  Bool = 'Bool',
  Phone = 'Phone',
  Email = 'Email',
  PickList = 'PickList',
  ZipCode = 'ZipCode',
  Notes = 'Notes',
  SocialSecurityNumber = 'SocialSecurityNumber',
  BankRegistrationNumber = 'BankRegistrationNumber',
  BankAccountNumber = 'BankAccountNumber',
  BankRegistrationAccountNumber = 'BankRegistrationAccountNumber',
  Revenue = 'Revenue',
  Profit = 'Profit',
  Commision = 'Commision',
  Points = 'Points',
  MainClosure = 'MainClosure',
  SubClosure = 'SubClosure',
  URL = 'URL',
  Label = 'Label',
  BookingEntry = 'BookingEntry',
  BookingCalendarCode = 'BookingCalendarCode',
  BookingTime = 'BookingTime',
  UnitPrice = 'UnitPrice',
  NUnitsSold = 'NUnitsSold',
  Birthday = 'Birthday',
  FullName = 'FullName',
  FirstName = 'FirstName',
  LastName = 'LastName',
  Address = 'Address',
  Country = 'Country',
  City = 'City',
  City2 = 'City2',
  Street = 'Street',
  BuildingNumber = 'BuildingNumber',
  ApartmentNumber = 'ApartmentNumber',
  ApartmentDoor = 'ApartmentDoor',
  SensitiveInteger = 'SensitiveInteger',
  SensitiveText = 'SensitiveText',
  SubscriptionStartDate = 'SubscriptionStartDate',
}
export enum EntryCategory {
  MasterData = 'MasterData',
  ResultData = 'ResultData',
}
export enum CampaignType {
  Master = 'Master',
  PowerDialer = 'PowerDialer',
  BasketDialer = 'BasketDialer',
  PredictiveDialer = 'PredictiveDialer',
}
export enum LeadSaveStatus {
  RedialAutomatic = 1,
  RedialManualCommon = 2,
  RedialManualPrivate = 3,
  VIPRedial = 10,
  FollowUpRedial = 20,
  ValidationAwaiting = 105,
  AwaitingExternalEvents = 110,
  OrderConfirmationReadyFor = 115,
  OrderConfirmationAwaiting = 116,
  OrderConfirmationFailed = 117,
  // OrderConfirmationExpired = 118,
  PaymentReadyFor = 120,
  PaymentAwaiting = 121,
  PaymentFailed = 122,
  // PaymentExpired = 123,
  MeetingFeedbackAwaiting = 125,
  UserProcessed = 200,
  Removed = 600,
  // For now we don't support other statuses here, but other that might make sense is:
  // Depleted = 210,
  // Failed = 400,
  // Removed = 600,
  // Anonymized = 610,
  // DoNotCall = 700,
}
export enum LeadStatus {
  New = 0,
  RedialAutomatic = 1,
  RedialManualCommon = 2,
  RedialManualPrivate = 3,
  VIPRedial = 10,
  FollowUpRedial = 20,
  ValidationAwaiting = 105,
  AwaitingExternalEvents = 110,
  OrderConfirmationReadyFor = 115,
  OrderConfirmationAwaiting = 116,
  OrderConfirmationFailed = 117,
  // OrderConfirmationExpired = 118,
  PaymentReadyFor = 120,
  PaymentAwaiting = 121,
  PaymentFailed = 122,
  // PaymentExpired = 123,
  MeetingFeedbackAwaiting = 125,
  Unresolved = 150,
  UserProcessed = 200,
  Depleted = 210,
  Abandoned = 300,
  Failed = 400,
  Expired = 500,
  Removed = 600,
  Anonymized = 610,
  DoNotCall = 700,
  GlobalDoNotCall = 750,
}
export enum LeadClosure {
  NotSet = 0,
  Success = 1,
  NotInterested = 2,
  InvalidLead = 3,
  Unqualified = 4,
}
export enum DataType {
  ShortText = 'varchar',
  LongText = 'text',
  Time = 'time',
  Date = 'date',
  DateTime = 'datetime',
  Integer = 'int',
  Decimal = 'decimal',
  Bool = 'bool',
  Phone = 'phone',
  Email = 'email',
  Set = 'set',
  Url = 'url',
  Label = 'label',
}
export interface LeadDefinitionEntryGroup extends Readonly<{
  id: string;
  name: string;
  style: string;
}> {}
export interface LeadDefinitionEntryOption extends Readonly<{
  name: string;
  value: string;
  closure?: LeadClosure;
  leadClosureCondition?: LeadClosure;
}> { }
export type ImportName = string;
export type LeadId = string;
export type CampaignId = string;
export type LeadDefinitionEntryId = string;
export type LeadDefinitionEntryGroupId = string;
export interface LeadDefinitionEntry extends Readonly<{
  id: LeadDefinitionEntryId;
  displayName: string;
  importName: ImportName;
  entryCategory?: EntryCategory;
  requiredOnClosure: boolean;
  isEditable: boolean;
  isDefaultVisible: boolean;
  isRestricted: boolean;
  valueOptions: ReadonlyArray<LeadDefinitionEntryOption>;
  keyInEntryGroup?: string;
  entryGroup?: LeadDefinitionEntryGroup;
  entryGroupId?: number;
  entryGroupDataLookupType?: string;
  semanticType?: SemanticEntryType;
  dataType: DataType;
  typeFormat: string;
  // LeadDefinitionEntry has no value, LeadEntry holds the value
}> { }
export interface LeadDefinitionEntryGroupWithEntries extends Readonly<{
  group: LeadDefinitionEntryGroup;
  entries: ReadonlyArray<LeadDefinitionEntry>;
}> { }
export interface LeadDefinition extends Readonly<{
  id: string;
  name: string;
  entryGroups: ReadonlyArray<LeadDefinitionEntryGroup>;
  entryDefinitions: ReadonlyArray<LeadDefinitionEntry>;
  entryDefinitionsById: ReadonlyMap<LeadDefinitionEntryId, LeadDefinitionEntry>;
}> { }
export interface Project extends Readonly<{
  id: string;
  name: string;
}> { }
export interface Campaign extends Readonly<{
  id: CampaignId;
  name: string;
  code: string;
  info?: string;
  manuscripts: ReadonlyArray<IContactManuscript>;
  leadDefinition: LeadDefinition;
  project: Project;
}> { }
export interface LeadEntry extends LeadDefinitionEntry {
  readonly value: string;
  readonly isValid: boolean;
  readonly isVisible: boolean;
}
export interface BaseLead extends Readonly<{
  id: string;
  uniqueId: string;
  campaign: Campaign;
}> { }
// Represents the current intermediate state of the lead.
export interface LeadState extends Readonly<{
  oldStatus: LeadStatus;
  newStatus: LeadStatus;
  newClosure: LeadClosure;
  leadReadyState: LeadReadyState;
}> {}
export interface Lead extends BaseLead, Readonly<{
  entriesById: ReadonlyMap<LeadDefinitionEntryId, LeadEntry>;
  entriesByImportName: ReadonlyMap<string, LeadEntry>;
  entries: ReadonlyArray<LeadEntry>;
  getEntryValueById(id: LeadDefinitionEntryId): string | undefined;
  getEntryValueByImportName(importName: string): string | undefined;
}> { }
export interface LeadEntryForSaving extends Readonly<{
  leadDefinitionEntryId: LeadDefinitionEntryId;
  value: string;
}> {}
export interface OrganizationalUnit {
  uniqueId: string;
  orgCode: string;
  name: string;
}
export enum Rights {
  AllowRestrictedEntries = 'AllowRestrictedEntries',
}
export interface CurrentUser extends Readonly<{
  uniqueId: string;
  organization: ReadonlyArray<OrganizationalUnit>;
  orgCode: string;
  username: string;
  name: string;
  rights: {
    [rightName in Rights]: Boolean;
  };
  pageLayout: 'natural' | 'reverse';
  leadEntryLayout: 'normal' | 'compact' | 'loose';
}> {}
export enum LeadReadyState { NoLead, Loaded, Active }
export enum PageMode { Dialer = 0, Edit = 1, New = 2, Preview = 3 }
<br>

Resultados Hook 

export enum ResultAction {
  Message = 'message',
  ChangeEntry = 'changeEntry',
  SaveLead = 'saveLead',
  AbortSave = 'abortSave',
  Nop = 'nop',
  Error = 'error',
}
export interface MessageParams {
  text: string;
  // data?: any;
}
export interface Message extends MessageParams {
  kind: ResultAction.Message;
}
export function message(params: MessageParams): Message {
  return {
    kind: ResultAction.Message,
    ...params,
  };
}
export interface ErrorParams {
  text: string;
  data?: any;
}
export interface Error extends ErrorParams {
  kind: ResultAction.Error;
}
export function error(params: ErrorParams): Error {
  return {
    kind: ResultAction.Error,
    ...params,
  };
}
interface ChangeEntryParams {
  leadId?: Contracts.LeadId;
  entryId: Contracts.LeadDefinitionEntryId;
  newValue?: string;
  isValid?: boolean;
  isVisible?: boolean;
  isRequired?: boolean;
  filterValueOptions?: ReadonlyArray<string> | ((option: string) => boolean);
}
export interface ChangeEntry extends ChangeEntryParams {
  kind: ResultAction.ChangeEntry;
}
export function changeEntry(params: ChangeEntryParams): ChangeEntry {
  return {
    kind: ResultAction.ChangeEntry,
    ...params,
  };
}
export interface AbortSaveParams {
  reason: string;
}
export interface AbortSave extends AbortSaveParams {
  kind: ResultAction.AbortSave;
}
export function abortSave(params: AbortSaveParams): AbortSave {
  return {
    kind: ResultAction.AbortSave,
    ...params,
  };
}
export interface SaveLeadParams {
  status?: Contracts.LeadSaveStatus;
  closure?: Contracts.LeadClosure;
  nextDialTime?: Date;
  changedEntries?: ReadonlyArray<Contracts.LeadEntryForSaving>;
}
export interface SaveLead extends SaveLeadParams {
  kind: ResultAction.SaveLead;
}
export function saveLead(params: SaveLeadParams): SaveLead {
  return {
    kind: ResultAction.SaveLead,
    ...params,
  };
}
export interface Nop {
  kind: ResultAction.Nop;
}
export function nop(): Nop {
  return {
    kind: ResultAction.Nop,
  };
}
// export type Awaitable<T> = PromiseLike<T | Error>;
export type AlwaysAllowedResults = Nop | Message | Error;
export type EntryEventResults = AlwaysAllowedResults | ChangeEntry;
export type Executable =
  EntryEventResults
  | AbortSave
  | SaveLead;
export type HookEventHandlerResult<T extends Executable = Nop> =
  T | AlwaysAllowedResults | ReadonlyArray<T | AlwaysAllowedResults>;
export type AsyncResult<T extends Executable = Nop> =
  PromiseLike<T | AlwaysAllowedResults | ReadonlyArray<T | AlwaysAllowedResults>>;
export type AsyncHookEventHandlerResult<T extends Executable = Nop> =
  T | AlwaysAllowedResults
  | AsyncResult<T>
  | ReadonlyArray<T | AlwaysAllowedResults | AsyncResult<T>>;
```
# Entry group event results
```
export enum ResultAction {
  Nop = 'nop',
  Override = 'override',
  Data = 'data',
}
export type LookupDataHeader = Readonly<{
  // isVisible: boolean;
  displayName: string;
  keyInEntryGroup: string;
}>;
export type LookupData = Readonly<{
  dataByKeyInGroup: ReadonlyArray<Readonly<{[key: string]: string}>>;
  headers: ReadonlyArray<LookupDataHeader>;
}>;
export interface DataParams {
  data: Promise<LookupData>;
}
export interface Data extends DataParams {
  kind: ResultAction.Data;
}
export function data(params: DataParams): Data {
  return {
    kind: ResultAction.Data,
    ...params,
  };
}
export interface OverrideParams {
  campaignId?: Contracts.CampaignId;
  startDate?: Date;
}
export interface Override extends OverrideParams {
  kind: ResultAction.Override;
}
export function override(params: OverrideParams): Override {
  return {
    kind: ResultAction.Override,
    ...params,
  };
}
export interface Nop {
  kind: ResultAction.Nop;
}
export function nop(): Nop {
  return {
    kind: ResultAction.Nop,
  };
}
export type AllResults = Nop | Override | Data;
<br>

Still need help? Contact Us Contact Us