/** * Presentational view models and pure helpers for the sales Svelte surfaces. * * The components are props-driven and presentational: hosts map their model * rows (CRM, referrals, commissions) onto these plain view-model interfaces * and wire the action callbacks. Only STATUS UNIONS are imported from the * sibling TS modules — type-only, so the compiled Svelte bundle never gains a * runtime dependency on the model/collection code. Monetary fields stay * integer cents in every view model; conversion to display strings happens at * render time via `format.ts`. * * Dashboard math, award validation, and status flows live here as exported * pure functions so they are unit-testable without mounting components. * * @module */ import type { CommissionAdjustmentKind, CommissionBasis, CommissionPayoutStatus, CommissionStatus, EarnerBalance, PayoutMethod } from '../commissions/index.js'; import type { LeadHumanActivityKind, LeadStatus, OpportunityStatus } from '../crm/index.js'; import type { AttributionAward, AttributionExceptionStatus, ReferralAgreementApprovalMode, ReferralAgreementStatus, ReferralLinkStatus, ReferralStatus, ReferralTouchKind } from '../referrals/index.js'; export type { AttributionAward, EarnerBalance }; /** Date-ish view-model field: hosts may pass `Date`s or ISO strings. */ export type DateInput = Date | string | null; /** Badge variant vocabulary (structurally matches `@happyvertical/smrt-ui`). */ export type StatusBadgeVariant = 'default' | 'primary' | 'success' | 'warning' | 'error' | 'info'; /** One assignable sales representative for owner pickers. */ export interface SalesRepOptionView { id: string; name: string; } /** Open next action attached to a lead/opportunity. */ export interface NextActionView { summary: string; dueAt?: DateInput; } /** Row of {@link LeadList}. */ export interface LeadListItemView { id: string; name: string; contactName?: string; email?: string; phone?: string; organizationName?: string; /** Human label for the acquisition source (`sourceKind`/`sourceId`). */ sourceLabel?: string; ownerRepId?: string; ownerName?: string; status: LeadStatus; /** Set on terminal `merged` leads — id of the surviving lead. */ mergedIntoId?: string; /** Earliest open next action, if any. */ nextAction?: NextActionView | null; } /** Header/facts view for the follow-up-focused {@link LeadDetail} surface. */ export interface LeadDetailView { id: string; name: string; status: LeadStatus; contactName?: string; email?: string; phone?: string; organizationName?: string; ownerRepId?: string; ownerName?: string; /** Set when the Lead was folded into a surviving Lead. */ mergedIntoId?: string; } /** Draft passed by {@link LeadDetail}'s human-activity form. */ export interface LeadHumanActivityDraft { activityKind: LeadHumanActivityKind; summary: string; } /** Draft passed by {@link LeadDetail}'s next-action form. */ export interface LeadNextActionDraft { summary: string; /** ISO calendar date from the native date input; hosts select a clock/time. */ dueAt: string; } /** Pipeline stage (ordered) for board columns and stage pickers. */ export interface PipelineStageView { id: string; name: string; /** Default win probability of the stage (0–1). */ probability?: number; /** Terminal-won stage. */ isWon?: boolean; /** Terminal-lost stage. */ isLost?: boolean; } /** Opportunity card for {@link OpportunityBoard} and dashboard math. */ export interface OpportunityCardView { id: string; name: string; stageId: string; ownerName?: string; expectedValueCents: number; currency: string; /** Win probability (0–1). */ probability: number; status: OpportunityStatus; } /** Activity/next-action row for the {@link OpportunityDetail} timeline. */ export interface SalesActivityView { id: string; /** Open-string kind (`note`, `call`, `stage_change`, …). */ activityKind: string; summary: string; /** Event creation time, used to render a complete chronological timeline. */ createdAt?: DateInput; dueAt?: DateInput; completedAt?: DateInput; actorName?: string; } /** Recorded downstream conversion link (client/project/contract/…). */ export interface ConversionLinkView { id: string; targetKind: string; targetId: string; note?: string; /** Optional host-provided navigation target for the downstream record. */ href?: string; } /** Header/facts view for {@link OpportunityDetail}. */ export interface OpportunityDetailView { id: string; name: string; status: OpportunityStatus; stageId: string; stageName?: string; ownerName?: string; expectedValueCents: number; currency: string; /** Win probability (0–1). */ probability: number; expectedCloseAt?: DateInput; /** Outcome note (conventionally set when closing lost). */ outcomeReason?: string; wonAt?: DateInput; lostAt?: DateInput; } /** Terminal outcome accepted by `OpportunityDetail`'s `onClose`. */ export type ClosedOpportunityOutcome = Exclude; /** Draft passed to `OpportunityDetail`'s `onRecordActivity`. */ export interface RecordActivityDraft { activityKind: string; summary: string; /** ISO date (`YYYY-MM-DD`) from the date input, when scheduled. */ dueAt?: string | null; } /** Row of {@link ReferralLinkManager}. */ export interface ReferralLinkView { id: string; code: string; label?: string; targetUrl?: string; clickCount: number; status: ReferralLinkStatus; } /** Draft passed to `ReferralLinkManager`'s `onCreate`. */ export interface CreateReferralLinkDraft { targetUrl: string; label?: string; } /** Row of {@link ReferralStatusList} (referrer portal). */ export interface ReferralStatusView { id: string; status: ReferralStatus; targetKind: string; /** Optional human label for the qualifying target. */ targetLabel?: string; /** Credit share of this referral (0–1; `1` when unsplit). */ creditFraction: number; /** Present when the referral shares credit with siblings. */ splitGroupId?: string; programName?: string; attributedAt?: DateInput; qualifiedAt?: DateInput; expiresAt?: DateInput; } /** One competing touch inside an {@link AttributionExceptionView}. */ export interface AttributionCandidateView { touchId: string; referrerId: string; referrerName?: string; kind: ReferralTouchKind; /** ISO-8601 timestamp of the candidate touch. */ occurredAt: string; } /** Conflict-review row for {@link AttributionConflictQueue}. */ export interface AttributionExceptionView { id: string; status: AttributionExceptionStatus; targetKind: string; targetId: string; targetLabel?: string; programName?: string; conflictReason: string; candidates: AttributionCandidateView[]; /** Resolved-audit fields (set once `status === 'resolved'`). */ resolutionMode?: string; resolutionReason?: string; resolvedByName?: string; resolvedAt?: DateInput; } /** Append-only adjustment rendered under its commission row. */ export interface CommissionAdjustmentView { id: string; adjustmentKind: CommissionAdjustmentKind; /** Signed integer cents — clawbacks are negative. */ amountCents: number; currency: string; reason: string; createdAt?: DateInput; } /** Snapshotted calculation explanation for one commission row. */ export interface CommissionTraceView { planKey: string; planVersion: number; componentKey: string; } /** Row of {@link CommissionBreakdown} — the explainable-amount surface. */ export interface CommissionRowView { id: string; /** Earning-event kind (`conversion`, `invoice_payment`, …). */ eventKind: string; /** Human label for the earning source (`sourceKind`/`sourceId`). */ sourceLabel?: string; basis: CommissionBasis; baseAmountCents: number; /** Rate applied (0–1; `0` for fixed-basis components). */ rate: number; /** Split share applied (0–1; `1` when unsplit). */ shareFraction: number; amountCents: number; currency: string; status: CommissionStatus; /** When the clearing window ends and the earning can mature. */ clearingEndsAt?: DateInput; /** Parsed calculation-trace references (plan key@version, component). */ trace?: CommissionTraceView | null; /** Adjustments appended against this commission. */ adjustments?: CommissionAdjustmentView[]; } /** Settlement batch for {@link PayoutHistoryList} / {@link PayoutBatchReview}. */ export interface PayoutView { id: string; periodStart?: DateInput; periodEnd?: DateInput; commissionTotalCents: number; /** Signed integer cents. */ adjustmentTotalCents: number; /** Net batch total (commission + adjustments), signed. */ totalAmountCents: number; currency: string; payoutMethod: PayoutMethod; status: CommissionPayoutStatus; paymentReference?: string; providerRef?: string; paidAt?: DateInput; notes?: string; } /** Payout row with the earner identity, for operator review. */ export interface PayoutBatchReviewItemView extends PayoutView { earnerName?: string; } /** Agreement version row for {@link ExecutedAgreementsList}. */ export interface AgreementVersionView { id: string; version: number; status: ReferralAgreementStatus; effectiveFrom?: DateInput; effectiveTo?: DateInput; planKey: string; planVersion: number; clearingDays: number; approvalMode: ReferralAgreementApprovalMode; executionId?: string; executedAgreementId?: string; /** Immutable Asset-backed evidence; applications authorize retrieval. */ signedDocumentAssetId?: string; signedDocumentSha256?: string; auditTrailAssetId?: string; auditTrailSha256?: string; } /** Pre-aggregated operator reconciliation row for {@link CommissionExpenseSummary}. */ export interface CommissionExpenseRowView { /** Stable row key (e.g. `2026-06:USD` or a program/plan id). */ id: string; /** Human label for the row (period, program, plan, …). */ label: string; currency: string; /** Accrued commission expense in the period (integer cents). */ commissionExpenseCents: number; /** Signed unsettled/settled adjustments in the period. */ adjustmentCents: number; /** Amount settled to earners via payouts in the period. */ payoutCents: number; } /** Badge variant for a Lead lifecycle status. */ export declare function leadStatusBadgeVariant(status: LeadStatus): StatusBadgeVariant; /** Badge variant for an Opportunity lifecycle status. */ export declare function opportunityStatusBadgeVariant(status: OpportunityStatus): StatusBadgeVariant; /** Badge variant for a Referral lifecycle status. */ export declare function referralStatusBadgeVariant(status: ReferralStatus): StatusBadgeVariant; /** Badge variant for a ReferralLink status. */ export declare function referralLinkStatusBadgeVariant(status: ReferralLinkStatus): StatusBadgeVariant; /** Badge variant for a Commission settlement-chain status. */ export declare function commissionStatusBadgeVariant(status: CommissionStatus): StatusBadgeVariant; /** Badge variant for a CommissionPayout batch status. */ export declare function payoutStatusBadgeVariant(status: CommissionPayoutStatus): StatusBadgeVariant; /** Badge variant for a ReferralAgreement version status. */ export declare function agreementStatusBadgeVariant(status: ReferralAgreementStatus): StatusBadgeVariant; /** * Whether the qualify action applies to a lead in `status` — mirrors the * `new|working → qualified` transitions guarded by the Lead model. */ export declare function canQualifyLead(status: LeadStatus): boolean; /** Callback action gates for the generic Lead follow-up detail surface. */ export interface LeadWorkflowActions { canAssign: boolean; canStartWorking: boolean; canDisqualify: boolean; canRecordActivity: boolean; canScheduleNextAction: boolean; canCompleteNextAction: boolean; canQualify: boolean; } /** * Render-time action gates mirroring the workflow service: `new` and * `working` are active follow-up states; `disqualified` can be reopened; * qualified and merged Leads receive no ordinary follow-up actions. */ export declare function leadWorkflowActionsFor(status: LeadStatus): LeadWorkflowActions; /** Whether a next action is overdue relative to `now` (default: current time). */ export declare function isOverdue(dueAt: DateInput | undefined, now?: Date): boolean; /** A per-currency integer-cents total (pipeline sums never mix currencies). */ export interface CurrencyAmount { currency: string; amountCents: number; } /** Count of opportunities still `open`. */ export declare function openOpportunityCount(opportunities: OpportunityCardView[]): number; /** * Total expected value of OPEN opportunities, grouped per currency (sorted by * currency code) — currencies are never summed together. */ export declare function openPipelineTotals(opportunities: OpportunityCardView[]): CurrencyAmount[]; /** Per-stage open-pipeline summary for the dashboard tiles. */ export interface StagePipelineSummary { stageId: string; stageName: string; openCount: number; totals: CurrencyAmount[]; } /** * Open-pipeline expected value per stage, in the given stage order. Terminal * stages report zero (their opportunities are no longer `open`). */ export declare function pipelineValueByStage(stages: PipelineStageView[], opportunities: OpportunityCardView[]): StagePipelineSummary[]; /** * Win rate over CLOSED opportunities: `won / (won + lost)`, or `null` when * nothing has closed yet (so the tile can render a placeholder, not `0%`). */ export declare function winRate(opportunities: OpportunityCardView[]): number | null; /** One rendered board column: a stage and its opportunities. */ export interface BoardColumn { stage: PipelineStageView; opportunities: OpportunityCardView[]; } /** * Group opportunities into the given stage order for the board. Opportunities * pointing at a stage not present in `stages` are omitted — the board renders * exactly the columns it is given. */ export declare function groupOpportunitiesByStage(stages: PipelineStageView[], opportunities: OpportunityCardView[]): BoardColumn[]; /** Neighbouring stage ids for keyboard-accessible next/prev stage movement. */ export declare function adjacentStageIds(stages: PipelineStageView[], stageId: string): { prevStageId: string | null; nextStageId: string | null; }; /** * Share URL for a referral code: `/` (trailing slashes on * the base are normalised; the code is URL-encoded). */ export declare function buildShareUrl(shareBaseUrl: string, code: string): string; /** Loose http(s) URL check used to gate the create-link form. */ export declare function isHttpUrl(value: string): boolean; /** * Tolerance for award credit fractions summing to 1.0 — mirrors * `AttributionService.assertAwards` (±0.0001). */ export declare const AWARD_FRACTION_TOLERANCE = 0.0001; /** Result of {@link validateAwards} for inline form validation. */ export interface AwardValidation { valid: boolean; /** Raw sum of the entered fractions. */ totalFraction: number; /** Human-readable problem when invalid. */ message?: string; } /** * Validate an award draft the way `AttributionService.resolveException` will: * at least one award, distinct referrers, every fraction in (0, 1], and the * fractions summing to 1.0 (±{@link AWARD_FRACTION_TOLERANCE}). */ export declare function validateAwards(awards: AttributionAward[]): AwardValidation; /** Distinct candidate referrer ids, in first-seen order. */ export declare function uniqueCandidateReferrerIds(candidates: AttributionCandidateView[]): string[]; /** * Seed an equal split across `referrerIds`, rounded to 4 decimal places with * the LAST share adjusted so the set sums to exactly 1.0 (mirrors the * AttributionService split convention). */ export declare function equalSplitAwards(referrerIds: string[]): AttributionAward[]; /** `planKey@vN` display reference for snapshotted plan versions. */ export declare function formatPlanRef(planKey: string, planVersion: number): string; /** Inputs for the explainable `base × rate × share = amount` formula line. */ export interface CommissionFormulaParts { basis: CommissionBasis; baseAmountCents: number; rate: number; shareFraction: number; amountCents: number; currency: string; } /** * Render the snapshotted calculation as a one-line formula. Fixed-basis * components have no rate factor (`rate` is recorded as `0`), so the line * becomes `base (fixed) × share = amount`. */ export declare function formatCommissionFormula(parts: CommissionFormulaParts, locale?: string): string; /** One step of a payout status timeline. */ export interface PayoutTimelineStep { status: CommissionPayoutStatus; state: 'done' | 'current' | 'upcoming'; } /** * Linear settlement timeline for a payout batch. The happy path is * `pending → approved → processing → completed`; for a `failed` batch the * terminal marker replaces `completed`, and a `rejected` batch collapses to * the decline it actually took (`pending → rejected`). */ export declare function payoutStatusTimeline(status: CommissionPayoutStatus): PayoutTimelineStep[]; /** Which operator actions apply to a payout batch in a given status. */ export interface PayoutActions { canApprove: boolean; canMarkProcessing: boolean; canComplete: boolean; canFail: boolean; canReject: boolean; } /** * Action gating for {@link PayoutBatchReview}, mirroring the CommissionPayout * transition guard: `pending → approved → processing → completed | failed`, * with `failed` reachable from `approved`/`processing` and the terminal * decline `rejected` reachable from `pending`/`approved`. */ export declare function payoutActionsFor(status: CommissionPayoutStatus): PayoutActions; /** Per-currency totals of {@link CommissionExpenseRowView} rows. */ export interface CommissionExpenseTotals { currency: string; commissionExpenseCents: number; adjustmentCents: number; payoutCents: number; /** `expense + adjustments − payouts`: outstanding accrued liability. */ netAccruedCents: number; } /** * Sum reconciliation rows per currency (sorted by currency code). Currencies * are never summed together. */ export declare function sumExpenseRowsByCurrency(rows: CommissionExpenseRowView[]): CommissionExpenseTotals[]; //# sourceMappingURL=types.d.ts.map