Skip to content

17 — Helpers & shared utilities

A reference for the shared utility functions in helpers/. Most of these are heavily used; reuse them rather than reimplementing.

Locations

helpers/
├── helper.js                ← the main grab-bag (164 lines, ~12 exports)
├── circleNetworkHelper.js   ← Circle Network helpers (small)
├── mixins/                  ← shared Vue mixins
└── models/                  ← data shape definitions

The main file imported across the app is helpers/helper.js. Everything in this section refers to it unless stated otherwise.

helpers/helper.js — function reference

Number formatting

import { formatMetrics, commaSeparatedNumber, thousandSeparator } from '@/helpers/helper'

formatMetrics(950)       // "950"
formatMetrics(1234)      // "1.2K"
formatMetrics(1_500_000) // "1.5M"
formatMetrics(2.5e9)     // "2.5B"

commaSeparatedNumber(1234567)   // "1,234,567"
thousandSeparator('1234567')    // "1,234,567" — accepts strings; commaSeparatedNumber coerces via Number()
  • formatMetrics(n) — abbreviates large numbers with K / M / B / T suffixes. Returns 0 for falsy input. Use for dashboard cards, leaderboard scores, reach numbers.
  • commaSeparatedNumber(value) — adds thousands separators. Falsy → 0, non-numeric → returned as-is.
  • thousandSeparator(value) — same idea but assumes a stringifiable input. Prefer commaSeparatedNumber for new code; it handles edge cases better.

Avatar helpers

import { getInitials, getAvatarBgColor } from '@/helpers/helper'

getInitials('Jane Doe')      // "JD"
getAvatarBgColor('Jane Doe') // a hex colour, deterministic from the first character
  • getInitials(name) — returns up to N uppercase initials, one per space-separated word.
  • getAvatarBgColor(name) — returns one of seven Bootstrap-style hex colours, deterministically based on the first character's char code. Use for placeholder avatars where you'd otherwise show a default image.

String helpers

import { minimizeLengthString } from '@/helpers/helper'

minimizeLengthString('A very long account name that overflows')
// "A very long acc..."
  • minimizeLengthString(s) — truncates strings longer than 17 chars to 15 chars + '...'. Used for sidebar labels, table cells. Note the asymmetric numbers (17/15) — that is intentional.

URL / query-string

import { queryStringToObject } from '@/helpers/helper'

queryStringToObject('a=1&b=hello%20world')   // { a: '1', b: 'hello world' }
  • queryStringToObject(qs) — parses a &-joined query string. URL-decodes values. Doesn't handle nested keys or arrays.

Image error handling

import { handleImageError } from '@/helpers/helper'

// Inside a Vue component
<img :ref="'avatar-' + id" @error="handleImageError($refs, 'avatar-' + id)" />
  • handleImageError(ref, refId) — replaces the image's src with assets/img/loadingError.svg when load fails. Use as the @error handler on every <img> that might 404.

Post status helpers

import { renderPostStatus, calStatus } from '@/helpers/helper'

renderPostStatus(post)   // "Scheduled" / "Needs approval" / "Partially Published" / etc.
calStatus(item)          // "POSTED" / "POSTED_WITH_ERROR" / "FAILED" / etc.
  • renderPostStatus(pData) — returns the human-readable label. Used in lists, cards.
  • calStatus(item) — returns the calendar status code. Used by the content-planner calendar to colour-code events.

These encode the partial-publish edge cases and the approval gate. Always go through these helpers rather than comparing status directly; see 13-post-lifecycle.md.

Plan filtering

import { getPlanList } from '@/helpers/helper'

const visiblePlans = getPlanList(userData, allPlans, /* isFromBilling */ true)
  • getPlanList(userData, planList, isFromBilling, avoidIsNew) — filters the full plan list down to what should be shown to this user. Branches on userData.noId (BNI cohort), the signup-date cutoff 2023-12-27, and whether we're on the billing surface vs onboarding.

See 08-integrations.md → Plan filtering and noId and 12-permissions-and-roles.md for the policy. Don't reimplement plan filtering; everything that displays a plan list should go through this function.

Toast options

import { TOAST_OPTIONS, TOAST_LIB_OPTIONS } from '@/helpers/helper'

this.$toast.success('Saved!', TOAST_OPTIONS)
  • TOAST_OPTIONS — preset for vue-toastification: bottom-right, 5s timeout, click-to-dismiss, pause-on-hover, draggable. Use this preset on every $toast.* call so toasts feel consistent.
  • TOAST_LIB_OPTIONS — library-init preset (maxToasts: 20, newestOnTop: true). Used when vue-toastification is registered.

For the older vue-snotify toasts (this.$snotify.*), there is no equivalent preset — use library defaults. New code should prefer vue-toastification.

helpers/circleNetworkHelper.js

Small file (20 lines) with helpers used by the Circle Network feature. Verify with the team for exact contract. The services/circleNetworkService.js and plugins/circleNetwork.js are companions.

helpers/mixins/

Vue mixins shared across components. Verify with the team for the inventory — the directory is sparsely used compared to mixins/onboardingNavigation.js at the repo root.

helpers/models/

Data-shape declarations / model definitions. Verify with the team for exact contents. These are referenced by some components for normalising backend responses; not all components use them.

Patterns

When to add a new helper

A function belongs in helpers/helper.js if all of the following hold:

  1. It's pure (no Vuex/$axios/cookie access).
  2. It's used by more than one component or page.
  3. It's not specific to a single feature surface.

Functions tied to a feature (e.g. only used in the content planner) belong in a feature-specific file or as a method on the relevant Vuex module. Don't bloat helper.js with one-off code.

When NOT to use a helper

  • Don't call formatMetrics on a number that's already been formatted by another component — you'll double-format.
  • Don't use getAvatarBgColor for stable user identity colours — it's deterministic per character but two users with the same first character will collide.
  • Don't route formatted output through commaSeparatedNumber — it expects raw numeric input and silently returns its argument unchanged when it can't parse.

Importing

The canonical import path uses the @ alias:

import { formatMetrics, getInitials } from '@/helpers/helper'

@ resolves to the repo root (see 09-conventions.md). Don't use relative paths to helper.js from page or component files — they'll work but they're inconsistent with the rest of the codebase.

Sharp edges

  • renderPostStatus falls through to 'Generating...' for unknown statuses. Adding new backend statuses without updating the helper hides bugs as "Generating..." spinners. See 13-post-lifecycle.md.
  • getPlanList's date cutoff is hardcoded. 2023-12-27T12:00:00Z separates "old" from "new" users. Don't change it without explicit sign-off — it's a pricing-cohort divider with revenue impact.
  • thousandSeparator calls .toString() on its input without null/undefined guards. Pass a number or string; passing undefined throws.
  • getInitials uses optional chaining, so it returns undefined (not '') for nullish input. Wrap with || '' if you concatenate the result.