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. PrefercommaSeparatedNumberfor 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'ssrcwithassets/img/loadingError.svgwhen load fails. Use as the@errorhandler 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 onuserData.noId(BNI cohort), the signup-date cutoff2023-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 forvue-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 whenvue-toastificationis 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:
- It's pure (no Vuex/$axios/cookie access).
- It's used by more than one component or page.
- 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
formatMetricson a number that's already been formatted by another component — you'll double-format. - Don't use
getAvatarBgColorfor 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:
@ 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¶
renderPostStatusfalls through to'Generating...'for unknown statuses. Adding new backend statuses without updating the helper hides bugs as "Generating..." spinners. See13-post-lifecycle.md.getPlanList's date cutoff is hardcoded.2023-12-27T12:00:00Zseparates "old" from "new" users. Don't change it without explicit sign-off — it's a pricing-cohort divider with revenue impact.thousandSeparatorcalls.toString()on its input without null/undefined guards. Pass a number or string; passingundefinedthrows.getInitialsuses optional chaining, so it returnsundefined(not'') for nullish input. Wrap with|| ''if you concatenate the result.