@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.
- package/AGENTS.md +44 -0
- package/CLAUDE.md +1 -0
- package/LICENSE +7 -0
- package/README.md +146 -0
- package/dist/__smrt-register__.d.ts +2 -0
- package/dist/__smrt-register__.d.ts.map +1 -0
- package/dist/collections/ContractCollection.d.ts +87 -0
- package/dist/collections/ContractCollection.d.ts.map +1 -0
- package/dist/collections/CustomerCollection.d.ts +58 -0
- package/dist/collections/CustomerCollection.d.ts.map +1 -0
- package/dist/collections/FulfillmentCollection.d.ts +75 -0
- package/dist/collections/FulfillmentCollection.d.ts.map +1 -0
- package/dist/collections/InvoiceCollection.d.ts +162 -0
- package/dist/collections/InvoiceCollection.d.ts.map +1 -0
- package/dist/collections/InvoiceLineItemCollection.d.ts +90 -0
- package/dist/collections/InvoiceLineItemCollection.d.ts.map +1 -0
- package/dist/collections/PaymentAllocationCollection.d.ts +86 -0
- package/dist/collections/PaymentAllocationCollection.d.ts.map +1 -0
- package/dist/collections/PaymentCollection.d.ts +96 -0
- package/dist/collections/PaymentCollection.d.ts.map +1 -0
- package/dist/collections/PaymentIntentCollection.d.ts +66 -0
- package/dist/collections/PaymentIntentCollection.d.ts.map +1 -0
- package/dist/collections/PayoutCollection.d.ts +47 -0
- package/dist/collections/PayoutCollection.d.ts.map +1 -0
- package/dist/collections/VendorCollection.d.ts +59 -0
- package/dist/collections/VendorCollection.d.ts.map +1 -0
- package/dist/collections/index.d.ts +15 -0
- package/dist/collections/index.d.ts.map +1 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5308 -0
- package/dist/index.js.map +1 -0
- package/dist/manifest.json +13852 -0
- package/dist/models/Contract.d.ts +425 -0
- package/dist/models/Contract.d.ts.map +1 -0
- package/dist/models/ContractLineItem.d.ts +92 -0
- package/dist/models/ContractLineItem.d.ts.map +1 -0
- package/dist/models/Customer.d.ts +98 -0
- package/dist/models/Customer.d.ts.map +1 -0
- package/dist/models/Fulfillment.d.ts +99 -0
- package/dist/models/Fulfillment.d.ts.map +1 -0
- package/dist/models/FulfillmentLineItem.d.ts +42 -0
- package/dist/models/FulfillmentLineItem.d.ts.map +1 -0
- package/dist/models/Invoice.d.ts +326 -0
- package/dist/models/Invoice.d.ts.map +1 -0
- package/dist/models/InvoiceLineItem.d.ts +120 -0
- package/dist/models/InvoiceLineItem.d.ts.map +1 -0
- package/dist/models/Payment.d.ts +269 -0
- package/dist/models/Payment.d.ts.map +1 -0
- package/dist/models/PaymentAllocation.d.ts +93 -0
- package/dist/models/PaymentAllocation.d.ts.map +1 -0
- package/dist/models/PaymentIntent.d.ts +341 -0
- package/dist/models/PaymentIntent.d.ts.map +1 -0
- package/dist/models/Payout.d.ts +200 -0
- package/dist/models/Payout.d.ts.map +1 -0
- package/dist/models/Vendor.d.ts +153 -0
- package/dist/models/Vendor.d.ts.map +1 -0
- package/dist/models/index.d.ts +17 -0
- package/dist/models/index.d.ts.map +1 -0
- package/dist/playground.d.ts +2 -0
- package/dist/playground.d.ts.map +1 -0
- package/dist/playground.js +108 -0
- package/dist/playground.js.map +1 -0
- package/dist/smrt-knowledge.json +5494 -0
- package/dist/svelte/components/InvoiceActions.svelte +191 -0
- package/dist/svelte/components/InvoiceActions.svelte.d.ts +26 -0
- package/dist/svelte/components/InvoiceActions.svelte.d.ts.map +1 -0
- package/dist/svelte/components/InvoiceCard.svelte +233 -0
- package/dist/svelte/components/InvoiceCard.svelte.d.ts +16 -0
- package/dist/svelte/components/InvoiceCard.svelte.d.ts.map +1 -0
- package/dist/svelte/components/InvoiceHeader.svelte +258 -0
- package/dist/svelte/components/InvoiceHeader.svelte.d.ts +26 -0
- package/dist/svelte/components/InvoiceHeader.svelte.d.ts.map +1 -0
- package/dist/svelte/components/InvoiceLineItems.svelte +322 -0
- package/dist/svelte/components/InvoiceLineItems.svelte.d.ts +24 -0
- package/dist/svelte/components/InvoiceLineItems.svelte.d.ts.map +1 -0
- package/dist/svelte/components/InvoiceTotals.svelte +193 -0
- package/dist/svelte/components/InvoiceTotals.svelte.d.ts +27 -0
- package/dist/svelte/components/InvoiceTotals.svelte.d.ts.map +1 -0
- package/dist/svelte/components/UnbilledItems.svelte +355 -0
- package/dist/svelte/components/UnbilledItems.svelte.d.ts +18 -0
- package/dist/svelte/components/UnbilledItems.svelte.d.ts.map +1 -0
- package/dist/svelte/i18n.d.ts +19 -0
- package/dist/svelte/i18n.d.ts.map +1 -0
- package/dist/svelte/i18n.js +19 -0
- package/dist/svelte/index.d.ts +40 -0
- package/dist/svelte/index.d.ts.map +1 -0
- package/dist/svelte/index.js +43 -0
- package/dist/svelte/playground.d.ts +103 -0
- package/dist/svelte/playground.d.ts.map +1 -0
- package/dist/svelte/playground.js +103 -0
- package/dist/svelte/types.d.ts +47 -0
- package/dist/svelte/types.d.ts.map +1 -0
- package/dist/svelte/types.js +4 -0
- package/dist/types/index.d.ts +234 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/ui.d.ts +10 -0
- package/dist/ui.d.ts.map +1 -0
- package/dist/ui.js +85 -0
- package/dist/ui.js.map +1 -0
- package/package.json +87 -0
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* InvoiceActions - Status-based action buttons for invoices
|
|
4
|
+
*
|
|
5
|
+
* Renders appropriate action buttons based on invoice status.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { useI18n } from '@happyvertical/smrt-ui/i18n';
|
|
9
|
+
import { M } from '../i18n.js';
|
|
10
|
+
import type { InvoiceStatus } from '../types.js';
|
|
11
|
+
|
|
12
|
+
const { t } = useI18n();
|
|
13
|
+
|
|
14
|
+
/** Props for InvoiceActions component */
|
|
15
|
+
export interface Props {
|
|
16
|
+
/** Current invoice status */
|
|
17
|
+
status: InvoiceStatus;
|
|
18
|
+
/** Loading state */
|
|
19
|
+
loading?: boolean;
|
|
20
|
+
/** Called when send is clicked */
|
|
21
|
+
onsend?: () => void;
|
|
22
|
+
/** Called when mark paid is clicked */
|
|
23
|
+
onmarkpaid?: () => void;
|
|
24
|
+
/** Called when edit is clicked */
|
|
25
|
+
onedit?: () => void;
|
|
26
|
+
/** Called when delete is clicked */
|
|
27
|
+
ondelete?: () => void;
|
|
28
|
+
/** Called when print is clicked */
|
|
29
|
+
onprint?: () => void;
|
|
30
|
+
/** Called when export/download is clicked */
|
|
31
|
+
onexport?: () => void;
|
|
32
|
+
/** Size variant */
|
|
33
|
+
size?: 'sm' | 'md';
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const {
|
|
37
|
+
status,
|
|
38
|
+
loading = false,
|
|
39
|
+
onsend,
|
|
40
|
+
onmarkpaid,
|
|
41
|
+
onedit,
|
|
42
|
+
ondelete,
|
|
43
|
+
onprint,
|
|
44
|
+
onexport,
|
|
45
|
+
size = 'md',
|
|
46
|
+
}: Props = $props();
|
|
47
|
+
|
|
48
|
+
// Determine which actions to show based on status
|
|
49
|
+
const canEdit = $derived(status === 'draft');
|
|
50
|
+
const canSend = $derived(status === 'draft');
|
|
51
|
+
const canMarkPaid = $derived(
|
|
52
|
+
status === 'sent' || status === 'viewed' || status === 'overdue',
|
|
53
|
+
);
|
|
54
|
+
const canDelete = $derived(status === 'draft');
|
|
55
|
+
</script>
|
|
56
|
+
|
|
57
|
+
<div class="invoice-actions" class:sm={size === 'sm'}>
|
|
58
|
+
{#if canSend && onsend}
|
|
59
|
+
<button type="button" class="btn btn-primary" onclick={onsend} disabled={loading}>
|
|
60
|
+
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5">
|
|
61
|
+
<path d="M14 2L7 9M14 2l-4 12-3-5-5-3 12-4z" />
|
|
62
|
+
</svg>
|
|
63
|
+
{t(M['commerce.invoice_actions.send_invoice'])}
|
|
64
|
+
</button>
|
|
65
|
+
{/if}
|
|
66
|
+
|
|
67
|
+
{#if canMarkPaid && onmarkpaid}
|
|
68
|
+
<button type="button" class="btn btn-success" onclick={onmarkpaid} disabled={loading}>
|
|
69
|
+
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="2">
|
|
70
|
+
<path d="M3 8l4 4 6-8" />
|
|
71
|
+
</svg>
|
|
72
|
+
{t(M['commerce.invoice_actions.mark_as_paid'])}
|
|
73
|
+
</button>
|
|
74
|
+
{/if}
|
|
75
|
+
|
|
76
|
+
{#if canEdit && onedit}
|
|
77
|
+
<button type="button" class="btn btn-secondary" onclick={onedit} disabled={loading}>
|
|
78
|
+
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5">
|
|
79
|
+
<path d="M11.5 2.5l2 2-8 8H3.5v-2l8-8z" />
|
|
80
|
+
</svg>
|
|
81
|
+
Edit
|
|
82
|
+
</button>
|
|
83
|
+
{/if}
|
|
84
|
+
|
|
85
|
+
{#if onprint}
|
|
86
|
+
<button type="button" class="btn btn-secondary" onclick={onprint} disabled={loading}>
|
|
87
|
+
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5">
|
|
88
|
+
<path d="M4 5V2h8v3M4 11h8M2 5h12v6H2z" />
|
|
89
|
+
<path d="M4 11v3h8v-3" />
|
|
90
|
+
</svg>
|
|
91
|
+
Print
|
|
92
|
+
</button>
|
|
93
|
+
{/if}
|
|
94
|
+
|
|
95
|
+
{#if onexport}
|
|
96
|
+
<button type="button" class="btn btn-secondary" onclick={onexport} disabled={loading}>
|
|
97
|
+
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5">
|
|
98
|
+
<path d="M8 2v8M4 6l4-4 4 4M2 12v2h12v-2" />
|
|
99
|
+
</svg>
|
|
100
|
+
Export
|
|
101
|
+
</button>
|
|
102
|
+
{/if}
|
|
103
|
+
|
|
104
|
+
{#if canDelete && ondelete}
|
|
105
|
+
<button type="button" class="btn btn-danger" onclick={ondelete} disabled={loading}>
|
|
106
|
+
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5">
|
|
107
|
+
<path d="M3 4h10M5 4V3a1 1 0 011-1h4a1 1 0 011 1v1M6 7v5M10 7v5" />
|
|
108
|
+
<path d="M4 4l1 10h6l1-10" />
|
|
109
|
+
</svg>
|
|
110
|
+
Delete
|
|
111
|
+
</button>
|
|
112
|
+
{/if}
|
|
113
|
+
</div>
|
|
114
|
+
|
|
115
|
+
<style>
|
|
116
|
+
.invoice-actions {
|
|
117
|
+
display: flex;
|
|
118
|
+
gap: var(--smrt-spacing-2, 0.5rem);
|
|
119
|
+
flex-wrap: wrap;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
.btn {
|
|
123
|
+
display: inline-flex;
|
|
124
|
+
align-items: center;
|
|
125
|
+
gap: var(--smrt-spacing-2, 0.5rem);
|
|
126
|
+
padding: var(--smrt-spacing-2, 0.5rem) var(--smrt-spacing-4, 1rem);
|
|
127
|
+
font: var(--smrt-typography-label-large-font);
|
|
128
|
+
font-weight: var(--smrt-typography-weight-medium, 500);
|
|
129
|
+
border: none;
|
|
130
|
+
border-radius: var(--smrt-radius-small, 0.375rem);
|
|
131
|
+
cursor: pointer;
|
|
132
|
+
transition: all var(--smrt-duration-fast, 150ms) var(--smrt-easing-standard, cubic-bezier(0.2, 0, 0, 1));
|
|
133
|
+
white-space: nowrap;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
@media (prefers-reduced-motion: reduce) {
|
|
137
|
+
.btn {
|
|
138
|
+
transition: none;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
.sm .btn {
|
|
143
|
+
padding: var(--smrt-spacing-1_5, 0.375rem) var(--smrt-spacing-3, 0.75rem);
|
|
144
|
+
font: var(--smrt-typography-label-medium-font);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
.btn:disabled {
|
|
148
|
+
opacity: 0.38;
|
|
149
|
+
cursor: not-allowed;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
.btn-primary {
|
|
153
|
+
background: var(--smrt-color-primary, #005ac1);
|
|
154
|
+
color: var(--smrt-color-on-primary, #ffffff);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
.btn-primary:hover:not(:disabled) {
|
|
158
|
+
background: color-mix(in srgb, var(--smrt-color-primary, #005ac1) 85%, var(--smrt-color-shadow, #000));
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
.btn-success {
|
|
162
|
+
background: var(--smrt-color-tertiary, #006c4c);
|
|
163
|
+
color: var(--smrt-color-on-tertiary, #ffffff);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
.btn-success:hover:not(:disabled) {
|
|
167
|
+
background: color-mix(in srgb, var(--smrt-color-tertiary, #006c4c) 85%, var(--smrt-color-shadow, #000));
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
.btn-secondary {
|
|
171
|
+
background: var(--smrt-color-surface-container-lowest, #ffffff);
|
|
172
|
+
color: var(--smrt-color-on-surface-variant, #44474e);
|
|
173
|
+
border: 1px solid var(--smrt-color-outline, #74777f);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
.btn-secondary:hover:not(:disabled) {
|
|
177
|
+
background: var(--smrt-color-surface-container-low, #f7f2fa);
|
|
178
|
+
border-color: var(--smrt-color-on-surface-variant, #44474e);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
.btn-danger {
|
|
182
|
+
background: var(--smrt-color-surface-container-lowest, #ffffff);
|
|
183
|
+
color: var(--smrt-color-error, #ba1a1a);
|
|
184
|
+
border: 1px solid var(--smrt-color-error-container, #ffdad6);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
.btn-danger:hover:not(:disabled) {
|
|
188
|
+
background: var(--smrt-color-error-container, #ffdad6);
|
|
189
|
+
border-color: var(--smrt-color-error, #ba1a1a);
|
|
190
|
+
}
|
|
191
|
+
</style>
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { InvoiceStatus } from '../types.js';
|
|
2
|
+
/** Props for InvoiceActions component */
|
|
3
|
+
export interface Props {
|
|
4
|
+
/** Current invoice status */
|
|
5
|
+
status: InvoiceStatus;
|
|
6
|
+
/** Loading state */
|
|
7
|
+
loading?: boolean;
|
|
8
|
+
/** Called when send is clicked */
|
|
9
|
+
onsend?: () => void;
|
|
10
|
+
/** Called when mark paid is clicked */
|
|
11
|
+
onmarkpaid?: () => void;
|
|
12
|
+
/** Called when edit is clicked */
|
|
13
|
+
onedit?: () => void;
|
|
14
|
+
/** Called when delete is clicked */
|
|
15
|
+
ondelete?: () => void;
|
|
16
|
+
/** Called when print is clicked */
|
|
17
|
+
onprint?: () => void;
|
|
18
|
+
/** Called when export/download is clicked */
|
|
19
|
+
onexport?: () => void;
|
|
20
|
+
/** Size variant */
|
|
21
|
+
size?: 'sm' | 'md';
|
|
22
|
+
}
|
|
23
|
+
declare const InvoiceActions: import("svelte").Component<Props, {}, "">;
|
|
24
|
+
type InvoiceActions = ReturnType<typeof InvoiceActions>;
|
|
25
|
+
export default InvoiceActions;
|
|
26
|
+
//# sourceMappingURL=InvoiceActions.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InvoiceActions.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/InvoiceActions.svelte.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAGjD,yCAAyC;AACzC,MAAM,WAAW,KAAK;IACpB,6BAA6B;IAC7B,MAAM,EAAE,aAAa,CAAC;IACtB,oBAAoB;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,kCAAkC;IAClC,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,uCAAuC;IACvC,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;IACxB,kCAAkC;IAClC,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,oCAAoC;IACpC,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,mCAAmC;IACnC,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,6CAA6C;IAC7C,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAC;IACtB,mBAAmB;IACnB,IAAI,CAAC,EAAE,IAAI,GAAG,IAAI,CAAC;CACpB;AA8FD,QAAA,MAAM,cAAc,2CAAwC,CAAC;AAC7D,KAAK,cAAc,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AACxD,eAAe,cAAc,CAAC"}
|
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
/**
|
|
3
|
+
* InvoiceCard - Invoice list/grid card
|
|
4
|
+
* refactored for Material 3
|
|
5
|
+
*
|
|
6
|
+
* Compact card view for displaying invoices in lists.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { ripple } from '@happyvertical/smrt-ui';
|
|
10
|
+
import type { InvoiceData, InvoiceStatus } from '../types.js';
|
|
11
|
+
|
|
12
|
+
/** Props for InvoiceCard component */
|
|
13
|
+
export interface Props {
|
|
14
|
+
/** Invoice data */
|
|
15
|
+
invoice: InvoiceData;
|
|
16
|
+
/** Currency code */
|
|
17
|
+
currency?: 'CAD' | 'USD';
|
|
18
|
+
/** Navigation href */
|
|
19
|
+
href?: string;
|
|
20
|
+
/** Click handler */
|
|
21
|
+
onclick?: () => void;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const { invoice, currency = 'CAD', href, onclick }: Props = $props();
|
|
25
|
+
|
|
26
|
+
// Format money
|
|
27
|
+
function formatMoney(cents: number): string {
|
|
28
|
+
return new Intl.NumberFormat('en-CA', {
|
|
29
|
+
style: 'currency',
|
|
30
|
+
currency,
|
|
31
|
+
minimumFractionDigits: 2,
|
|
32
|
+
}).format(cents / 100);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Format date
|
|
36
|
+
function formatDate(date: Date | string | null | undefined): string {
|
|
37
|
+
if (!date) return '';
|
|
38
|
+
const d = typeof date === 'string' ? new Date(date) : date;
|
|
39
|
+
return new Intl.DateTimeFormat('en-CA', {
|
|
40
|
+
month: 'short',
|
|
41
|
+
day: 'numeric',
|
|
42
|
+
}).format(d);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Map status to M3 classes
|
|
46
|
+
const statusClass = $derived.by(() => {
|
|
47
|
+
switch (invoice.status) {
|
|
48
|
+
case 'paid':
|
|
49
|
+
return 'status-paid';
|
|
50
|
+
case 'overdue':
|
|
51
|
+
return 'status-error';
|
|
52
|
+
case 'sent':
|
|
53
|
+
return 'status-info';
|
|
54
|
+
case 'viewed':
|
|
55
|
+
return 'status-info';
|
|
56
|
+
case 'cancelled':
|
|
57
|
+
return 'status-error';
|
|
58
|
+
default:
|
|
59
|
+
return 'status-default';
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// Check if overdue
|
|
64
|
+
const isOverdue = $derived.by(() => {
|
|
65
|
+
if (invoice.status === 'paid' || invoice.status === 'cancelled') return false;
|
|
66
|
+
if (!invoice.dueDate) return false;
|
|
67
|
+
const due =
|
|
68
|
+
typeof invoice.dueDate === 'string'
|
|
69
|
+
? new Date(invoice.dueDate)
|
|
70
|
+
: invoice.dueDate;
|
|
71
|
+
return due < new Date();
|
|
72
|
+
});
|
|
73
|
+
</script>
|
|
74
|
+
|
|
75
|
+
{#if href}
|
|
76
|
+
<a class="invoice-card" {href} use:ripple>
|
|
77
|
+
<div class="card-header">
|
|
78
|
+
<span class="invoice-number">{invoice.invoiceNumber}</span>
|
|
79
|
+
<span class="status-badge {isOverdue ? 'status-error' : statusClass}">
|
|
80
|
+
{isOverdue ? 'Overdue' : invoice.status}
|
|
81
|
+
</span>
|
|
82
|
+
</div>
|
|
83
|
+
|
|
84
|
+
<div class="card-body">
|
|
85
|
+
{#if invoice.customerName}
|
|
86
|
+
<span class="customer-name">{invoice.customerName}</span>
|
|
87
|
+
{/if}
|
|
88
|
+
<span class="invoice-amount">{formatMoney(invoice.totalAmount)}</span>
|
|
89
|
+
</div>
|
|
90
|
+
|
|
91
|
+
<div class="card-footer">
|
|
92
|
+
<span class="issue-date">{formatDate(invoice.issueDate)}</span>
|
|
93
|
+
{#if invoice.dueDate}
|
|
94
|
+
<span class="due-date" class:overdue={isOverdue}>
|
|
95
|
+
Due {formatDate(invoice.dueDate)}
|
|
96
|
+
</span>
|
|
97
|
+
{/if}
|
|
98
|
+
</div>
|
|
99
|
+
</a>
|
|
100
|
+
{:else}
|
|
101
|
+
<button type="button" class="invoice-card" onclick={onclick} use:ripple>
|
|
102
|
+
<div class="card-header">
|
|
103
|
+
<span class="invoice-number">{invoice.invoiceNumber}</span>
|
|
104
|
+
<span class="status-badge {isOverdue ? 'status-error' : statusClass}">
|
|
105
|
+
{isOverdue ? 'Overdue' : invoice.status}
|
|
106
|
+
</span>
|
|
107
|
+
</div>
|
|
108
|
+
|
|
109
|
+
<div class="card-body">
|
|
110
|
+
{#if invoice.customerName}
|
|
111
|
+
<span class="customer-name">{invoice.customerName}</span>
|
|
112
|
+
{/if}
|
|
113
|
+
<span class="invoice-amount">{formatMoney(invoice.totalAmount)}</span>
|
|
114
|
+
</div>
|
|
115
|
+
|
|
116
|
+
<div class="card-footer">
|
|
117
|
+
<span class="issue-date">{formatDate(invoice.issueDate)}</span>
|
|
118
|
+
{#if invoice.dueDate}
|
|
119
|
+
<span class="due-date" class:overdue={isOverdue}>
|
|
120
|
+
Due {formatDate(invoice.dueDate)}
|
|
121
|
+
</span>
|
|
122
|
+
{/if}
|
|
123
|
+
</div>
|
|
124
|
+
</button>
|
|
125
|
+
{/if}
|
|
126
|
+
|
|
127
|
+
<style>
|
|
128
|
+
.invoice-card {
|
|
129
|
+
display: flex;
|
|
130
|
+
flex-direction: column;
|
|
131
|
+
gap: var(--smrt-spacing-3, 12px);
|
|
132
|
+
padding: var(--smrt-spacing-4, 16px);
|
|
133
|
+
background-color: var(--smrt-color-surface-container-low);
|
|
134
|
+
border-radius: var(--smrt-radius-large, 12px);
|
|
135
|
+
text-decoration: none;
|
|
136
|
+
color: var(--smrt-color-on-surface);
|
|
137
|
+
cursor: pointer;
|
|
138
|
+
transition:
|
|
139
|
+
background-color var(--smrt-duration-normal, 200ms) var(--smrt-easing-standard, cubic-bezier(0.2, 0, 0, 1)),
|
|
140
|
+
box-shadow var(--smrt-duration-normal, 200ms) var(--smrt-easing-standard, cubic-bezier(0.2, 0, 0, 1));
|
|
141
|
+
text-align: left;
|
|
142
|
+
width: 100%;
|
|
143
|
+
border: none;
|
|
144
|
+
position: relative;
|
|
145
|
+
overflow: hidden;
|
|
146
|
+
box-shadow: var(--smrt-elevation-1);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
@media (prefers-reduced-motion: reduce) {
|
|
150
|
+
.invoice-card {
|
|
151
|
+
transition: none;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
.invoice-card:hover {
|
|
156
|
+
background-color: var(--smrt-color-surface-container-high);
|
|
157
|
+
box-shadow: var(--smrt-elevation-2);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
.card-header {
|
|
161
|
+
display: flex;
|
|
162
|
+
justify-content: space-between;
|
|
163
|
+
align-items: center;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
.invoice-number {
|
|
167
|
+
font: var(--smrt-typography-label-large-font);
|
|
168
|
+
font-weight: var(--smrt-typography-weight-semibold, 600);
|
|
169
|
+
color: var(--smrt-color-on-surface);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
.status-badge {
|
|
173
|
+
display: inline-flex;
|
|
174
|
+
padding: 0 var(--smrt-spacing-2, 8px);
|
|
175
|
+
height: 20px;
|
|
176
|
+
align-items: center;
|
|
177
|
+
font: var(--smrt-typography-label-small-font);
|
|
178
|
+
font-weight: var(--smrt-typography-weight-semibold, 600);
|
|
179
|
+
border-radius: var(--smrt-radius-full, 10px);
|
|
180
|
+
text-transform: uppercase;
|
|
181
|
+
letter-spacing: 0.5px;
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/* Status Badge Colors */
|
|
185
|
+
.status-default {
|
|
186
|
+
background-color: var(--smrt-color-surface-variant);
|
|
187
|
+
color: var(--smrt-color-on-surface-variant);
|
|
188
|
+
}
|
|
189
|
+
.status-paid {
|
|
190
|
+
background-color: var(--smrt-color-secondary-container);
|
|
191
|
+
color: var(--smrt-color-on-secondary-container);
|
|
192
|
+
}
|
|
193
|
+
.status-info {
|
|
194
|
+
background-color: var(--smrt-color-primary-container);
|
|
195
|
+
color: var(--smrt-color-on-primary-container);
|
|
196
|
+
}
|
|
197
|
+
.status-error {
|
|
198
|
+
background-color: var(--smrt-color-error-container);
|
|
199
|
+
color: var(--smrt-color-on-error-container);
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
.card-body {
|
|
203
|
+
display: flex;
|
|
204
|
+
flex-direction: column;
|
|
205
|
+
gap: var(--smrt-spacing-1, 4px);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
.customer-name {
|
|
209
|
+
font: var(--smrt-typography-body-small-font);
|
|
210
|
+
color: var(--smrt-color-on-surface-variant);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
.invoice-amount {
|
|
214
|
+
font: var(--smrt-typography-headline-small-font);
|
|
215
|
+
font-weight: var(--smrt-typography-weight-semibold, 600);
|
|
216
|
+
color: var(--smrt-color-on-surface);
|
|
217
|
+
font-variant-numeric: tabular-nums;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
.card-footer {
|
|
221
|
+
display: flex;
|
|
222
|
+
justify-content: space-between;
|
|
223
|
+
align-items: center;
|
|
224
|
+
font: var(--smrt-typography-body-small-font);
|
|
225
|
+
color: var(--smrt-color-on-surface-variant);
|
|
226
|
+
opacity: 0.8;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
.due-date.overdue {
|
|
230
|
+
color: var(--smrt-color-error);
|
|
231
|
+
font-weight: var(--smrt-typography-weight-semibold, 600);
|
|
232
|
+
}
|
|
233
|
+
</style>
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { InvoiceData } from '../types.js';
|
|
2
|
+
/** Props for InvoiceCard component */
|
|
3
|
+
export interface Props {
|
|
4
|
+
/** Invoice data */
|
|
5
|
+
invoice: InvoiceData;
|
|
6
|
+
/** Currency code */
|
|
7
|
+
currency?: 'CAD' | 'USD';
|
|
8
|
+
/** Navigation href */
|
|
9
|
+
href?: string;
|
|
10
|
+
/** Click handler */
|
|
11
|
+
onclick?: () => void;
|
|
12
|
+
}
|
|
13
|
+
declare const InvoiceCard: import("svelte").Component<Props, {}, "">;
|
|
14
|
+
type InvoiceCard = ReturnType<typeof InvoiceCard>;
|
|
15
|
+
export default InvoiceCard;
|
|
16
|
+
//# sourceMappingURL=InvoiceCard.svelte.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InvoiceCard.svelte.d.ts","sourceRoot":"","sources":["../../../src/svelte/components/InvoiceCard.svelte.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,WAAW,EAAiB,MAAM,aAAa,CAAC;AAG9D,sCAAsC;AACtC,MAAM,WAAW,KAAK;IACpB,mBAAmB;IACnB,OAAO,EAAE,WAAW,CAAC;IACrB,oBAAoB;IACpB,QAAQ,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC;IACzB,sBAAsB;IACtB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,oBAAoB;IACpB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB;AAkHD,QAAA,MAAM,WAAW,2CAAwC,CAAC;AAC1D,KAAK,WAAW,GAAG,UAAU,CAAC,OAAO,WAAW,CAAC,CAAC;AAClD,eAAe,WAAW,CAAC"}
|