@happyvertical/smrt-commerce 0.30.0

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 (101) hide show
  1. package/AGENTS.md +44 -0
  2. package/CLAUDE.md +1 -0
  3. package/LICENSE +7 -0
  4. package/README.md +146 -0
  5. package/dist/__smrt-register__.d.ts +2 -0
  6. package/dist/__smrt-register__.d.ts.map +1 -0
  7. package/dist/collections/ContractCollection.d.ts +87 -0
  8. package/dist/collections/ContractCollection.d.ts.map +1 -0
  9. package/dist/collections/CustomerCollection.d.ts +58 -0
  10. package/dist/collections/CustomerCollection.d.ts.map +1 -0
  11. package/dist/collections/FulfillmentCollection.d.ts +75 -0
  12. package/dist/collections/FulfillmentCollection.d.ts.map +1 -0
  13. package/dist/collections/InvoiceCollection.d.ts +162 -0
  14. package/dist/collections/InvoiceCollection.d.ts.map +1 -0
  15. package/dist/collections/InvoiceLineItemCollection.d.ts +90 -0
  16. package/dist/collections/InvoiceLineItemCollection.d.ts.map +1 -0
  17. package/dist/collections/PaymentAllocationCollection.d.ts +86 -0
  18. package/dist/collections/PaymentAllocationCollection.d.ts.map +1 -0
  19. package/dist/collections/PaymentCollection.d.ts +96 -0
  20. package/dist/collections/PaymentCollection.d.ts.map +1 -0
  21. package/dist/collections/PaymentIntentCollection.d.ts +66 -0
  22. package/dist/collections/PaymentIntentCollection.d.ts.map +1 -0
  23. package/dist/collections/PayoutCollection.d.ts +47 -0
  24. package/dist/collections/PayoutCollection.d.ts.map +1 -0
  25. package/dist/collections/VendorCollection.d.ts +59 -0
  26. package/dist/collections/VendorCollection.d.ts.map +1 -0
  27. package/dist/collections/index.d.ts +15 -0
  28. package/dist/collections/index.d.ts.map +1 -0
  29. package/dist/index.d.ts +5 -0
  30. package/dist/index.d.ts.map +1 -0
  31. package/dist/index.js +5308 -0
  32. package/dist/index.js.map +1 -0
  33. package/dist/manifest.json +13852 -0
  34. package/dist/models/Contract.d.ts +425 -0
  35. package/dist/models/Contract.d.ts.map +1 -0
  36. package/dist/models/ContractLineItem.d.ts +92 -0
  37. package/dist/models/ContractLineItem.d.ts.map +1 -0
  38. package/dist/models/Customer.d.ts +98 -0
  39. package/dist/models/Customer.d.ts.map +1 -0
  40. package/dist/models/Fulfillment.d.ts +99 -0
  41. package/dist/models/Fulfillment.d.ts.map +1 -0
  42. package/dist/models/FulfillmentLineItem.d.ts +42 -0
  43. package/dist/models/FulfillmentLineItem.d.ts.map +1 -0
  44. package/dist/models/Invoice.d.ts +326 -0
  45. package/dist/models/Invoice.d.ts.map +1 -0
  46. package/dist/models/InvoiceLineItem.d.ts +120 -0
  47. package/dist/models/InvoiceLineItem.d.ts.map +1 -0
  48. package/dist/models/Payment.d.ts +269 -0
  49. package/dist/models/Payment.d.ts.map +1 -0
  50. package/dist/models/PaymentAllocation.d.ts +93 -0
  51. package/dist/models/PaymentAllocation.d.ts.map +1 -0
  52. package/dist/models/PaymentIntent.d.ts +341 -0
  53. package/dist/models/PaymentIntent.d.ts.map +1 -0
  54. package/dist/models/Payout.d.ts +200 -0
  55. package/dist/models/Payout.d.ts.map +1 -0
  56. package/dist/models/Vendor.d.ts +153 -0
  57. package/dist/models/Vendor.d.ts.map +1 -0
  58. package/dist/models/index.d.ts +17 -0
  59. package/dist/models/index.d.ts.map +1 -0
  60. package/dist/playground.d.ts +2 -0
  61. package/dist/playground.d.ts.map +1 -0
  62. package/dist/playground.js +108 -0
  63. package/dist/playground.js.map +1 -0
  64. package/dist/smrt-knowledge.json +5494 -0
  65. package/dist/svelte/components/InvoiceActions.svelte +191 -0
  66. package/dist/svelte/components/InvoiceActions.svelte.d.ts +26 -0
  67. package/dist/svelte/components/InvoiceActions.svelte.d.ts.map +1 -0
  68. package/dist/svelte/components/InvoiceCard.svelte +233 -0
  69. package/dist/svelte/components/InvoiceCard.svelte.d.ts +16 -0
  70. package/dist/svelte/components/InvoiceCard.svelte.d.ts.map +1 -0
  71. package/dist/svelte/components/InvoiceHeader.svelte +258 -0
  72. package/dist/svelte/components/InvoiceHeader.svelte.d.ts +26 -0
  73. package/dist/svelte/components/InvoiceHeader.svelte.d.ts.map +1 -0
  74. package/dist/svelte/components/InvoiceLineItems.svelte +322 -0
  75. package/dist/svelte/components/InvoiceLineItems.svelte.d.ts +24 -0
  76. package/dist/svelte/components/InvoiceLineItems.svelte.d.ts.map +1 -0
  77. package/dist/svelte/components/InvoiceTotals.svelte +193 -0
  78. package/dist/svelte/components/InvoiceTotals.svelte.d.ts +27 -0
  79. package/dist/svelte/components/InvoiceTotals.svelte.d.ts.map +1 -0
  80. package/dist/svelte/components/UnbilledItems.svelte +355 -0
  81. package/dist/svelte/components/UnbilledItems.svelte.d.ts +18 -0
  82. package/dist/svelte/components/UnbilledItems.svelte.d.ts.map +1 -0
  83. package/dist/svelte/i18n.d.ts +19 -0
  84. package/dist/svelte/i18n.d.ts.map +1 -0
  85. package/dist/svelte/i18n.js +19 -0
  86. package/dist/svelte/index.d.ts +40 -0
  87. package/dist/svelte/index.d.ts.map +1 -0
  88. package/dist/svelte/index.js +43 -0
  89. package/dist/svelte/playground.d.ts +103 -0
  90. package/dist/svelte/playground.d.ts.map +1 -0
  91. package/dist/svelte/playground.js +103 -0
  92. package/dist/svelte/types.d.ts +47 -0
  93. package/dist/svelte/types.d.ts.map +1 -0
  94. package/dist/svelte/types.js +4 -0
  95. package/dist/types/index.d.ts +234 -0
  96. package/dist/types/index.d.ts.map +1 -0
  97. package/dist/ui.d.ts +10 -0
  98. package/dist/ui.d.ts.map +1 -0
  99. package/dist/ui.js +85 -0
  100. package/dist/ui.js.map +1 -0
  101. package/package.json +87 -0
