npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@wisemen/wise-crm-web

v1.3.1

Published

CRM frontend package with Vue 3 components, composables and types

Downloads

1,253

Readme

@wisemen/wise-crm-web

A comprehensive CRM component library for Vue 3 applications, providing ready-to-use components, composables, and utilities for building CRM features.

Table of Contents


Installation

# In a monorepo with pnpm workspaces
pnpm add @wisemen/wise-crm-web@workspace:*

Or for external projects:

npm install @wisemen/wise-crm-web

Quick Start

1. Initialize the CRM

Initialize the CRM configuration in your root layout or app entry point:

<script setup lang="ts">
import { useWiseCrmConfig } from '@wisemen/wise-crm-web'
import { BoardColumnTransitionTriggerType } from '@wisemen/wise-crm-web/client'

const wiseCrm = useWiseCrmConfig()

wiseCrm.init({
  apiCrmBaseUrl: 'https://api.your-crm.com',
  getAccessToken: () => oAuthClient.getAccessToken(),
  googleMapsApiKey: 'YOUR_GOOGLE_MAPS_API_KEY',
  locale: 'en-US',
  options: {
    deals: {
      allowedTransitionTriggerTypes: [
        BoardColumnTransitionTriggerType.EMAIL,
        BoardColumnTransitionTriggerType.MANUAL,
      ],
    },
    widgets: {
      entityStatistics: {
        viewAllBusinessRouteName: 'crm-businesses',
        viewAllIndividualRouteName: 'crm-individuals',
      },
      recentlyCreatedEntities: true,
    },
  },
})
</script>

2. Set Up Routes

Configure CRM routes in your router:

import { useWiseCrmRoutes } from '@wisemen/wise-crm-web'

const routes = [
  // ... your other routes
  useWiseCrmRoutes({
    businessTabRoutes: [
      {
        label: computed(() => 'Information'),
        route: {
          name: 'crm-business-information',
          component: BusinessInformationTabView,
        },
      },
    ],
    individualTabRoutes: [
      {
        label: computed(() => 'Information'),
        route: {
          name: 'crm-individual-information',
          component: IndividualInformationTabView,
        },
      },
    ],
    dealTabRoutes: [
      {
        label: computed(() => 'Information'),
        route: {
          name: 'crm-deal-information',
          component: DealInformationTabView,
        },
      },
    ],
    excludedReturnRoutes: [], // Optional: routes to exclude from return navigation
  }),
]

3. Integrate i18n and Icons

// i18n setup
import { wiseCrmLocales } from '@wisemen/wise-crm-web'

const messages = {
  'en-US': {
    ...yourEnLocale,
    ...wiseCrmLocales['en-US'],
  },
  'nl-BE': {
    ...yourNlLocale,
    ...wiseCrmLocales['nl-BE'],
  },
}

// Icons setup
import { wiseCrmIcons } from '@wisemen/wise-crm-web'

export const icons = {
  ...wiseCrmIcons,
  ...yourCustomIcons,
}

Configuration

useWiseCrmConfig().init(options)

Initialize the CRM with the following configuration options:

Required Parameters

| Parameter | Type | Description | |-----------|------|-------------| | apiCrmBaseUrl | string | Base URL for the CRM API | | getAccessToken | () => Promise<string> | Function that returns the current authentication token | | googleMapsApiKey | string | Google Maps API key for address autocomplete | | locale | Locale | Initial locale ('en-US' or 'nl-BE') |

Options

options.deals

Configure deal/pipeline behavior:

{
  deals: {
    // Types of transitions allowed in the deal board
    allowedTransitionTriggerTypes: BoardColumnTransitionTriggerType[]
  } | null
}

Available transition types:

  • BoardColumnTransitionTriggerType.MANUAL - Manual drag & drop
  • BoardColumnTransitionTriggerType.EMAIL - Email-triggered transitions
options.widgets

Configure widget behaviors:

{
  widgets: {
    entityStatistics?: {
      // Route names for "View All" buttons in statistics widgets
      viewAllBusinessRouteName: string
      viewAllIndividualRouteName: string
    }
    // Enable recently created entities widget
    recentlyCreatedEntities?: true
  }
}

Components

Overview Components

WiseCrmOverviewContent

Main CRM dashboard with statistics widgets and recent activity.

<template>
  <WiseCrmOverviewContent />
