@happyvertical/smrt-sales 0.39.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (201) hide show
  1. package/AGENTS.md +87 -0
  2. package/CLAUDE.md +1 -0
  3. package/LICENSE +7 -0
  4. package/dist/.tsbuildinfo +1 -0
  5. package/dist/__smrt-register__.d.ts +2 -0
  6. package/dist/__smrt-register__.d.ts.map +1 -0
  7. package/dist/chunks/__smrt-register__-kWdT7LOt.js +6 -0
  8. package/dist/chunks/__smrt-register__-kWdT7LOt.js.map +1 -0
  9. package/dist/chunks/commissions-BCxqOKVM.js +2181 -0
  10. package/dist/chunks/commissions-BCxqOKVM.js.map +1 -0
  11. package/dist/chunks/crm-DwEz7E2r.js +1428 -0
  12. package/dist/chunks/crm-DwEz7E2r.js.map +1 -0
  13. package/dist/chunks/referrals-BY47p70T.js +2880 -0
  14. package/dist/chunks/referrals-BY47p70T.js.map +1 -0
  15. package/dist/commissions/collections/CommissionAdjustmentCollection.d.ts +26 -0
  16. package/dist/commissions/collections/CommissionAdjustmentCollection.d.ts.map +1 -0
  17. package/dist/commissions/collections/CommissionCollection.d.ts +43 -0
  18. package/dist/commissions/collections/CommissionCollection.d.ts.map +1 -0
  19. package/dist/commissions/collections/CommissionPayoutCollection.d.ts +19 -0
  20. package/dist/commissions/collections/CommissionPayoutCollection.d.ts.map +1 -0
  21. package/dist/commissions/collections/CommissionPlanCollection.d.ts +46 -0
  22. package/dist/commissions/collections/CommissionPlanCollection.d.ts.map +1 -0
  23. package/dist/commissions/collections/EarnerCollection.d.ts +14 -0
  24. package/dist/commissions/collections/EarnerCollection.d.ts.map +1 -0
  25. package/dist/commissions/collections/EarningEventCollection.d.ts +27 -0
  26. package/dist/commissions/collections/EarningEventCollection.d.ts.map +1 -0
  27. package/dist/commissions/collections/index.d.ts +11 -0
  28. package/dist/commissions/collections/index.d.ts.map +1 -0
  29. package/dist/commissions/index.d.ts +20 -0
  30. package/dist/commissions/index.d.ts.map +1 -0
  31. package/dist/commissions/models/Commission.d.ts +150 -0
  32. package/dist/commissions/models/Commission.d.ts.map +1 -0
  33. package/dist/commissions/models/CommissionAdjustment.d.ts +62 -0
  34. package/dist/commissions/models/CommissionAdjustment.d.ts.map +1 -0
  35. package/dist/commissions/models/CommissionPayout.d.ts +119 -0
  36. package/dist/commissions/models/CommissionPayout.d.ts.map +1 -0
  37. package/dist/commissions/models/CommissionPlan.d.ts +117 -0
  38. package/dist/commissions/models/CommissionPlan.d.ts.map +1 -0
  39. package/dist/commissions/models/Earner.d.ts +49 -0
  40. package/dist/commissions/models/Earner.d.ts.map +1 -0
  41. package/dist/commissions/models/EarningEvent.d.ts +89 -0
  42. package/dist/commissions/models/EarningEvent.d.ts.map +1 -0
  43. package/dist/commissions/models/index.d.ts +11 -0
  44. package/dist/commissions/models/index.d.ts.map +1 -0
  45. package/dist/commissions/money.d.ts +42 -0
  46. package/dist/commissions/money.d.ts.map +1 -0
  47. package/dist/commissions/services/CommissionBalanceService.d.ts +14 -0
  48. package/dist/commissions/services/CommissionBalanceService.d.ts.map +1 -0
  49. package/dist/commissions/services/CommissionCalculationService.d.ts +142 -0
  50. package/dist/commissions/services/CommissionCalculationService.d.ts.map +1 -0
  51. package/dist/commissions/services/CommissionPayoutService.d.ts +134 -0
  52. package/dist/commissions/services/CommissionPayoutService.d.ts.map +1 -0
  53. package/dist/commissions/services/CommissionSettlementService.d.ts +42 -0
  54. package/dist/commissions/services/CommissionSettlementService.d.ts.map +1 -0
  55. package/dist/commissions/services/index.d.ts +9 -0
  56. package/dist/commissions/services/index.d.ts.map +1 -0
  57. package/dist/commissions/types.d.ts +243 -0
  58. package/dist/commissions/types.d.ts.map +1 -0
  59. package/dist/commissions.d.ts +2 -0
  60. package/dist/commissions.d.ts.map +1 -0
  61. package/dist/commissions.js +3 -0
  62. package/dist/crm/collections/LeadCollection.d.ts +79 -0
  63. package/dist/crm/collections/LeadCollection.d.ts.map +1 -0
  64. package/dist/crm/collections/OpportunityCollection.d.ts +42 -0
  65. package/dist/crm/collections/OpportunityCollection.d.ts.map +1 -0
  66. package/dist/crm/collections/OpportunityConversionCollection.d.ts +26 -0
  67. package/dist/crm/collections/OpportunityConversionCollection.d.ts.map +1 -0
  68. package/dist/crm/collections/PipelineDefinitionCollection.d.ts +46 -0
  69. package/dist/crm/collections/PipelineDefinitionCollection.d.ts.map +1 -0
  70. package/dist/crm/collections/PipelineStageCollection.d.ts +22 -0
  71. package/dist/crm/collections/PipelineStageCollection.d.ts.map +1 -0
  72. package/dist/crm/collections/SalesActivityCollection.d.ts +24 -0
  73. package/dist/crm/collections/SalesActivityCollection.d.ts.map +1 -0
  74. package/dist/crm/collections/SalesRepresentativeCollection.d.ts +19 -0
  75. package/dist/crm/collections/SalesRepresentativeCollection.d.ts.map +1 -0
  76. package/dist/crm/index.d.ts +27 -0
  77. package/dist/crm/index.d.ts.map +1 -0
  78. package/dist/crm/models/Lead.d.ts +135 -0
  79. package/dist/crm/models/Lead.d.ts.map +1 -0
  80. package/dist/crm/models/Opportunity.d.ts +111 -0
  81. package/dist/crm/models/Opportunity.d.ts.map +1 -0
  82. package/dist/crm/models/OpportunityConversion.d.ts +66 -0
  83. package/dist/crm/models/OpportunityConversion.d.ts.map +1 -0
  84. package/dist/crm/models/PipelineDefinition.d.ts +58 -0
  85. package/dist/crm/models/PipelineDefinition.d.ts.map +1 -0
  86. package/dist/crm/models/PipelineStage.d.ts +66 -0
  87. package/dist/crm/models/PipelineStage.d.ts.map +1 -0
  88. package/dist/crm/models/SalesActivity.d.ts +76 -0
  89. package/dist/crm/models/SalesActivity.d.ts.map +1 -0
  90. package/dist/crm/models/SalesRepresentative.d.ts +60 -0
  91. package/dist/crm/models/SalesRepresentative.d.ts.map +1 -0
  92. package/dist/crm/types.d.ts +228 -0
  93. package/dist/crm/types.d.ts.map +1 -0
  94. package/dist/crm.d.ts +2 -0
  95. package/dist/crm.d.ts.map +1 -0
  96. package/dist/crm.js +3 -0
  97. package/dist/index.d.ts +4 -0
  98. package/dist/index.d.ts.map +1 -0
  99. package/dist/index.js +5 -0
  100. package/dist/manifest.json +9340 -0
  101. package/dist/referrals/collections/AttributionExceptionCollection.d.ts +17 -0
  102. package/dist/referrals/collections/AttributionExceptionCollection.d.ts.map +1 -0
  103. package/dist/referrals/collections/AttributionPolicyCollection.d.ts +50 -0
  104. package/dist/referrals/collections/AttributionPolicyCollection.d.ts.map +1 -0
  105. package/dist/referrals/collections/ReferralAgreementCollection.d.ts +48 -0
  106. package/dist/referrals/collections/ReferralAgreementCollection.d.ts.map +1 -0
  107. package/dist/referrals/collections/ReferralCollection.d.ts +25 -0
  108. package/dist/referrals/collections/ReferralCollection.d.ts.map +1 -0
  109. package/dist/referrals/collections/ReferralLinkCollection.d.ts +96 -0
  110. package/dist/referrals/collections/ReferralLinkCollection.d.ts.map +1 -0
  111. package/dist/referrals/collections/ReferralProgramCollection.d.ts +18 -0
  112. package/dist/referrals/collections/ReferralProgramCollection.d.ts.map +1 -0
  113. package/dist/referrals/collections/ReferralTermSnapshotCollection.d.ts +13 -0
  114. package/dist/referrals/collections/ReferralTermSnapshotCollection.d.ts.map +1 -0
  115. package/dist/referrals/collections/ReferralTouchCollection.d.ts +30 -0
  116. package/dist/referrals/collections/ReferralTouchCollection.d.ts.map +1 -0
  117. package/dist/referrals/collections/ReferrerCollection.d.ts +17 -0
  118. package/dist/referrals/collections/ReferrerCollection.d.ts.map +1 -0
  119. package/dist/referrals/index.d.ts +38 -0
  120. package/dist/referrals/index.d.ts.map +1 -0
  121. package/dist/referrals/models/AttributionException.d.ts +85 -0
  122. package/dist/referrals/models/AttributionException.d.ts.map +1 -0
  123. package/dist/referrals/models/AttributionPolicy.d.ts +128 -0
  124. package/dist/referrals/models/AttributionPolicy.d.ts.map +1 -0
  125. package/dist/referrals/models/Referral.d.ts +112 -0
  126. package/dist/referrals/models/Referral.d.ts.map +1 -0
  127. package/dist/referrals/models/ReferralAgreement.d.ts +125 -0
  128. package/dist/referrals/models/ReferralAgreement.d.ts.map +1 -0
  129. package/dist/referrals/models/ReferralLink.d.ts +71 -0
  130. package/dist/referrals/models/ReferralLink.d.ts.map +1 -0
  131. package/dist/referrals/models/ReferralProgram.d.ts +57 -0
  132. package/dist/referrals/models/ReferralProgram.d.ts.map +1 -0
  133. package/dist/referrals/models/ReferralTermSnapshot.d.ts +61 -0
  134. package/dist/referrals/models/ReferralTermSnapshot.d.ts.map +1 -0
  135. package/dist/referrals/models/ReferralTouch.d.ts +55 -0
  136. package/dist/referrals/models/ReferralTouch.d.ts.map +1 -0
  137. package/dist/referrals/models/Referrer.d.ts +69 -0
  138. package/dist/referrals/models/Referrer.d.ts.map +1 -0
  139. package/dist/referrals/services/AttributionService.d.ts +248 -0
  140. package/dist/referrals/services/AttributionService.d.ts.map +1 -0
  141. package/dist/referrals/services/ReferralCommissionService.d.ts +91 -0
  142. package/dist/referrals/services/ReferralCommissionService.d.ts.map +1 -0
  143. package/dist/referrals/services/ReferralQualificationService.d.ts +135 -0
  144. package/dist/referrals/services/ReferralQualificationService.d.ts.map +1 -0
  145. package/dist/referrals/types.d.ts +226 -0
  146. package/dist/referrals/types.d.ts.map +1 -0
  147. package/dist/referrals.d.ts +2 -0
  148. package/dist/referrals.d.ts.map +1 -0
  149. package/dist/referrals.js +3 -0
  150. package/dist/smrt-knowledge.json +5501 -0
  151. package/dist/svelte/__tests__/format.test.js +68 -0
  152. package/dist/svelte/__tests__/types.test.js +349 -0
  153. package/dist/svelte/components/AttributionConflictQueue.svelte +361 -0
  154. package/dist/svelte/components/AttributionConflictQueue.svelte.d.ts +15 -0
  155. package/dist/svelte/components/AttributionConflictQueue.svelte.d.ts.map +1 -0
  156. package/dist/svelte/components/CommissionBreakdown.svelte +251 -0
  157. package/dist/svelte/components/CommissionBreakdown.svelte.d.ts +11 -0
  158. package/dist/svelte/components/CommissionBreakdown.svelte.d.ts.map +1 -0
  159. package/dist/svelte/components/CommissionExpenseSummary.svelte +186 -0
  160. package/dist/svelte/components/CommissionExpenseSummary.svelte.d.ts +13 -0
  161. package/dist/svelte/components/CommissionExpenseSummary.svelte.d.ts.map +1 -0
  162. package/dist/svelte/components/ExecutedAgreementsList.svelte +136 -0
  163. package/dist/svelte/components/ExecutedAgreementsList.svelte.d.ts +11 -0
  164. package/dist/svelte/components/ExecutedAgreementsList.svelte.d.ts.map +1 -0
  165. package/dist/svelte/components/LeadList.svelte +224 -0
  166. package/dist/svelte/components/LeadList.svelte.d.ts +19 -0
  167. package/dist/svelte/components/LeadList.svelte.d.ts.map +1 -0
  168. package/dist/svelte/components/OpportunityBoard.svelte +230 -0
  169. package/dist/svelte/components/OpportunityBoard.svelte.d.ts +17 -0
  170. package/dist/svelte/components/OpportunityBoard.svelte.d.ts.map +1 -0
  171. package/dist/svelte/components/OpportunityDetail.svelte +472 -0
  172. package/dist/svelte/components/OpportunityDetail.svelte.d.ts +27 -0
  173. package/dist/svelte/components/OpportunityDetail.svelte.d.ts.map +1 -0
  174. package/dist/svelte/components/PayoutBatchReview.svelte +285 -0
  175. package/dist/svelte/components/PayoutBatchReview.svelte.d.ts +21 -0
  176. package/dist/svelte/components/PayoutBatchReview.svelte.d.ts.map +1 -0
  177. package/dist/svelte/components/PayoutHistoryList.svelte +245 -0
  178. package/dist/svelte/components/PayoutHistoryList.svelte.d.ts +11 -0
  179. package/dist/svelte/components/PayoutHistoryList.svelte.d.ts.map +1 -0
  180. package/dist/svelte/components/ReferralLinkManager.svelte +216 -0
  181. package/dist/svelte/components/ReferralLinkManager.svelte.d.ts +17 -0
  182. package/dist/svelte/components/ReferralLinkManager.svelte.d.ts.map +1 -0
  183. package/dist/svelte/components/ReferralStatusList.svelte +118 -0
  184. package/dist/svelte/components/ReferralStatusList.svelte.d.ts +11 -0
  185. package/dist/svelte/components/ReferralStatusList.svelte.d.ts.map +1 -0
  186. package/dist/svelte/components/ReferrerEarningsSummary.svelte +106 -0
  187. package/dist/svelte/components/ReferrerEarningsSummary.svelte.d.ts +11 -0
  188. package/dist/svelte/components/ReferrerEarningsSummary.svelte.d.ts.map +1 -0
  189. package/dist/svelte/components/SalesDashboard.svelte +192 -0
  190. package/dist/svelte/components/SalesDashboard.svelte.d.ts +29 -0
  191. package/dist/svelte/components/SalesDashboard.svelte.d.ts.map +1 -0
  192. package/dist/svelte/format.d.ts +46 -0
  193. package/dist/svelte/format.d.ts.map +1 -0
  194. package/dist/svelte/format.js +75 -0
  195. package/dist/svelte/index.d.ts +50 -0
  196. package/dist/svelte/index.d.ts.map +1 -0
  197. package/dist/svelte/index.js +37 -0
  198. package/dist/svelte/types.d.ts +422 -0
  199. package/dist/svelte/types.d.ts.map +1 -0
  200. package/dist/svelte/types.js +376 -0
  201. package/package.json +89 -0