@@ -0,0 +1,193 @@
1
+ <script lang="ts">
2
+ /**
3
+ * InvoiceTotals - Totals section for invoices/estimates
4
+ *
5
+ * Displays subtotal, tax, total, and optional amount paid/balance due.
6
+ */
7
+
8
+ import { useI18n } from '@happyvertical/smrt-ui/i18n';
9
+ import { M } from '../i18n.js';
10
+
11
+ const { t } = useI18n();
12
+
13
+ /** Props for InvoiceTotals component */
14
+ export interface Props {
15
+ /** Subtotal in cents */
16
+ subtotal: number;
17
+ /** Tax rate as percentage (e.g., 5 for 5%) */
18
+ taxRate?: number;
19
+ /** Tax amount in cents (calculated from rate if not provided) */
20
+ taxAmount?: number;
21
+ /** Total in cents */
22
+ total: number;
23
+ /** Amount already paid in cents */
24
+ amountPaid?: number;
25
+ /** Currency code */
26
+ currency?: 'CAD' | 'USD';
27
+ /** Show tax breakdown */
28
+ showTax?: boolean;
29
+ /** Show payment status */
30
+ showPaid?: boolean;
31
+ /** Tax label */
32
+ taxLabel?: string;
33
+ /** Size variant */
34
+ size?: 'sm' | 'md' | 'lg';
35
+ }
36
+
37
+ const {
38
+ subtotal,
39
+ taxRate = 0,
40
+ taxAmount,
41
+ total,
42
+ amountPaid = 0,
43
+ currency = 'CAD',
44
+ showTax = true,
45
+ showPaid = false,
46
+ taxLabel = 'GST',
47
+ size = 'md',
48
+ }: Props = $props();
49
+
50
+ // Calculate tax if not provided
51
+ const calculatedTax = $derived(
52
+ taxAmount ?? Math.round(subtotal * (taxRate / 100)),
53
+ );
54
+
55
+ // Calculate balance due
56
+ const balanceDue = $derived(total - amountPaid);
57
+
58
+ // Format cents to dollars
59
+ function formatMoney(cents: number): string {
60
+ return new Intl.NumberFormat('en-CA', {
61
+ style: 'currency',
62
+ currency,
63
+ minimumFractionDigits: 2,
64
+ }).format(cents / 100);
65
+ }
66
+ </script>
67
+
68
+ <div class="invoice-totals" class:sm={size === 'sm'} class:lg={size === 'lg'}>
69
+ <div class="totals-row">
70
+ <span class="totals-label">Subtotal</span>
71
+ <span class="totals-value">{formatMoney(subtotal)}</span>
72
+ </div>
73
+
74
+ {#if showTax && (taxRate > 0 || calculatedTax > 0)}
75
+ <div class="totals-row">
76
+ <span class="totals-label">{taxLabel} ({taxRate}%)</span>
77
+ <span class="totals-value">{formatMoney(calculatedTax)}</span>
78
+ </div>
79
+ {/if}
80
+
81
+ <div class="totals-row total">
82
+ <span class="totals-label">Total</span>
83
+ <span class="totals-value">{formatMoney(total)}</span>
84
+ </div>
85
+
86
+ {#if showPaid && amountPaid > 0}
87
+ <div class="totals-row paid">
88
+ <span class="totals-label">{t(M['commerce.invoice_totals.amount_paid'])}</span>
89
+ <span class="totals-value">-{formatMoney(amountPaid)}</span>
90
+ </div>
91
+
92
+ <div class="totals-row balance" class:due={balanceDue > 0} class:credit={balanceDue < 0}>
93
+ <span class="totals-label">
94
+ {#if balanceDue > 0}
95
+ {t(M['commerce.invoice_totals.balance_due'])}
96
+ {:else if balanceDue < 0}
97
+ {t(M['commerce.invoice_totals.credit'])}
98
+ {:else}
99
+ {t(M['commerce.invoice_totals.paid_in_full'])}
100
+ {/if}
101
+ </span>
102
+ <span class="totals-value">{formatMoney(Math.abs(balanceDue))}</span>
103
+ </div>
104
+ {/if}
105
+ </div>
106
+
107
+ <style>
108
+ .invoice-totals {
109
+ display: flex;
110
+ flex-direction: column;
111
+ gap: var(--smrt-spacing-2, 0.5rem);
112
+ width: 100%;
113
+ max-width: 280px;
114
+ margin-left: auto;
115
+ }
116
+
117
+ .invoice-totals.sm {
118
+ max-width: 220px;
119
+ gap: var(--smrt-spacing-1_5, 0.375rem);
120
+ }
121
+
122
+ .invoice-totals.lg {
123
+ max-width: 320px;
124
+ gap: var(--smrt-spacing-3, 0.75rem);
125
+ }
126
+
127
+ .totals-row {
128
+ display: flex;
129
+ justify-content: space-between;
130
+ align-items: center;
131
+ font: var(--smrt-typography-body-medium-font);
132
+ color: var(--smrt-color-on-surface, #1c1b1f);
133
+ }
134
+
135
+ .sm .totals-row {
136
+ font: var(--smrt-typography-body-small-font);
137
+ }
138
+
139
+ .lg .totals-row {
140
+ font: var(--smrt-typography-body-large-font);
141
+ }
142
+
143
+ .totals-label {
144
+ color: var(--smrt-color-on-surface-variant, #49454f);
145
+ }
146
+
147
+ .totals-value {
148
+ font-variant-numeric: tabular-nums;
149
+ font-weight: var(--smrt-typography-weight-medium, 500);
150
+ color: var(--smrt-color-on-surface, #1c1b1f);
151
+ }
152
+
153
+ .totals-row.total {
154
+ padding-top: var(--smrt-spacing-2, 0.5rem);
155
+ border-top: 2px solid var(--smrt-color-outline-variant, #c4c6d0);
156
+ font-weight: var(--smrt-typography-weight-semibold, 600);
157
+ }
158
+
159
+ .totals-row.total .totals-label,
160
+ .totals-row.total .totals-value {
161
+ color: var(--smrt-color-on-surface, #1c1b1f);
162
+ font: var(--smrt-typography-title-medium-font);
163
+ }
164
+
165
+ .lg .totals-row.total .totals-label,
166
+ .lg .totals-row.total .totals-value {
167
+ font: var(--smrt-typography-title-large-font);
168
+ }
169
+
170
+ .totals-row.paid {
171
+ color: var(--smrt-color-tertiary, #006c4c);
172
+ }
173
+
174
+ .totals-row.paid .totals-label {
175
+ color: var(--smrt-color-tertiary, #006c4c);
176
+ }
177
+
178
+ .totals-row.balance {
179
+ padding-top: var(--smrt-spacing-2, 0.5rem);
180
+ border-top: 1px solid var(--smrt-color-outline-variant, #c4c6d0);
181
+ font-weight: var(--smrt-typography-weight-semibold, 600);
182
+ }
183
+
184
+ .totals-row.balance.due .totals-label,
185
+ .totals-row.balance.due .totals-value {
186
+ color: var(--smrt-color-error, #ba1a1a);
187
+ }
188
+
189
+ .totals-row.balance.credit .totals-label,
190
+ .totals-row.balance.credit .totals-value {
191
+ color: var(--smrt-color-tertiary, #006c4c);
192
+ }
193
+ </style>
@@ -0,0 +1,27 @@
1
+ /** Props for InvoiceTotals component */
2
+ export interface Props {
3
+ /** Subtotal in cents */
4
+ subtotal: number;
5
+ /** Tax rate as percentage (e.g., 5 for 5%) */
6
+ taxRate?: number;
7
+ /** Tax amount in cents (calculated from rate if not provided) */
8
+ taxAmount?: number;
9
+ /** Total in cents */
10
+ total: number;
11
+ /** Amount already paid in cents */
12
+ amountPaid?: number;
13
+ /** Currency code */
14
+ currency?: 'CAD' | 'USD';
15
+ /** Show tax breakdown */
16
+ showTax?: boolean;
17
+ /** Show payment status */
18
+ showPaid?: boolean;
19
+ /** Tax label */
20
+ taxLabel?: string;
21
+ /** Size variant */
22
+ size?: 'sm' | 'md' | 'lg';
23
+ }
24
+ declare const InvoiceTotals: import("svelte").Component<Props, {}, "">;
25
+ type InvoiceTotals = ReturnType<typeof InvoiceTotals>;
26
+ export default InvoiceTotals;
27
+ //# sourceMappingURL=InvoiceTotals.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"InvoiceTotals.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/InvoiceTotals.svelte.ts"],"names":[],"mappings":"AAYA,wCAAwC;AACxC,MAAM,WAAW,KAAK;IACpB,wBAAwB;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,8CAA8C;IAC9C,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,iEAAiE;IACjE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,qBAAqB;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,mCAAmC;IACnC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,oBAAoB;IACpB,QAAQ,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC;IACzB,yBAAyB;IACzB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,0BAA0B;IAC1B,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,gBAAgB;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,mBAAmB;IACnB,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,GAAG,IAAI,CAAC;CAC3B;AAoFD,QAAA,MAAM,aAAa,2CAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
@@ -0,0 +1,355 @@
1
+ <script lang="ts">
2
+ /**
3
+ * UnbilledItems - Selectable list of unbilled expenses/time
4
+ *
5
+ * Shows unbilled items with checkbox selection for invoice creation.
6
+ */
7
+
8
+ import { useI18n } from '@happyvertical/smrt-ui/i18n';
9
+ import { M } from '../i18n.js';
10
+ import type { UnbilledItem } from '../types.js';
11
+
12
+ const { t } = useI18n();
13
+
14
+ /** Props for UnbilledItems component */
15
+ export interface Props {
16
+ /** Unbilled items */
17
+ items: UnbilledItem[];
18
+ /** Currency code */
19
+ currency?: 'CAD' | 'USD';
20
+ /** Called when selection changes */
21
+ onselectionchange?: (selectedIds: string[]) => void;
22
+ /** Called when create invoice is clicked */
23
+ oncreate?: (selectedIds: string[]) => void;
24
+ /** Empty state message */
25
+ emptyMessage?: string;
26
+ }
27
+
28
+ let {
29
+ items = $bindable([]),
30
+ currency = 'CAD',
31
+ onselectionchange,
32
+ oncreate,
33
+ emptyMessage = 'No unbilled items',
34
+ }: Props = $props();
35
+
36
+ // Track selection state
37
+ let selectedIds = $state<Set<string>>(
38
+ new Set(items.filter((i) => i.selected).map((i) => i.id)),
39
+ );
40
+
41
+ // Update parent when selection changes
42
+ $effect(() => {
43
+ onselectionchange?.([...selectedIds]);
44
+ });
45
+
46
+ // Format money
47
+ function formatMoney(cents: number): string {
48
+ return new Intl.NumberFormat('en-CA', {
49
+ style: 'currency',
50
+ currency,
51
+ minimumFractionDigits: 2,
52
+ }).format(cents / 100);
53
+ }
54
+
55
+ // Format date
56
+ function formatDate(date: Date | string): string {
57
+ const d = typeof date === 'string' ? new Date(date) : date;
58
+ return new Intl.DateTimeFormat('en-CA', {
59
+ month: 'short',
60
+ day: 'numeric',
61
+ }).format(d);
62
+ }
63
+
64
+ // Toggle item selection
65
+ function toggleItem(id: string) {
66
+ if (selectedIds.has(id)) {
67
+ selectedIds.delete(id);
68
+ } else {
69
+ selectedIds.add(id);
70
+ }
71
+ selectedIds = new Set(selectedIds); // Trigger reactivity
72
+ }
73
+
74
+ // Toggle all items
75
+ function toggleAll() {
76
+ if (selectedIds.size === items.length) {
77
+ selectedIds = new Set();
78
+ } else {
79
+ selectedIds = new Set(items.map((i) => i.id));
80
+ }
81
+ }
82
+
83
+ // Calculate selected total
84
+ const selectedTotal = $derived(
85
+ items
86
+ .filter((i) => selectedIds.has(i.id))
87
+ .reduce((sum, i) => sum + i.amount, 0),
88
+ );
89
+
90
+ // All selected
91
+ const allSelected = $derived(
92
+ selectedIds.size === items.length && items.length > 0,
93
+ );
94
+ const someSelected = $derived(
95
+ selectedIds.size > 0 && selectedIds.size < items.length,
96
+ );
97
+ </script>
98
+
99
+ <div class="unbilled-items">
100
+ {#if items.length === 0}
101
+ <div class="empty-state">
102
+ <p>{emptyMessage}</p>
103
+ </div>
104
+ {:else}
105
+ <div class="items-header">
106
+ <label class="select-all">
107
+ <input
108
+ type="checkbox"
109
+ checked={allSelected}
110
+ indeterminate={someSelected}
111
+ onchange={toggleAll}
112
+ />
113
+ <span>{t(M['commerce.unbilled_items.select_all'])}</span>
114
+ </label>
115
+ <span class="selected-count">
116
+ {t(M['commerce.unbilled_items.selected_count'], { selected: selectedIds.size, total: items.length })}
117
+ </span>
118
+ </div>
119
+
120
+ <div class="items-list">
121
+ {#each items as item (item.id)}
122
+ <label class="item-row" class:selected={selectedIds.has(item.id)}>
123
+ <input
124
+ type="checkbox"
125
+ checked={selectedIds.has(item.id)}
126
+ onchange={() => toggleItem(item.id)}
127
+ />
128
+ <div class="item-content">
129
+ <div class="item-main">
130
+ <span class="item-type type-{item.type}">{item.type}</span>
131
+ <span class="item-description">{item.description}</span>
132
+ </div>
133
+ <div class="item-meta">
134
+ {#if item.category}
135
+ <span class="item-category">{item.category}</span>
136
+ {/if}
137
+ <span class="item-date">{formatDate(item.date)}</span>
138
+ </div>
139
+ </div>
140
+ <span class="item-amount">{formatMoney(item.amount)}</span>
141
+ </label>
142
+ {/each}
143
+ </div>
144
+
145
+ {#if selectedIds.size > 0}
146
+ <div class="items-footer">
147
+ <div class="selected-summary">
148
+ <span class="summary-label">{t(M['commerce.unbilled_items.selected_total'])}</span>
149
+ <span class="summary-value">{formatMoney(selectedTotal)}</span>
150
+ </div>
151
+ {#if oncreate}
152
+ <button
153
+ type="button"
154
+ class="create-btn"
155
+ onclick={() => oncreate?.([...selectedIds])}
156
+ >
157
+ {t(M['commerce.unbilled_items.create_invoice'])}
158
+ </button>
159
+ {/if}
160
+ </div>
161
+ {/if}
162
+ {/if}
163
+ </div>
164
+
165
+ <style>
166
+ .unbilled-items {
167
+ display: flex;
168
+ flex-direction: column;
169
+ border: 1px solid var(--smrt-color-outline-variant, #c4c6d0);
170
+ border-radius: var(--smrt-radius-medium, 0.5rem);
171
+ overflow: hidden;
172
+ }
173
+
174
+ .empty-state {
175
+ display: flex;
176
+ align-items: center;
177
+ justify-content: center;
178
+ padding: var(--smrt-spacing-8, 2rem) var(--smrt-spacing-4, 1rem);
179
+ color: var(--smrt-color-on-surface-variant, #49454f);
180
+ }
181
+
182
+ .empty-state p {
183
+ margin: 0;
184
+ }
185
+
186
+ .items-header {
187
+ display: flex;
188
+ justify-content: space-between;
189
+ align-items: center;
190
+ padding: var(--smrt-spacing-3, 0.75rem) var(--smrt-spacing-4, 1rem);
191
+ background: var(--smrt-color-surface-container-low, #f7f2fa);
192
+ border-bottom: 1px solid var(--smrt-color-outline-variant, #c4c6d0);
193
+ }
194
+
195
+ .select-all {
196
+ display: flex;
197
+ align-items: center;
198
+ gap: var(--smrt-spacing-2, 0.5rem);
199
+ font: var(--smrt-typography-body-medium-font);
200
+ font-weight: var(--smrt-typography-weight-medium, 500);
201
+ color: var(--smrt-color-on-surface, #1c1b1f);
202
+ cursor: pointer;
203
+ }
204
+
205
+ .selected-count {
206
+ font: var(--smrt-typography-label-small-font);
207
+ color: var(--smrt-color-on-surface-variant, #49454f);
208
+ }
209
+
210
+ .items-list {
211
+ display: flex;
212
+ flex-direction: column;
213
+ max-height: 400px;
214
+ overflow-y: auto;
215
+ background: var(--smrt-color-surface, #ffffff);
216
+ }
217
+
218
+ .item-row {
219
+ display: flex;
220
+ align-items: center;
221
+ gap: var(--smrt-spacing-3, 0.75rem);
222
+ padding: var(--smrt-spacing-3, 0.75rem) var(--smrt-spacing-4, 1rem);
223
+ border-bottom: 1px solid var(--smrt-color-surface-variant, #e7e0ec);
224
+ cursor: pointer;
225
+ transition: background var(--smrt-duration-fast, 150ms) var(--smrt-easing-standard, cubic-bezier(0.2, 0, 0, 1));
226
+ }
227
+
228
+ @media (prefers-reduced-motion: reduce) {
229
+ .item-row {
230
+ transition: none;
231
+ }
232
+ }
233
+
234
+ .item-row:hover {
235
+ background: var(--smrt-color-surface-container-low, #f7f2fa);
236
+ }
237
+
238
+ .item-row.selected {
239
+ background: var(--smrt-color-primary-container, #d3e3fd);
240
+ }
241
+
242
+ .item-row:last-child {
243
+ border-bottom: none;
244
+ }
245
+
246
+ .item-content {
247
+ flex: 1;
248
+ display: flex;
249
+ flex-direction: column;
250
+ gap: var(--smrt-spacing-1, 0.25rem);
251
+ min-width: 0;
252
+ }
253
+
254
+ .item-main {
255
+ display: flex;
256
+ align-items: center;
257
+ gap: var(--smrt-spacing-2, 0.5rem);
258
+ }
259
+
260
+ .item-type {
261
+ display: inline-flex;
262
+ padding: var(--smrt-spacing-0_5, 0.125rem) var(--smrt-spacing-2, 0.5rem);
263
+ font: var(--smrt-typography-label-small-font);
264
+ font-weight: var(--smrt-typography-weight-medium, 500);
265
+ text-transform: uppercase;
266
+ letter-spacing: 0.025em;
267
+ border-radius: var(--smrt-radius-full, 9999px);
268
+ }
269
+
270
+ .type-expense {
271
+ background: var(--smrt-color-tertiary-container, #ddf5e5);
272
+ color: var(--smrt-color-on-tertiary-container, #0c1f15);
273
+ }
274
+
275
+ .type-time {
276
+ background: var(--smrt-color-primary-container, #d3e3fd);
277
+ color: var(--smrt-color-on-primary-container, #041e49);
278
+ }
279
+
280
+ .item-description {
281
+ font: var(--smrt-typography-body-medium-font);
282
+ color: var(--smrt-color-on-surface, #1c1b1f);
283
+ white-space: nowrap;
284
+ overflow: hidden;
285
+ text-overflow: ellipsis;
286
+ }
287
+
288
+ .item-meta {
289
+ display: flex;
290
+ gap: var(--smrt-spacing-3, 0.75rem);
291
+ font: var(--smrt-typography-body-small-font);
292
+ color: var(--smrt-color-on-surface-variant, #49454f);
293
+ }
294
+
295
+ .item-category {
296
+ color: var(--smrt-color-on-surface-variant, #49454f);
297
+ }
298
+
299
+ .item-amount {
300
+ font: var(--smrt-typography-body-medium-font);
301
+ font-weight: var(--smrt-typography-weight-medium, 500);
302
+ color: var(--smrt-color-on-surface, #1c1b1f);
303
+ font-variant-numeric: tabular-nums;
304
+ }
305
+
306
+ .items-footer {
307
+ display: flex;
308
+ justify-content: space-between;
309
+ align-items: center;
310
+ padding: var(--smrt-spacing-4, 1rem);
311
+ background: var(--smrt-color-surface-container-low, #f7f2fa);
312
+ border-top: 1px solid var(--smrt-color-outline-variant, #c4c6d0);
313
+ }
314
+
315
+ .selected-summary {
316
+ display: flex;
317
+ align-items: baseline;
318
+ gap: var(--smrt-spacing-2, 0.5rem);
319
+ }
320
+
321
+ .summary-label {
322
+ font: var(--smrt-typography-body-medium-font);
323
+ color: var(--smrt-color-on-surface-variant, #49454f);
324
+ }
325
+
326
+ .summary-value {
327
+ font: var(--smrt-typography-title-large-font);
328
+ font-weight: var(--smrt-typography-weight-semibold, 600);
329
+ color: var(--smrt-color-on-surface, #1c1b1f);
330
+ }
331
+
332
+ .create-btn {
333
+ display: inline-flex;
334
+ align-items: center;
335
+ padding: var(--smrt-spacing-2, 0.5rem) var(--smrt-spacing-4, 1rem);
336
+ font: var(--smrt-typography-label-large-font);
337
+ font-weight: var(--smrt-typography-weight-medium, 500);
338
+ color: var(--smrt-color-on-primary, #ffffff);
339
+ background: var(--smrt-color-primary, #005ac1);
340
+ border: none;
341
+ border-radius: var(--smrt-radius-small, 0.375rem);
342
+ cursor: pointer;
343
+ transition: background var(--smrt-duration-fast, 150ms) var(--smrt-easing-standard, cubic-bezier(0.2, 0, 0, 1));
344
+ }
345
+
346
+ @media (prefers-reduced-motion: reduce) {
347
+ .create-btn {
348
+ transition: none;
349
+ }
350
+ }
351
+
352
+ .create-btn:hover {
353
+ background: color-mix(in srgb, var(--smrt-color-primary, #005ac1) 85%, var(--smrt-color-shadow, #000));
354
+ }
355
+ </style>
@@ -0,0 +1,18 @@
1
+ import type { UnbilledItem } from '../types.js';
2
+ /** Props for UnbilledItems component */
3
+ export interface Props {
4
+ /** Unbilled items */
5
+ items: UnbilledItem[];
6
+ /** Currency code */
7
+ currency?: 'CAD' | 'USD';
8
+ /** Called when selection changes */
9
+ onselectionchange?: (selectedIds: string[]) => void;
10
+ /** Called when create invoice is clicked */
11
+ oncreate?: (selectedIds: string[]) => void;
12
+ /** Empty state message */
13
+ emptyMessage?: string;
14
+ }
15
+ declare const UnbilledItems: import("svelte").Component<Props, {}, "items">;
16
+ type UnbilledItems = ReturnType<typeof UnbilledItems>;
17
+ export default UnbilledItems;
18
+ //# sourceMappingURL=UnbilledItems.svelte.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UnbilledItems.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/UnbilledItems.svelte.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAGhD,wCAAwC;AACxC,MAAM,WAAW,KAAK;IACpB,qBAAqB;IACrB,KAAK,EAAE,YAAY,EAAE,CAAC;IACtB,oBAAoB;IACpB,QAAQ,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC;IACzB,oCAAoC;IACpC,iBAAiB,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IACpD,4CAA4C;IAC5C,QAAQ,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IAC3C,0BAA0B;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AA2ID,QAAA,MAAM,aAAa,gDAAwC,CAAC;AAC5D,KAAK,aAAa,GAAG,UAAU,CAAC,OAAO,aAAa,CAAC,CAAC;AACtD,eAAe,aAAa,CAAC"}
@@ -0,0 +1,19 @@
1
+ export declare const M: {
2
+ readonly 'commerce.invoice_actions.send_invoice': "commerce.invoice_actions.send_invoice";
3
+ readonly 'commerce.invoice_actions.mark_as_paid': "commerce.invoice_actions.mark_as_paid";
4
+ readonly 'commerce.invoice_header.issue_date': "commerce.invoice_header.issue_date";
5
+ readonly 'commerce.invoice_header.due_date': "commerce.invoice_header.due_date";
6
+ readonly 'commerce.invoice_header.paid_date': "commerce.invoice_header.paid_date";
7
+ readonly 'commerce.invoice_line_items.add_item': "commerce.invoice_line_items.add_item";
8
+ readonly 'commerce.invoice_line_items.unit_price': "commerce.invoice_line_items.unit_price";
9
+ readonly 'commerce.invoice_line_items.remove_item': "commerce.invoice_line_items.remove_item";
10
+ readonly 'commerce.invoice_totals.amount_paid': "commerce.invoice_totals.amount_paid";
11
+ readonly 'commerce.invoice_totals.balance_due': "commerce.invoice_totals.balance_due";
12
+ readonly 'commerce.invoice_totals.credit': "commerce.invoice_totals.credit";
13
+ readonly 'commerce.invoice_totals.paid_in_full': "commerce.invoice_totals.paid_in_full";
14
+ readonly 'commerce.unbilled_items.select_all': "commerce.unbilled_items.select_all";
15
+ readonly 'commerce.unbilled_items.selected_count': "commerce.unbilled_items.selected_count";
16
+ readonly 'commerce.unbilled_items.selected_total': "commerce.unbilled_items.selected_total";
17
+ readonly 'commerce.unbilled_items.create_invoice': "commerce.unbilled_items.create_invoice";
18
+ };
19
+ //# sourceMappingURL=i18n.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"i18n.d.ts","sourceRoot":"","sources":["../../src/svelte/i18n.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,CAAC;;;;;;;;;;;;;;;;;CAiBZ,CAAC"}
@@ -0,0 +1,19 @@
1
+ import { defineMessages } from '@happyvertical/smrt-ui/i18n';
2
+ export const M = defineMessages({
3
+ 'commerce.invoice_actions.send_invoice': 'Send Invoice',
4
+ 'commerce.invoice_actions.mark_as_paid': 'Mark as Paid',
5
+ 'commerce.invoice_header.issue_date': 'Issue Date',
6
+ 'commerce.invoice_header.due_date': 'Due Date',
7
+ 'commerce.invoice_header.paid_date': 'Paid Date',
8
+ 'commerce.invoice_line_items.add_item': 'Add Item',
9
+ 'commerce.invoice_line_items.unit_price': 'Unit Price',
10
+ 'commerce.invoice_line_items.remove_item': 'Remove item',
11
+ 'commerce.invoice_totals.amount_paid': 'Amount Paid',
12
+ 'commerce.invoice_totals.balance_due': 'Balance Due',
13
+ 'commerce.invoice_totals.credit': 'Credit',
14
+ 'commerce.invoice_totals.paid_in_full': 'Paid in Full',
15
+ 'commerce.unbilled_items.select_all': 'Select All',
16
+ 'commerce.unbilled_items.selected_count': '{selected} of {total} selected',
17
+ 'commerce.unbilled_items.selected_total': 'Selected Total:',
18
+ 'commerce.unbilled_items.create_invoice': 'Create Invoice',
19
+ });
@@ -0,0 +1,40 @@
1
+ /**
2
+ * Commerce Svelte Components
3
+ *
4
+ * This module auto-registers all commerce UI components with the ModuleUIRegistry
5
+ * when imported. Import this module to enable registry-based component discovery.
6
+ *
7
+ * @example Direct imports
8
+ * ```typescript
9
+ * import { InvoiceCard, InvoiceHeader } from '@happyvertical/smrt-commerce/svelte';
10
+ * ```
11
+ *
12
+ * @example Registry-based discovery
13
+ * ```typescript
14
+ * import { ModuleUIRegistry } from '@happyvertical/smrt-ui/registry';
15
+ * import '@happyvertical/smrt-commerce/svelte'; // Auto-registers components
16
+ *
17
+ * const Component = ModuleUIRegistry.get('@happyvertical/smrt-commerce', 'invoice-card');
18
+ * ```
19
+ */
20
+ import type { ComponentProps } from 'svelte';
21
+ import InvoiceActions from './components/InvoiceActions.svelte';
22
+ import InvoiceCard from './components/InvoiceCard.svelte';
23
+ import InvoiceHeader from './components/InvoiceHeader.svelte';
24
+ import InvoiceLineItems from './components/InvoiceLineItems.svelte';
25
+ import InvoiceTotals from './components/InvoiceTotals.svelte';
26
+ import UnbilledItems from './components/UnbilledItems.svelte';
27
+ export type InvoiceActionsProps = ComponentProps<typeof InvoiceActions>;
28
+ export { default as InvoiceActions } from './components/InvoiceActions.svelte';
29
+ export type InvoiceCardProps = ComponentProps<typeof InvoiceCard>;
30
+ export { default as InvoiceCard } from './components/InvoiceCard.svelte';
31
+ export type InvoiceHeaderProps = ComponentProps<typeof InvoiceHeader>;
32
+ export { default as InvoiceHeader } from './components/InvoiceHeader.svelte';
33
+ export type InvoiceLineItemsProps = ComponentProps<typeof InvoiceLineItems>;
34
+ export { default as InvoiceLineItems } from './components/InvoiceLineItems.svelte';
35
+ export type InvoiceTotalsProps = ComponentProps<typeof InvoiceTotals>;
36
+ export { default as InvoiceTotals } from './components/InvoiceTotals.svelte';
37
+ export type UnbilledItemsProps = ComponentProps<typeof UnbilledItems>;
38
+ export { default as UnbilledItems } from './components/UnbilledItems.svelte';
39
+ export type { InvoiceData, InvoiceStatus, LineItem, UnbilledItem, } from './types.js';
40
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/svelte/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAGH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,QAAQ,CAAC;AAI7C,OAAO,cAAc,MAAM,oCAAoC,CAAC;AAChE,OAAO,WAAW,MAAM,iCAAiC,CAAC;AAC1D,OAAO,aAAa,MAAM,mCAAmC,CAAC;AAC9D,OAAO,gBAAgB,MAAM,sCAAsC,CAAC;AACpE,OAAO,aAAa,MAAM,mCAAmC,CAAC;AAC9D,OAAO,aAAa,MAAM,mCAAmC,CAAC;AAG9D,MAAM,MAAM,mBAAmB,GAAG,cAAc,CAAC,OAAO,cAAc,CAAC,CAAC;AAExE,OAAO,EAAE,OAAO,IAAI,cAAc,EAAE,MAAM,oCAAoC,CAAC;AAC/E,MAAM,MAAM,gBAAgB,GAAG,cAAc,CAAC,OAAO,WAAW,CAAC,CAAC;AAClE,OAAO,EAAE,OAAO,IAAI,WAAW,EAAE,MAAM,iCAAiC,CAAC;AACzE,MAAM,MAAM,kBAAkB,GAAG,cAAc,CAAC,OAAO,aAAa,CAAC,CAAC;AACtE,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,mCAAmC,CAAC;AAC7E,MAAM,MAAM,qBAAqB,GAAG,cAAc,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAC5E,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,sCAAsC,CAAC;AACnF,MAAM,MAAM,kBAAkB,GAAG,cAAc,CAAC,OAAO,aAAa,CAAC,CAAC;AACtE,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,mCAAmC,CAAC;AAC7E,MAAM,MAAM,kBAAkB,GAAG,cAAc,CAAC,OAAO,aAAa,CAAC,CAAC;AACtE,OAAO,EAAE,OAAO,IAAI,aAAa,EAAE,MAAM,mCAAmC,CAAC;AAE7E,YAAY,EACV,WAAW,EACX,aAAa,EACb,QAAQ,EACR,YAAY,GACb,MAAM,YAAY,CAAC"}