</template>

Deal Components

WiseCrmCrmDealOverview

Kanban-style deal pipeline board with drag-and-drop.

<template>
  <WiseCrmCrmDealOverview />
</template>

Dialog Components

WiseCrmContactCreateDialog

Dialog for adding contact persons to a business.

<script setup lang="ts">
import { useWiseCrmContactCreateDialog, WiseCrmContactCreateDialog } from '@wisemen/wise-crm-web'

const contactDialog = useWiseCrmContactCreateDialog()

// Open from anywhere in your app
function addContact(): void {
  contactDialog.open(businessUuid)
}
</script>

<template>
  <WiseCrmContactCreateDialog />
</template>

Card Components

WiseCrmBusinessUpdateCard

Form card for updating business information.

<script setup lang="ts">
import { useRouteParams } from '@vueuse/router'
import type { WiseCrmBusinessUuid } from '@wisemen/wise-crm-web'
import { WiseCrmBusinessUpdateCard } from '@wisemen/wise-crm-web'

const businessUuid = useRouteParams<WiseCrmBusinessUuid>('businessUuid', null)
</script>

<template>
  <WiseCrmBusinessUpdateCard
    v-if="businessUuid !== null"
    :business-uuid="businessUuid"
  />
</template>

Props:

  • businessUuid: WiseCrmBusinessUuid - The business to update

WiseCrmBusinessBillingInformationCard

Form card for managing business billing information.

<template>
  <WiseCrmBusinessBillingInformationCard :business-uuid="businessUuid" />
</template>

Props:

  • businessUuid: WiseCrmBusinessUuid - The business UUID

WiseCrmIndividualUpdateCard

Form card for updating individual information.

<template>
  <WiseCrmIndividualUpdateCard :individual-uuid="individualUuid" />
</template>

Props:

  • individualUuid: WiseCrmIndividualUuid - The individual UUID

WiseCrmIndividualBillingInformationCard

Form card for managing individual billing information.

<template>
  <WiseCrmIndividualBillingInformationCard :individual-uuid="individualUuid" />
</template>

Props:

  • individualUuid: WiseCrmIndividualUuid - The individual UUID

Widget Components

WiseCrmBusinessWidget

Statistics widget showing business metrics.

<template>
  <WiseCrmBusinessWidget />
</template>

WiseCrmIndividualWidget

Statistics widget showing individual metrics.

<template>
  <WiseCrmIndividualWidget />
</template>

WiseCrmRecentlyCreatedWidget

Widget displaying recently created entities.

<template>
  <WiseCrmRecentlyCreatedWidget />
</template>

Composables

useWiseCrm()

Main composable for CRM operations and navigation.

import { useWiseCrm } from '@wisemen/wise-crm-web'

const crm = useWiseCrm()

Methods

openBusinessDetail(businessUuid: WiseCrmBusinessUuid): void

Navigate to business detail page.

crm.openBusinessDetail(businessUuid)
openIndividualDetail(individualUuid: WiseCrmIndividualUuid): void

Navigate to individual detail page.

crm.openIndividualDetail(individualUuid)
getEntityConfig(type: EntityType): EntityConfig | null

Get configuration for a specific entity type.

const businessConfig = crm.getEntityConfig(EntityType.BUSINESS)
console.log(businessConfig?.name) // Localized name like "Business" or "Bedrijf"
goToReturnRoute(): void

Navigate back to the stored return route (useful for "back" buttons).

crm.goToReturnRoute()
storeReturnRoute(route: RouteLocationNormalized, excludedRoutes?: (string | RegExp)[]): void

Manually store a return route (usually handled automatically by useWiseCrmRoutes).

crm.storeReturnRoute(router.currentRoute.value, ['crm-detail'])

useWiseCrmConfig()

CRM initialization and configuration composable.

import { useWiseCrmConfig } from '@wisemen/wise-crm-web'

const wiseCrm = useWiseCrmConfig()

Properties

isInitialized: Ref<boolean>

Reactive flag indicating if CRM has been initialized.

watch(wiseCrm.isInitialized, (initialized) => {
  if (initialized) {
    console.log('CRM is ready!')
  }
})

Methods

init(props: InitProps): Promise<void>

Initialize the CRM (see Configuration section).

getConfig(): TenantConfig

Get the current tenant configuration.

