Smart multi-column layout for Add Lead form
Restructure the page into a top-level grid so sections compose horizontally instead of stretching individual fields: - Market & Assignment uses a 3-col interio...
Use /api/security/members/names for assigned-member name resolution
The by-department endpoint filters to active TeamMembers only, missing assignments to base Members or inactive members. Switch to /names which returns all membe...
Add /names endpoint returning id→name for all members regardless of state
The by-department endpoint filters to active TeamMembers only, but extension assignments may reference base Members or inactive members. Use /names for ID→name ...
Make Best Contact Time a free-text field
Replace the Morning/Afternoon/Evening dropdown on the Add Lead and Lead View edit forms with a plain text input so members can describe specific windows (e.g., ...
Full-width Add Lead form and clearance for sticky footer
Drop the 1200px max-width on /leads/new so the form spans the page area, and bump bottom padding so the sticky action bar no longer overlaps the last form secti...
Single-lead Add Lead form at /leads/new
Refactor Add Lead from an in-page multi-tab pane into a dedicated route at /leads/new so it can be linked directly. The current market is passed through as ?mar...
Add **************** endpoint
Returns salesType, subType, and customForms for a market by marketId so the Add Lead form can render sub-type fields and intake custom forms without first creat...
Resolve display name with correct priority in by-department endpoint
Priority: member.usernameAlias → user.name.formatFull() → user.alias → user.username
Add missing isDisplayOnIntake field to FormField interface and new field literal
Serialize Long as string to prevent JS precision loss on large CockroachDB IDs
Coerce isDisplayOnIntake=true when isRequired=true in CustomFormService
Add member name display and unassign button to per-instance extensions tab
Show member names and add Unassign action to org-level extensions tab
Wire VOIP configuration to provider+extension selectors with assignment enforcement
Add typed DTOs, unassign API method, and update MembershipPermissionsTab VOIP types
Add DELETE assignment endpoint to VoipExtensionsController for admin unassign
Add MemberVoipConfigController for member VOIP config with extension assignment
Add findByAssignedMemberId query methods to PhoneExtensionRepository
Probe master model endpoint and fix appliedModel org ID
- Replace appliedModel.source-based gate with a direct probe of /api/security/master-model on mount; r.ok=false (409) means the org is governed by a branch ...
Gate censorship tab options by applied model constraints
- Hide EDIT MASTER MODEL button when org is governed by a branch type (use appliedModel.source instead of unreliable org context branchTypeID) - Hide ATF fiel...
Persist active mailbox in URL query params
Switching mailboxes now updates the URL (?mailboxType, ?mailboxId, ?mailbox) so refreshing the browser restores the same mailbox. MessageBrowser reads window.lo...
Mark-as-read and message fetch for shared/member mailboxes
Classic mode now loads shared mailbox messages via the correct /messages/{folder} endpoint (was calling the non-existent /conversations endpoint). Fetching a fu...
Mark-as-read for shared and member mailboxes
EmailController.setFlags now accepts an optional mailboxId query param and routes through resolveProviderWithMailbox so member-mailbox flags are set against the...
Show accurate provider connection status from isConnected field
Replaces the old logic that showed "Connected" for any provider with no sync error (including brand-new providers that were never tested). New three-state badg...
Persist connection test result to isConnected field
- test() endpoint saves isConnected=true/false to DB after every health check - update() resets isConnected to null when configJson changes (credentials may h...
Add isConnected field to VoipProviderInstance
Nullable Boolean: null = never tested, true = last test passed, false = failed. Requires KamoInitializerService schema migration to add IS_CONNECTED column.
Use extensionId as external ID, not Relay node id
FreePBX returns the same Relay id (ZXh0ZW5zaW9uOg== = "extension:") for every extension because the id field is not populated. Using it caused all extensions to...
Handle IMAPInputStream in getMessage to prevent ClassCastException
JavaMail can return an IMAPInputStream instead of a decoded String from part.getContent() when message content has not been pre-fetched. The direct (String) cas...
Show active offer banner with live millisecond countdown
Fetches the active promotion server-side on the home page and renders a HeroOfferBanner between the subheadline and the free-forever callout. The banner shows t...
Wire Available/Unavailable switch to backend and fix OFFLINE presence bug
- Rename Paused/Active chip to Available/Unavailable in NavTop - Fetch initial availability on mount and persist toggles via new me/availability API - Add isAva...
Add isAvailable to by-department response and me/availability endpoints
Rewrite all GQL queries/mutations against live schema introspection
Verified every field name and type against the actual FreePBX 17 schema. Full set of corrections: Queries: - fetchAllExtensions: remove non-existent 'voicemail...
Pass selectedFolder to ThreadDetailView so email:folder-changed fires with a valid folder
conversation.folder is always undefined because the Java ConversationEntity has folderIds (text[]) not a folder string field. This caused loadThread to call mar...
Correct GraphQL field name to fetchAsteriskDetails
FreePBX API returns "Did you mean fetchAsteriskDetails?" — rename the health check query and its data path accessor accordingly.
Like what you see shipping?
Every one of these updates lands in your workspace automatically. Start free and watch it grow week after week.