@@ -0,0 +1,216 @@
1
+ <script lang="ts">
2
+ /**
3
+ * ReferralLinkManager — referrer-portal share link manager (#1931).
4
+ *
5
+ * Lists a referrer's share links (label, code, share URL, clicks, status) and
6
+ * offers a create form plus per-link enable/disable. The share URL is built
7
+ * from the host-provided `shareBaseUrl`; creation and status changes are
8
+ * delegated to callbacks.
9
+ */
10
+ import { Form, FormGroup, Input } from '@happyvertical/smrt-ui/forms';
11
+ import { Badge, Button } from '@happyvertical/smrt-ui/ui';
12
+ import type { CreateReferralLinkDraft, ReferralLinkView } from '../types.js';
13
+ import {
14
+ buildShareUrl,
15
+ isHttpUrl,
16
+ referralLinkStatusBadgeVariant,
17
+ } from '../types.js';
18
+
19
+ export interface Props {
20
+ /** The referrer's links. */
21
+ links?: ReferralLinkView[];
22
+ /** Base URL share links are served from (e.g. `https://example.com/r`). */
23
+ shareBaseUrl?: string;
24
+ /** Disable actions while a mutation is in flight. */
25
+ busy?: boolean;
26
+ /** Create a new share link. */
27
+ onCreate?: (draft: CreateReferralLinkDraft) => void;
28
+ /** Enable or disable an existing link. */
29
+ onSetEnabled?: (linkId: string, enabled: boolean) => void;
30
+ }
31
+
32
+ let {
33
+ links = [],
34
+ shareBaseUrl = '',
35
+ busy = false,
36
+ onCreate,
37
+ onSetEnabled,
38
+ }: Props = $props();
39
+
40
+ let draftTargetUrl = $state('');
41
+ let draftLabel = $state('');
42
+
43
+ const canCreate = $derived(isHttpUrl(draftTargetUrl));
44
+
45
+ function submitCreate() {
46
+ if (!canCreate) return;
47
+ const label = draftLabel.trim();
48
+ onCreate?.({
49
+ targetUrl: draftTargetUrl.trim(),
50
+ label: label === '' ? undefined : label,
51
+ });
52
+ draftTargetUrl = '';
53
+ draftLabel = '';
54
+ }
55
+ </script>
56
+
57
+ <div class="sales-referral-links">
58
+ {#if onCreate}
59
+ <Form class="sales-referral-links-create" onsubmit={submitCreate}>
60
+ <FormGroup
61
+ label="Destination URL"
62
+ required
63
+ hint="Where the share link should send visitors (must be http/https)."
64
+ >
65
+ <Input
66
+ type="url"
67
+ value={draftTargetUrl}
68
+ disabled={busy}
69
+ required
70
+ oninput={(event) => {
71
+ draftTargetUrl = (event.currentTarget as HTMLInputElement).value;
72
+ }}
73
+ />
74
+ </FormGroup>
75
+ <FormGroup label="Label" hint="Optional name for this link.">
76
+ <Input
77
+ value={draftLabel}
78
+ disabled={busy}
79
+ oninput={(event) => {
80
+ draftLabel = (event.currentTarget as HTMLInputElement).value;
81
+ }}
82
+ />
83
+ </FormGroup>
84
+ <Button type="submit" variant="primary" size="sm" disabled={busy || !canCreate}>
85
+ Create link
86
+ </Button>
87
+ </Form>
88
+ {/if}
89
+
90
+ {#if links.length === 0}
91
+ <p class="empty">No share links yet.</p>
92
+ {:else}
93
+ <table>
94
+ <thead>
95
+ <tr>
96
+ <th scope="col">Label</th>
97
+ <th scope="col">Code</th>
98
+ <th scope="col">Share URL</th>
99
+ <th scope="col">Clicks</th>
100
+ <th scope="col">Status</th>
101
+ <th scope="col"><span class="visually-hidden">Actions</span></th>
102
+ </tr>
103
+ </thead>
104
+ <tbody>
105
+ {#each links as link (link.id)}
106
+ <tr>
107
+ <td>{link.label ?? '—'}</td>
108
+ <td><code>{link.code}</code></td>
109
+ <td class="share-url">
110
+ {#if shareBaseUrl}
111
+ <code>{buildShareUrl(shareBaseUrl, link.code)}</code>
112
+ {:else}
113
+
114
+ {/if}
115
+ </td>
116
+ <td class="clicks">{link.clickCount}</td>
117
+ <td>
118
+ <Badge variant={referralLinkStatusBadgeVariant(link.status)} size="sm">
119
+ {link.status}
120
+ </Badge>
121
+ </td>
122
+ <td class="actions">
123
+ {#if onSetEnabled}
124
+ <Button
125
+ variant="secondary"
126
+ size="sm"
127
+ disabled={busy}
128
+ onclick={() => onSetEnabled?.(link.id, link.status !== 'active')}
129
+ >
130
+ {link.status === 'active' ? 'Disable' : 'Enable'}
131
+ </Button>
132
+ {/if}
133
+ </td>
134
+ </tr>
135
+ {/each}
136
+ </tbody>
137
+ </table>
138
+ {/if}
139
+ </div>
140
+
141
+ <style>
142
+ .sales-referral-links {
143
+ display: flex;
144
+ flex-direction: column;
145
+ gap: var(--smrt-spacing-4, 1rem);
146
+ width: 100%;
147
+ overflow-x: auto;
148
+ }
149
+
150
+ .sales-referral-links :global(.sales-referral-links-create) {
151
+ display: flex;
152
+ flex-wrap: wrap;
153
+ align-items: flex-end;
154
+ gap: var(--smrt-spacing-2, 0.5rem);
155
+ }
156
+
157
+ .sales-referral-links :global(.sales-referral-links-create .form-group) {
158
+ margin-bottom: 0;
159
+ flex: 1 1 14rem;
160
+ }
161
+
162
+ .empty {
163
+ margin: 0;
164
+ color: var(--smrt-color-on-surface-variant, #64748b);
165
+ font-style: italic;
166
+ }
167
+
168
+ table {
169
+ width: 100%;
170
+ border-collapse: collapse;
171
+ font-size: var(--smrt-typography-body-medium-size, 0.875rem);
172
+ }
173
+
174
+ th {
175
+ padding: var(--smrt-spacing-2, 0.5rem) var(--smrt-spacing-3, 0.75rem);
176
+ text-align: left;
177
+ font-weight: var(--smrt-typography-weight-semibold, 600);
178
+ background: var(--smrt-color-surface-container, #f3f4f6);
179
+ border-bottom: 1px solid var(--smrt-color-outline-variant, #d8dde6);
180
+ white-space: nowrap;
181
+ }
182
+
183
+ td {
184
+ padding: var(--smrt-spacing-2, 0.5rem) var(--smrt-spacing-3, 0.75rem);
185
+ border-bottom: 1px solid var(--smrt-color-outline-variant, #d8dde6);
186
+ vertical-align: middle;
187
+ }
188
+
189
+ code {
190
+ font-family: var(--smrt-font-family-mono, ui-monospace, monospace);
191
+ font-size: var(--smrt-typography-body-small-size, 0.8125rem);
192
+ }
193
+
194
+ .share-url {
195
+ word-break: break-all;
196
+ }
197
+
198
+ .clicks {
199
+ text-align: right;
200
+ font-family: var(--smrt-font-family-mono, ui-monospace, monospace);
201
+ }
202
+
203
+ .actions {
204
+ white-space: nowrap;
205
+ text-align: right;
206
+ }
207
+
208
+ .visually-hidden {
209
+ position: absolute;
210
+ width: 1px;
211
+ height: 1px;
212
+ overflow: hidden;
213
+ clip: rect(0 0 0 0);
214
+ white-space: nowrap;
215
+ }
216
+ </style>
@@ -0,0 +1,17 @@
1
+ import type { CreateReferralLinkDraft, ReferralLinkView } from '../types.js';
2
+ export interface Props {
3
+ /** The referrer's links. */
4
+ links?: ReferralLinkView[];
5
+ /** Base URL share links are served from (e.g. `https://example.com/r`). */
6
+ shareBaseUrl?: string;
7
+ /** Disable actions while a mutation is in flight. */
8
+ busy?: boolean;
9
+ /** Create a new share link. */
10
+ onCreate?: (draft: CreateReferralLinkDraft) => void;
11
+ /** Enable or disable an existing link. */
12
+ onSetEnabled?: (linkId: string, enabled: boolean) => void;
13
+ }
14
+ declare const ReferralLinkManager: import("svelte").Component<Props, {}, "">;
15
+ type ReferralLinkManager = ReturnType<typeof ReferralLinkManager>;
16
+ export default ReferralLinkManager;
17
+ //# sourceMappingURL=ReferralLinkManager.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ReferralLinkManager.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/ReferralLinkManager.svelte.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EAAE,uBAAuB,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAQ7E,MAAM,WAAW,KAAK;IACpB,4BAA4B;IAC5B,KAAK,CAAC,EAAE,gBAAgB,EAAE,CAAC;IAC3B,2EAA2E;IAC3E,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,qDAAqD;IACrD,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,+BAA+B;IAC/B,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,uBAAuB,KAAK,IAAI,CAAC;IACpD,0CAA0C;IAC1C,YAAY,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,KAAK,IAAI,CAAC;CAC3D;AAqGD,QAAA,MAAM,mBAAmB,2CAAwC,CAAC;AAClE,KAAK,mBAAmB,GAAG,UAAU,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAClE,eAAe,mBAAmB,CAAC"}
@@ -0,0 +1,118 @@
1
+ <script lang="ts">
2
+ /**
3
+ * ReferralStatusList — referrer-portal referral status view (#1931).
4
+ *
5
+ * Read-only table of a referrer's introductions: lifecycle status badge,
6
+ * qualifying target kind, credit fraction (with a split indicator when the
7
+ * referral shares a `splitGroupId`), and the attribution/qualification dates.
8
+ */
9
+ import { Badge } from '@happyvertical/smrt-ui/ui';
10
+ import { formatDate, formatPercent } from '../format.js';
11
+ import type { ReferralStatusView } from '../types.js';
12
+ import { referralStatusBadgeVariant } from '../types.js';
13
+
14
+ export interface Props {
15
+ /** The referrer's referrals, newest first by convention. */
16
+ referrals?: ReferralStatusView[];
17
+ /** BCP 47 locale for date/percent formatting. */
18
+ locale?: string;
19
+ }
20
+
21
+ let { referrals = [], locale }: Props = $props();
22
+ </script>
23
+
24
+ <div class="sales-referral-status">
25
+ {#if referrals.length === 0}
26
+ <p class="empty">No referrals yet.</p>
27
+ {:else}
28
+ <table>
29
+ <thead>
30
+ <tr>
31
+ <th scope="col">Status</th>
32
+ <th scope="col">Target</th>
33
+ <th scope="col">Credit</th>
34
+ <th scope="col">Program</th>
35
+ <th scope="col">Attributed</th>
36
+ <th scope="col">Qualified</th>
37
+ <th scope="col">Expires</th>
38
+ </tr>
39
+ </thead>
40
+ <tbody>
41
+ {#each referrals as referral (referral.id)}
42
+ <tr>
43
+ <td>
44
+ <Badge variant={referralStatusBadgeVariant(referral.status)} size="sm">
45
+ {referral.status.replace('_', ' ')}
46
+ </Badge>
47
+ </td>
48
+ <td>
49
+ <span class="target-kind">{referral.targetKind}</span>
50
+ {#if referral.targetLabel}
51
+ <span class="secondary">{referral.targetLabel}</span>
52
+ {/if}
53
+ </td>
54
+ <td class="credit">
55
+ {formatPercent(referral.creditFraction, locale)}
56
+ {#if referral.splitGroupId}
57
+ <Badge variant="info" size="sm">split</Badge>
58
+ {/if}
59
+ </td>
60
+ <td>{referral.programName ?? '—'}</td>
61
+ <td>{formatDate(referral.attributedAt, locale)}</td>
62
+ <td>{formatDate(referral.qualifiedAt, locale)}</td>
63
+ <td>{formatDate(referral.expiresAt, locale)}</td>
64
+ </tr>
65
+ {/each}
66
+ </tbody>
67
+ </table>
68
+ {/if}
69
+ </div>
70
+
71
+ <style>
72
+ .sales-referral-status {
73
+ width: 100%;
74
+ overflow-x: auto;
75
+ }
76
+
77
+ .empty {
78
+ margin: 0;
79
+ color: var(--smrt-color-on-surface-variant, #64748b);
80
+ font-style: italic;
81
+ }
82
+
83
+ table {
84
+ width: 100%;
85
+ border-collapse: collapse;
86
+ font-size: var(--smrt-typography-body-medium-size, 0.875rem);
87
+ }
88
+
89
+ th {
90
+ padding: var(--smrt-spacing-2, 0.5rem) var(--smrt-spacing-3, 0.75rem);
91
+ text-align: left;
92
+ font-weight: var(--smrt-typography-weight-semibold, 600);
93
+ background: var(--smrt-color-surface-container, #f3f4f6);
94
+ border-bottom: 1px solid var(--smrt-color-outline-variant, #d8dde6);
95
+ white-space: nowrap;
96
+ }
97
+
98
+ td {
99
+ padding: var(--smrt-spacing-2, 0.5rem) var(--smrt-spacing-3, 0.75rem);
100
+ border-bottom: 1px solid var(--smrt-color-outline-variant, #d8dde6);
101
+ vertical-align: top;
102
+ }
103
+
104
+ .target-kind {
105
+ display: block;
106
+ font-weight: var(--smrt-typography-weight-medium, 500);
107
+ }
108
+
109
+ .secondary {
110
+ display: block;
111
+ color: var(--smrt-color-on-surface-variant, #64748b);
112
+ font-size: var(--smrt-typography-body-small-size, 0.75rem);
113
+ }
114
+
115
+ .credit {
116
+ white-space: nowrap;
117
+ }
118
+ </style>
@@ -0,0 +1,11 @@
1
+ import type { ReferralStatusView } from '../types.js';
2
+ export interface Props {
3
+ /** The referrer's referrals, newest first by convention. */
4
+ referrals?: ReferralStatusView[];
5
+ /** BCP 47 locale for date/percent formatting. */
6
+ locale?: string;
7
+ }
8
+ declare const ReferralStatusList: import("svelte").Component<Props, {}, "">;
9
+ type ReferralStatusList = ReturnType<typeof ReferralStatusList>;
10
+ export default ReferralStatusList;
11
+ //# sourceMappingURL=ReferralStatusList.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ReferralStatusList.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/ReferralStatusList.svelte.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAItD,MAAM,WAAW,KAAK;IACpB,4DAA4D;IAC5D,SAAS,CAAC,EAAE,kBAAkB,EAAE,CAAC;IACjC,iDAAiD;IACjD,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AA+DD,QAAA,MAAM,kBAAkB,2CAAwC,CAAC;AACjE,KAAK,kBAAkB,GAAG,UAAU,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAChE,eAAe,kBAAkB,CAAC"}
@@ -0,0 +1,106 @@
1
+ <script lang="ts">
2
+ /**
3
+ * ReferrerEarningsSummary — referrer-portal balance tiles (#1933).
4
+ *
5
+ * Renders an `EarnerBalance`-shaped prop (the computed, never-stored balance
6
+ * from `CommissionBalanceService`) as tiles: pending, earned, approved,
7
+ * payable, unsettled adjustments, and net payable. Negative-safe — clawbacks
8
+ * can drive adjustments and even net payable below zero.
9
+ */
10
+ import { formatCents } from '../format.js';
11
+ import type { EarnerBalance } from '../types.js';
12
+
13
+ export interface Props {
14
+ /** Computed balance for one earner+currency, or `null` before load. */
15
+ balance?: EarnerBalance | null;
16
+ /** BCP 47 locale for money formatting. */
17
+ locale?: string;
18
+ }
19
+
20
+ let { balance = null, locale }: Props = $props();
21
+
22
+ interface Tile {
23
+ label: string;
24
+ amountCents: number;
25
+ emphasis?: boolean;
26
+ }
27
+
28
+ const tiles = $derived.by((): Tile[] => {
29
+ if (!balance) return [];
30
+ return [
31
+ { label: 'Pending (clearing)', amountCents: balance.pendingCents },
32
+ { label: 'Earned', amountCents: balance.earnedCents },
33
+ { label: 'Approved', amountCents: balance.approvedCents },
34
+ { label: 'Payable', amountCents: balance.payableCents },
35
+ {
36
+ label: 'Unsettled adjustments',
37
+ amountCents: balance.unsettledAdjustmentCents,
38
+ },
39
+ {
40
+ label: 'Net payable',
41
+ amountCents: balance.netPayableCents,
42
+ emphasis: true,
43
+ },
44
+ ];
45
+ });
46
+ </script>
47
+
48
+ <div class="sales-earnings-summary">
49
+ {#if !balance}
50
+ <p class="empty">No earnings yet.</p>
51
+ {:else}
52
+ <dl class="tiles">
53
+ {#each tiles as tile (tile.label)}
54
+ <div class="tile" class:tile--emphasis={tile.emphasis}>
55
+ <dt>{tile.label}</dt>
56
+ <dd class:negative={tile.amountCents < 0}>
57
+ {formatCents(tile.amountCents, balance.currency, locale)}
58
+ </dd>
59
+ </div>
60
+ {/each}
61
+ </dl>
62
+ {/if}
63
+ </div>
64
+
65
+ <style>
66
+ .empty {
67
+ margin: 0;
68
+ color: var(--smrt-color-on-surface-variant, #64748b);
69
+ font-style: italic;
70
+ }
71
+
72
+ .tiles {
73
+ display: grid;
74
+ grid-template-columns: repeat(auto-fit, minmax(10rem, 1fr));
75
+ gap: var(--smrt-spacing-3, 0.75rem);
76
+ margin: 0;
77
+ }
78
+
79
+ .tile {
80
+ background: var(--smrt-color-surface, #fff);
81
+ border: 1px solid var(--smrt-color-outline-variant, #d8dde6);
82
+ border-radius: var(--smrt-radius-md, 8px);
83
+ padding: var(--smrt-spacing-3, 0.75rem);
84
+ }
85
+
86
+ .tile--emphasis {
87
+ border-color: var(--smrt-color-primary, #2563eb);
88
+ }
89
+
90
+ .tile dt {
91
+ color: var(--smrt-color-on-surface-variant, #64748b);
92
+ font-size: var(--smrt-typography-label-medium-size, 0.75rem);
93
+ text-transform: uppercase;
94
+ letter-spacing: 0.04em;
95
+ }
96
+
97
+ .tile dd {
98
+ margin: var(--smrt-spacing-1, 0.25rem) 0 0;
99
+ font-size: var(--smrt-typography-title-medium-size, 1rem);
100
+ font-weight: var(--smrt-typography-weight-bold, 700);
101
+ }
102
+
103
+ .tile dd.negative {
104
+ color: var(--smrt-color-error, #dc2626);
105
+ }
106
+ </style>
@@ -0,0 +1,11 @@
1
+ import type { EarnerBalance } from '../types.js';
2
+ export interface Props {
3
+ /** Computed balance for one earner+currency, or `null` before load. */
4
+ balance?: EarnerBalance | null;
5
+ /** BCP 47 locale for money formatting. */
6
+ locale?: string;
7
+ }
8
+ declare const ReferrerEarningsSummary: import("svelte").Component<Props, {}, "">;
9
+ type ReferrerEarningsSummary = ReturnType<typeof ReferrerEarningsSummary>;
10
+ export default ReferrerEarningsSummary;
11
+ //# sourceMappingURL=ReferrerEarningsSummary.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ReferrerEarningsSummary.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/ReferrerEarningsSummary.svelte.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAGjD,MAAM,WAAW,KAAK;IACpB,uEAAuE;IACvE,OAAO,CAAC,EAAE,aAAa,GAAG,IAAI,CAAC;IAC/B,0CAA0C;IAC1C,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAyDD,QAAA,MAAM,uBAAuB,2CAAwC,CAAC;AACtE,KAAK,uBAAuB,GAAG,UAAU,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAC1E,eAAe,uBAAuB,CAAC"}
@@ -0,0 +1,192 @@
1
+ <script lang="ts">
2
+ /**
3
+ * SalesDashboard — CRM composition surface (#1924).
4
+ *
5
+ * Summary tiles (open opportunity count, open pipeline value per stage, win
6
+ * rate) computed from props via the exported pure helpers in `types.ts`, plus
7
+ * snippet props to embed the board and list surfaces underneath. Multi-currency
8
+ * pipelines render one total line per currency — amounts are never summed
9
+ * across currencies.
10
+ */
11
+ import type { Snippet } from 'svelte';
12
+ import { formatCents, formatPercent } from '../format.js';
13
+ import type { OpportunityCardView, PipelineStageView } from '../types.js';
14
+ import {
15
+ openOpportunityCount,
16
+ openPipelineTotals,
17
+ pipelineValueByStage,
18
+ winRate,
19
+ } from '../types.js';
20
+
21
+ export interface Props {
22
+ /** All opportunities in scope (open and closed — closed feed the win rate). */
23
+ opportunities?: OpportunityCardView[];
24
+ /** Pipeline stages, in order (for the per-stage value breakdown). */
25
+ stages?: PipelineStageView[];
26
+ /** BCP 47 locale for money/percent formatting. */
27
+ locale?: string;
28
+ /** Embedded pipeline board (e.g. an `OpportunityBoard`). */
29
+ board?: Snippet;
30
+ /** Embedded lead/opportunity list (e.g. a `LeadList`). */
31
+ list?: Snippet;
32
+ /** Additional embedded content rendered last. */
33
+ children?: Snippet;
34
+ }
35
+
36
+ let {
37
+ opportunities = [],
38
+ stages = [],
39
+ locale,
40
+ board,
41
+ list,
42
+ children,
43
+ }: Props = $props();
44
+
45
+ const openCount = $derived(openOpportunityCount(opportunities));
46
+ const totals = $derived(openPipelineTotals(opportunities));
47
+ const byStage = $derived(pipelineValueByStage(stages, opportunities));
48
+ const rate = $derived(winRate(opportunities));
49
+ </script>
50
+
51
+ <div class="sales-dashboard">
52
+ <dl class="tiles">
53
+ <div class="tile">
54
+ <dt>Open opportunities</dt>
55
+ <dd>{openCount}</dd>
56
+ </div>
57
+ <div class="tile">
58
+ <dt>Open pipeline value</dt>
59
+ <dd>
60
+ {#if totals.length === 0}
61
+
62
+ {:else}
63
+ {#each totals as total (total.currency)}
64
+ <span class="tile__line">
65
+ {formatCents(total.amountCents, total.currency, locale)}
66
+ </span>
67
+ {/each}
68
+ {/if}
69
+ </dd>
70
+ </div>
71
+ <div class="tile">
72
+ <dt>Win rate</dt>
73
+ <dd>{rate === null ? '—' : formatPercent(rate, locale)}</dd>
74
+ </div>
75
+ </dl>
76
+
77
+ {#if byStage.length > 0}
78
+ <section class="stages" aria-label="Pipeline value by stage">
79
+ <h3>Pipeline by stage</h3>
80
+ <ul class="stages__list">
81
+ {#each byStage as stage (stage.stageId)}
82
+ <li class="stages__item">
83
+ <span class="stages__name">{stage.stageName}</span>
84
+ <span class="stages__count">{stage.openCount} open</span>
85
+ <span class="stages__value">
86
+ {#if stage.totals.length === 0}
87
+
88
+ {:else}
89
+ {#each stage.totals as total, index (total.currency)}
90
+ {index > 0 ? ' · ' : ''}{formatCents(total.amountCents, total.currency, locale)}
91
+ {/each}
92
+ {/if}
93
+ </span>
94
+ </li>
95
+ {/each}
96
+ </ul>
97
+ </section>
98
+ {/if}
99
+
100
+ {#if board}
101
+ <section aria-label="Pipeline board">
102
+ {@render board()}
103
+ </section>
104
+ {/if}
105
+
106
+ {#if list}
107
+ <section aria-label="Lead list">
108
+ {@render list()}
109
+ </section>
110
+ {/if}
111
+
112
+ {@render children?.()}
113
+ </div>
114
+
115
+ <style>
116
+ .sales-dashboard {
117
+ display: flex;
118
+ flex-direction: column;
119
+ gap: var(--smrt-spacing-4, 1rem);
120
+ }
121
+
122
+ .tiles {
123
+ display: grid;
124
+ grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
125
+ gap: var(--smrt-spacing-3, 0.75rem);
126
+ margin: 0;
127
+ }
128
+
129
+ .tile {
130
+ background: var(--smrt-color-surface, #fff);
131
+ border: 1px solid var(--smrt-color-outline-variant, #d8dde6);
132
+ border-radius: var(--smrt-radius-md, 8px);
133
+ padding: var(--smrt-spacing-3, 0.75rem);
134
+ }
135
+
136
+ .tile dt {
137
+ color: var(--smrt-color-on-surface-variant, #64748b);
138
+ font-size: var(--smrt-typography-label-medium-size, 0.75rem);
139
+ text-transform: uppercase;
140
+ letter-spacing: 0.04em;
141
+ }
142
+
143
+ .tile dd {
144
+ margin: var(--smrt-spacing-1, 0.25rem) 0 0;
145
+ font-size: var(--smrt-typography-title-large-size, 1.25rem);
146
+ font-weight: var(--smrt-typography-weight-bold, 700);
147
+ }
148
+
149
+ .tile__line {
150
+ display: block;
151
+ }
152
+
153
+ h3 {
154
+ margin: 0 0 var(--smrt-spacing-2, 0.5rem);
155
+ font-size: var(--smrt-typography-title-small-size, 0.875rem);
156
+ text-transform: uppercase;
157
+ letter-spacing: 0.04em;
158
+ color: var(--smrt-color-on-surface-variant, #64748b);
159
+ }
160
+
161
+ .stages__list {
162
+ margin: 0;
163
+ padding: 0;
164
+ list-style: none;
165
+ display: flex;
166
+ flex-direction: column;
167
+ gap: var(--smrt-spacing-1, 0.25rem);
168
+ }
169
+
170
+ .stages__item {
171
+ display: flex;
172
+ align-items: baseline;
173
+ gap: var(--smrt-spacing-2, 0.5rem);
174
+ padding: var(--smrt-spacing-1, 0.25rem) 0;
175
+ border-bottom: 1px solid var(--smrt-color-outline-variant, #d8dde6);
176
+ font-size: var(--smrt-typography-body-medium-size, 0.875rem);
177
+ }
178
+
179
+ .stages__name {
180
+ flex: 1 1 auto;
181
+ font-weight: var(--smrt-typography-weight-medium, 500);
182
+ }
183
+
184
+ .stages__count {
185
+ color: var(--smrt-color-on-surface-variant, #64748b);
186
+ font-size: var(--smrt-typography-body-small-size, 0.75rem);
187
+ }
188
+
189
+ .stages__value {
190
+ font-weight: var(--smrt-typography-weight-semibold, 600);
191
+ }
192
+ </style>