const config = wiseCrm.getConfig()
console.log(config.entities) // Entity configurations
getOptions(): InitProps['options']

Get the initialization options.

const options = wiseCrm.getOptions()
console.log(options.deals?.allowedTransitionTriggerTypes)
getCustomFieldDefinitionForEntity(entityType: EntityType): CustomFieldDefinitionIndex[]

Get custom field definitions for an entity type.

const customFields = wiseCrm.getCustomFieldDefinitionForEntity(EntityType.BUSINESS)

useWiseCrmContactCreateDialog()

Composable for controlling the contact creation dialog.

import { useWiseCrmContactCreateDialog } from '@wisemen/wise-crm-web'

const contactDialog = useWiseCrmContactCreateDialog()

Methods

open(businessUuid: WiseCrmBusinessUuid): void

Open the contact creation dialog for a specific business.

contactDialog.open(businessUuid)

Routing

useWiseCrmRoutes(options)

Generate CRM routes with tab-based detail views.

import type { ComputedRef } from 'vue'
import { useWiseCrmRoutes } from '@wisemen/wise-crm-web'

interface CrmRouteOptions {
  label: ComputedRef<string>
  route: RouteRecordRaw
}

const crmRoutes = useWiseCrmRoutes({
  businessTabRoutes: CrmRouteOptions[],
  individualTabRoutes: CrmRouteOptions[],
  dealTabRoutes: CrmRouteOptions[],
  excludedReturnRoutes?: (string | RegExp)[], // Optional
})

Parameters

  • businessTabRoutes: Array of tab routes for business detail pages
  • individualTabRoutes: Array of tab routes for individual detail pages
  • dealTabRoutes: Array of tab routes for deal detail pages
  • excludedReturnRoutes: Routes to exclude from return navigation tracking

Example

const crmRoutes = useWiseCrmRoutes({
  businessTabRoutes: [
    {
      label: computed(() => i18n.t('information')),
      route: {
        name: 'crm-business-information',
        path: 'information',
        component: BusinessInformationView,
      },
    },
    {
      label: computed(() => i18n.t('contacts')),
      route: {
        name: 'crm-business-contacts',
        path: 'contacts',
        component: BusinessContactsView,
      },
    },
  ],
  individualTabRoutes: [
    {
      label: computed(() => i18n.t('information')),
      route: {
        name: 'crm-individual-information',
        path: 'information',
        component: IndividualInformationView,
      },
    },
  ],
  dealTabRoutes: [],
  excludedReturnRoutes: [
    'crm-business-detail',
    'crm-individual-detail',
    /^crm-.*-edit$/,
  ],
})

Generated Routes

The composable automatically generates these route patterns:

  • /individual/:individualUuid - Individual detail page with tabs
  • /business/:businessUuid - Business detail page with tabs
  • /deal/:dealUuid - Deal detail page with tabs

Internationalization

Available Locales

  • en-US - English (United States)
  • nl-BE - Dutch (Belgium)

Integration

import { wiseCrmLocales } from '@wisemen/wise-crm-web'

const i18n = createI18n({
  messages: {
    'en-US': {
      ...yourMessages,
      ...wiseCrmLocales['en-US'],
    },
    'nl-BE': {
      ...yourMessages,
      ...wiseCrmLocales['nl-BE'],
    },
  },
})

Updating Locale

The CRM automatically watches for locale changes if you're using vue-i18n. To manually update:

import { useI18n } from 'vue-i18n'

const i18n = useI18n()
i18n.locale.value = 'nl-BE' // CRM will automatically update

Icons

Integration

import { wiseCrmIcons } from '@wisemen/wise-crm-web'

export const icons = {
  ...wiseCrmIcons,
  // Your custom icons
  customIcon: () => import('./CustomIcon.vue'),
}

Available Icons

The CRM includes icons for:

  • Entity types (business, individual, deal)
  • Actions (add, edit, delete, search)
  • Navigation (back, forward, close)
  • Status indicators
  • And more...

Testing Utilities

Factory Classes

The library includes factory classes for generating mock data in tests.

Import Path

import {
  BusinessFactory,
  IndividualFactory,
  DealFactory,
  NoteFactory,
} from '@wisemen/wise-crm-web/testing'

BusinessFactory

Methods

createDetail(overrides?): ViewBusinessDetailResponse

Create a mock business detail object.

const business = BusinessFactory.createDetail({
  name: 'Custom Corp',
  email: '[email protected]',
})
createIndex(overrides?): ViewBusinessIndexResponseItem

Create a mock business list item.

const businessItem = BusinessFactory.createIndex({
  name: 'List Item Corp',
})
createIndexList(count, overrides?): ViewBusinessIndexResponseItem[]

Create an array of mock business list items.

const businesses = BusinessFactory.createIndexList(10)
createBillingInformation(overrides?): ViewBusinessBillingInformationDetailResponse

Create mock billing information.

const billing = BusinessFactory.createBillingInformation({
  invoiceEmail: '[email protected]',
})
createContactPerson(overrides?): BusinessContactPersonResponse

Create a mock contact person.

const contact = BusinessFactory.createContactPerson({
  firstName: 'John',
  lastName: 'Doe',
})
createStatistics(overrides?): ViewBusinessStatisticsResponse

Create mock business statistics.

const stats = BusinessFactory.createStatistics({
  total: 100,
  monthlyAdded: 10,
})

IndividualFactory

Similar methods to BusinessFactory for individual entities:

  • createDetail(overrides?)
  • createIndex(overrides?)
  • createIndexList(count, overrides?)
  • createBillingInformation(overrides?)
  • createStatistics(overrides?)

DealFactory

  • createDetail(overrides?)
  • createIndex(overrides?)
  • createIndexList(count, overrides?)

NoteFactory

  • create(overrides?)
  • createList(count, overrides?)

Testing Example

import { expect, test } from '@playwright/test'
import { BusinessFactory } from '@wisemen/wise-crm-web/testing'
import { HttpResponse } from 'msw'

test('Create business', async ({ page, http, worker }) => {
  const mockBusiness = BusinessFactory.createDetail({
    name: 'Test Business',
  })

  await worker.use(
    http.post('*/api/v1/businesses', () => {
      return HttpResponse.json(
        { uuid: mockBusiness.uuid },
        { status: 201 }
      )
    }),
    http.get(`*/api/v1/businesses/${mockBusiness.uuid}`, () => {
      return HttpResponse.json(mockBusiness)
    }),
  )

  await page.goto('/crm/businesses')
  // ... rest of test
})

Type System

Entity UUID Types

The library exports branded UUID types for type safety:

import type {
  WiseCrmBusinessUuid,
  WiseCrmIndividualUuid,
} from '@wisemen/wise-crm-web'

// These are branded types that prevent mixing UUIDs
function updateBusiness(uuid: WiseCrmBusinessUuid): void {
  // TypeScript ensures you can't pass WiseCrmIndividualUuid here
}

Validation Schemas

UUID schemas for runtime validation:

import {
  businessUuidSchema,
  individualUuidSchema,
} from '@wisemen/wise-crm-web'

// Use with zod or similar validators
const result = businessUuidSchema.safeParse(value)

Dialog Content Tabs

import type { WiseCrmDialogContentTab } from '@wisemen/wise-crm-web'

// Type for custom dialog tab content
const tabs: WiseCrmDialogContentTab[] = [
  { label: 'General', component: GeneralTab },
  { label: 'Advanced', component: AdvancedTab },
]

Environment Variables

Required

# CRM API Base URL
API_CRM_BASE_URL=https://api.your-crm.com

# Google Maps API Key (for address autocomplete)
GOOGLE_MAPS_API_KEY=your_google_maps_api_key

Example .env

API_CRM_BASE_URL=https://wise-crm.development.appwi.se
GOOGLE_MAPS_API_KEY=AIzaSyD0DOeAK9X_gXAtwPEyKPl4CaJc1dhKC9Q

Advanced Usage

Custom Field Definitions

Access custom field definitions for dynamic form generation:

import { EntityType } from '@wisemen/wise-crm-web/client'
import { useWiseCrmConfig } from '@wisemen/wise-crm-web'

const crmConfig = useWiseCrmConfig()

const customFields = crmConfig.getCustomFieldDefinitionForEntity(
  EntityType.BUSINESS
)

// Use customFields to render dynamic form fields

Entity Configuration

Get localized entity names and colors:

import { EntityType } from '@wisemen/wise-crm-web/client'
import { useWiseCrm } from '@wisemen/wise-crm-web'

const crm = useWiseCrm()
const businessConfig = crm.getEntityConfig(EntityType.BUSINESS)

console.log(businessConfig?.name) // "Business" or "Bedrijf"
console.log(businessConfig?.color) // "blue", "green", etc.

CSS Custom Properties

The CRM automatically sets CSS custom properties for entity colors:

/* Available CSS variables */
--wise-crm-business-50
--wise-crm-business-100
--wise-crm-business-200
/* ... etc for all color shades */

--wise-crm-individual-50
--wise-crm-individual-100
/* ... etc */

Use these in your custom components for consistent theming:

<style scoped>
.business-badge {
  background-color: var(--wise-crm-business-100);
  color: var(--wise-crm-business-700);
}
</style>

Troubleshooting

"CRM Tenant Config not initialized" Error

Cause: Attempting to use CRM components before initialization.

Solution: Ensure useWiseCrmConfig().init() is called before rendering any CRM components. Use isInitialized to conditionally render:

<script setup lang="ts">
import { useWiseCrmConfig } from '@wisemen/wise-crm-web'

const crmConfig = useWiseCrmConfig()

// Initialize in onMounted or similar
onMounted(async () => {
  await crmConfig.init({ /* ... */ })
})
</script>

<template>
  <div v-if="crmConfig.isInitialized.value">
    <WiseCrmOverviewContent />
  </div>
  <div v-else>
    Loading CRM...
  </div>
</template>

TypeScript Errors with Factory Methods

Cause: Stale TypeScript cache or build artifacts.

Solution:

  1. Restart TypeScript server: Cmd+Shift+P → "TypeScript: Restart TS Server"
  2. Rebuild packages: pnpm build
  3. Clear build cache: rm -rf dist node_modules/.cache

Address Autocomplete Not Working

Cause: Invalid or missing Google Maps API key.

Solution: Ensure your GOOGLE_MAPS_API_KEY environment variable is set and the key has the Places API enabled in Google Cloud Console.


Examples

Complete Integration Example

<!-- App.vue -->
<script setup lang="ts">
import { useWiseCrmConfig } from '@wisemen/wise-crm-web'
import { BoardColumnTransitionTriggerType } from '@wisemen/wise-crm-web/client'
import { onMounted } from 'vue'

const crmConfig = useWiseCrmConfig()

onMounted(async () => {
  await crmConfig.init({
    apiCrmBaseUrl: import.meta.env.API_CRM_BASE_URL,
    getAccessToken: () => authClient.getToken(),
    googleMapsApiKey: import.meta.env.GOOGLE_MAPS_API_KEY,
    locale: 'en-US',
    options: {
      deals: {
        allowedTransitionTriggerTypes: [
          BoardColumnTransitionTriggerType.MANUAL,
        ],
      },
      widgets: {
        entityStatistics: {
          viewAllBusinessRouteName: 'businesses',
          viewAllIndividualRouteName: 'individuals',
        },
        recentlyCreatedEntities: true,
      },
    },
  })
})
</script>

<template>
  <RouterView v-if="crmConfig.isInitialized.value" />
</template>
// router.ts
import { createRouter } from 'vue-router'
import { useWiseCrmRoutes } from '@wisemen/wise-crm-web'
import { computed } from 'vue'

const router = createRouter({
  routes: [
    {
      path: '/crm',
      children: [
        {
          name: 'crm-overview',
          path: '',
          component: () => import('./views/CrmOverview.vue'),
        },
        {
          name: 'crm-businesses',
          path: 'businesses',
          component: () => import('./views/BusinessList.vue'),
        },
        {
          name: 'crm-individuals',
          path: 'individuals',
          component: () => import('./views/IndividualList.vue'),
        },
      ],
    },
    useWiseCrmRoutes({
      businessTabRoutes: [
        {
          label: computed(() => 'Information'),
          route: {
            name: 'business-info',
            path: 'information',
            component: () => import('./views/BusinessInfo.vue'),
          },
        },
      ],
      individualTabRoutes: [
        {
          label: computed(() => 'Information'),
          route: {
            name: 'individual-info',
            path: 'information',
            component: () => import('./views/IndividualInfo.vue'),
          },
        },
      ],
      dealTabRoutes: [],
    }),
  ],
})

License

MIT

Support

For issues and questions, please contact the WiseMen Digital team.