@spaceinvoices/react-ui 0.4.5 → 0.4.7
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/cli/dist/index.js +1 -1
- package/package.json +1 -1
- package/src/components/advance-invoices/advance-invoices.hooks.ts +2 -2
- package/src/components/advance-invoices/create/create-advance-invoice-form.tsx +146 -74
- package/src/components/advance-invoices/create/locales/de.ts +5 -0
- package/src/components/advance-invoices/create/locales/es.ts +5 -0
- package/src/components/advance-invoices/create/locales/fr.ts +5 -0
- package/src/components/advance-invoices/create/locales/hr.ts +5 -0
- package/src/components/advance-invoices/create/locales/it.ts +5 -0
- package/src/components/advance-invoices/create/locales/nl.ts +5 -0
- package/src/components/advance-invoices/create/locales/pl.ts +5 -0
- package/src/components/advance-invoices/create/locales/pt.ts +5 -0
- package/src/components/advance-invoices/create/locales/sl.ts +5 -0
- package/src/components/advance-invoices/create/prepare-advance-invoice-submission.ts +5 -5
- package/src/components/credit-notes/create/create-credit-note-form.tsx +138 -72
- package/src/components/credit-notes/create/locales/de.ts +5 -0
- package/src/components/credit-notes/create/locales/es.ts +5 -0
- package/src/components/credit-notes/create/locales/fr.ts +5 -0
- package/src/components/credit-notes/create/locales/hr.ts +5 -0
- package/src/components/credit-notes/create/locales/it.ts +5 -0
- package/src/components/credit-notes/create/locales/nl.ts +5 -0
- package/src/components/credit-notes/create/locales/pl.ts +5 -0
- package/src/components/credit-notes/create/locales/pt.ts +5 -0
- package/src/components/credit-notes/create/locales/sl.ts +5 -0
- package/src/components/credit-notes/credit-notes.hooks.ts +2 -2
- package/src/components/dashboard/collection-rate-card/use-collection-rate.ts +48 -92
- package/src/components/dashboard/invoice-status-chart/use-invoice-status.ts +48 -82
- package/src/components/dashboard/payment-methods-chart/use-payment-methods.ts +22 -31
- package/src/components/dashboard/payment-trend-chart/use-payment-trend.ts +33 -48
- package/src/components/dashboard/revenue-trend-chart/use-revenue-trend.ts +56 -76
- package/src/components/dashboard/shared/index.ts +1 -1
- package/src/components/dashboard/shared/use-revenue-data.ts +106 -182
- package/src/components/dashboard/shared/use-stats-counts.ts +18 -68
- package/src/components/dashboard/shared/use-stats-query.ts +35 -5
- package/src/components/dashboard/tax-collected-card/use-tax-collected.ts +57 -75
- package/src/components/dashboard/top-customers-chart/use-top-customers.ts +38 -49
- package/src/components/delivery-notes/create/create-delivery-note-form.tsx +50 -2
- package/src/components/delivery-notes/create/locales/de.ts +5 -0
- package/src/components/delivery-notes/create/locales/es.ts +5 -0
- package/src/components/delivery-notes/create/locales/fr.ts +5 -0
- package/src/components/delivery-notes/create/locales/hr.ts +5 -0
- package/src/components/delivery-notes/create/locales/it.ts +5 -0
- package/src/components/delivery-notes/create/locales/nl.ts +5 -0
- package/src/components/delivery-notes/create/locales/pl.ts +5 -0
- package/src/components/delivery-notes/create/locales/pt.ts +5 -0
- package/src/components/delivery-notes/create/locales/sl.ts +5 -0
- package/src/components/documents/create/document-details-section.tsx +478 -350
- package/src/components/documents/create/document-recipient-section.tsx +30 -1
- package/src/components/documents/create/live-preview.tsx +15 -28
- package/src/components/documents/create/prepare-document-submission.ts +4 -1
- package/src/components/documents/create/smart-code-insert-button.tsx +6 -0
- package/src/components/documents/create/use-document-customer-form.ts +4 -0
- package/src/components/documents/shared/document-preview-skeleton.tsx +63 -0
- package/src/components/documents/shared/index.ts +1 -0
- package/src/components/documents/view/document-actions-bar.tsx +29 -7
- package/src/components/documents/view/document-details-card.tsx +6 -0
- package/src/components/documents/view/locales/de.ts +1 -0
- package/src/components/documents/view/locales/es.ts +1 -0
- package/src/components/documents/view/locales/fr.ts +1 -0
- package/src/components/documents/view/locales/hr.ts +1 -0
- package/src/components/documents/view/locales/it.ts +1 -0
- package/src/components/documents/view/locales/nl.ts +1 -0
- package/src/components/documents/view/locales/pl.ts +1 -0
- package/src/components/documents/view/locales/pt.ts +1 -0
- package/src/components/documents/view/locales/sl.ts +1 -0
- package/src/components/entities/entity-settings-form/email-template-variables-info.tsx +6 -0
- package/src/components/entities/entity-settings-form/input-with-preview.tsx +2 -145
- package/src/components/entities/entity-settings-form/locales/de.ts +4 -0
- package/src/components/entities/entity-settings-form/locales/es.ts +4 -0
- package/src/components/entities/entity-settings-form/locales/fr.ts +4 -0
- package/src/components/entities/entity-settings-form/locales/hr.ts +4 -0
- package/src/components/entities/entity-settings-form/locales/it.ts +4 -0
- package/src/components/entities/entity-settings-form/locales/nl.ts +4 -0
- package/src/components/entities/entity-settings-form/locales/pl.ts +4 -0
- package/src/components/entities/entity-settings-form/locales/pt.ts +4 -0
- package/src/components/entities/entity-settings-form/locales/sl.ts +4 -0
- package/src/components/entities/fina-settings-form/fina-settings-form.tsx +15 -0
- package/src/components/entities/fina-settings-form/fina-settings.hooks.ts +5 -1
- package/src/components/entities/fina-settings-form/locales/de.ts +3 -0
- package/src/components/entities/fina-settings-form/locales/en.ts +3 -0
- package/src/components/entities/fina-settings-form/locales/es.ts +3 -0
- package/src/components/entities/fina-settings-form/locales/fr.ts +3 -0
- package/src/components/entities/fina-settings-form/locales/hr.ts +3 -0
- package/src/components/entities/fina-settings-form/locales/it.ts +3 -0
- package/src/components/entities/fina-settings-form/locales/nl.ts +3 -0
- package/src/components/entities/fina-settings-form/locales/pl.ts +3 -0
- package/src/components/entities/fina-settings-form/locales/pt.ts +3 -0
- package/src/components/entities/fina-settings-form/locales/sl.ts +3 -0
- package/src/components/entities/fina-settings-form/sections/premises-management-section.tsx +4 -4
- package/src/components/entities/fina-settings-form/sections/register-premise-dialog.tsx +3 -3
- package/src/components/entities/settings/defaults-settings-form.tsx +38 -1
- package/src/components/entities/settings/tax-rules-settings-form.tsx +32 -15
- package/src/components/estimates/create/create-estimate-form.tsx +46 -4
- package/src/components/estimates/create/locales/de.ts +5 -0
- package/src/components/estimates/create/locales/es.ts +5 -0
- package/src/components/estimates/create/locales/fr.ts +5 -0
- package/src/components/estimates/create/locales/hr.ts +5 -0
- package/src/components/estimates/create/locales/it.ts +5 -0
- package/src/components/estimates/create/locales/nl.ts +5 -0
- package/src/components/estimates/create/locales/pl.ts +5 -0
- package/src/components/estimates/create/locales/pt.ts +5 -0
- package/src/components/estimates/create/locales/sl.ts +5 -0
- package/src/components/invoices/create/create-invoice-form.tsx +258 -96
- package/src/components/invoices/create/locales/de.ts +19 -0
- package/src/components/invoices/create/locales/es.ts +19 -0
- package/src/components/invoices/create/locales/fr.ts +19 -0
- package/src/components/invoices/create/locales/hr.ts +19 -0
- package/src/components/invoices/create/locales/it.ts +19 -0
- package/src/components/invoices/create/locales/nl.ts +19 -0
- package/src/components/invoices/create/locales/pl.ts +19 -0
- package/src/components/invoices/create/locales/pt.ts +19 -0
- package/src/components/invoices/create/locales/sl.ts +19 -0
- package/src/components/invoices/create/prepare-invoice-submission.ts +5 -5
- package/src/components/invoices/invoices.hooks.ts +3 -3
- package/src/components/table/table-pagination.tsx +1 -1
- package/src/components/ui/progress.tsx +27 -0
- package/src/generate-schemas.ts +15 -2
- package/src/generated/schemas/advanceinvoice.ts +4 -0
- package/src/generated/schemas/creditnote.ts +3 -0
- package/src/generated/schemas/customer.ts +2 -0
- package/src/generated/schemas/deliverynote.ts +3 -0
- package/src/generated/schemas/entity.ts +14 -4
- package/src/generated/schemas/entityapikey.ts +19 -0
- package/src/generated/schemas/estimate.ts +4 -0
- package/src/generated/schemas/finasettings.ts +4 -3
- package/src/generated/schemas/index.ts +1 -0
- package/src/generated/schemas/invoice.ts +4 -0
- package/src/generated/schemas/renderadvanceinvoicepreview_body.ts +17 -11
- package/src/generated/schemas/rendercreditnotepreview_body.ts +17 -11
- package/src/generated/schemas/renderdeliverynotepreview_body.ts +15 -8
- package/src/generated/schemas/renderestimatepreview_body.ts +15 -8
- package/src/generated/schemas/renderinvoicepreview_body.ts +17 -11
- package/src/generated/schemas/startpdfexport_body.ts +12 -5
- package/src/generated/schemas/webhook.ts +4 -0
- package/src/hooks/use-transaction-type-check.ts +152 -0
- package/src/hooks/use-vies-check.ts +7 -131
- package/src/lib/template-variables.tsx +167 -0
- package/src/providers/entities-context.tsx +2 -2
|
@@ -1,133 +1,9 @@
|
|
|
1
|
-
import type { ViesCheckRequest, ViesCheckResponse } from "@spaceinvoices/js-sdk";
|
|
2
|
-
import { useQuery } from "@tanstack/react-query";
|
|
3
|
-
import { useMemo } from "react";
|
|
4
|
-
import { useSDK } from "../providers/sdk-provider";
|
|
5
|
-
import { useDebounce } from "./use-debounce";
|
|
6
|
-
|
|
7
|
-
export const VIES_CHECK_CACHE_KEY = "vies-check";
|
|
8
|
-
|
|
9
|
-
/** Debounce delay for VIES checks (ms) */
|
|
10
|
-
const VIES_DEBOUNCE_DELAY = 500;
|
|
11
|
-
|
|
12
|
-
/** Cache time for VIES results - 5 minutes */
|
|
13
|
-
const VIES_STALE_TIME = 5 * 60 * 1000;
|
|
14
|
-
|
|
15
|
-
export interface UseViesCheckParams {
|
|
16
|
-
/** Issuer country name or code */
|
|
17
|
-
issuerCountry?: string | null;
|
|
18
|
-
/** Issuer country code (takes precedence over country name) */
|
|
19
|
-
issuerCountryCode?: string | null;
|
|
20
|
-
/** Whether the issuer is a tax subject (default: true) */
|
|
21
|
-
isTaxSubject?: boolean;
|
|
22
|
-
/** Customer country name or code */
|
|
23
|
-
customerCountry?: string | null;
|
|
24
|
-
/** Customer country code (takes precedence over country name) */
|
|
25
|
-
customerCountryCode?: string | null;
|
|
26
|
-
/** Customer VAT/tax number */
|
|
27
|
-
customerTaxNumber?: string | null;
|
|
28
|
-
/** Whether to enable the query (default: true) */
|
|
29
|
-
enabled?: boolean;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export interface UseViesCheckResult {
|
|
33
|
-
/** The VIES check result */
|
|
34
|
-
data: ViesCheckResponse | undefined;
|
|
35
|
-
/** Whether the query is loading */
|
|
36
|
-
isLoading: boolean;
|
|
37
|
-
/** Whether the query is fetching (includes background refetches) */
|
|
38
|
-
isFetching: boolean;
|
|
39
|
-
/** Error if the query failed */
|
|
40
|
-
error: Error | null;
|
|
41
|
-
/** Whether reverse charge should be applied */
|
|
42
|
-
reverseChargeApplies: boolean;
|
|
43
|
-
/** Transaction type determined by VIES check */
|
|
44
|
-
transactionType: ViesCheckResponse["transaction_type"] | undefined;
|
|
45
|
-
/** Customer country code returned by VIES check */
|
|
46
|
-
customerCountryCode: string | null;
|
|
47
|
-
/** Warning message from VIES validation */
|
|
48
|
-
warning: string | null;
|
|
49
|
-
/** Whether VIES validation was successful */
|
|
50
|
-
viesValid: boolean | null;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
1
|
/**
|
|
54
|
-
*
|
|
55
|
-
* Uses debouncing to avoid excessive API calls during typing.
|
|
56
|
-
*
|
|
57
|
-
* @example
|
|
58
|
-
* ```tsx
|
|
59
|
-
* const { reverseChargeApplies, transactionType, warning } = useViesCheck({
|
|
60
|
-
* issuerCountryCode: entity.country_code,
|
|
61
|
-
* isTaxSubject: entity.is_tax_subject,
|
|
62
|
-
* customerCountry: customer.country,
|
|
63
|
-
* customerTaxNumber: customer.tax_number,
|
|
64
|
-
* });
|
|
65
|
-
*
|
|
66
|
-
* // Disable taxes when reverse charge applies
|
|
67
|
-
* if (reverseChargeApplies) {
|
|
68
|
-
* // Show reverse charge message and disable tax controls
|
|
69
|
-
* }
|
|
70
|
-
* ```
|
|
2
|
+
* @deprecated Use use-transaction-type-check.ts instead
|
|
71
3
|
*/
|
|
72
|
-
export
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
customerTaxNumber,
|
|
79
|
-
enabled = true,
|
|
80
|
-
}: UseViesCheckParams): UseViesCheckResult {
|
|
81
|
-
const { sdk } = useSDK();
|
|
82
|
-
|
|
83
|
-
// Build the request object
|
|
84
|
-
const requestData = useMemo((): ViesCheckRequest | null => {
|
|
85
|
-
// Need at least issuer info to make a check
|
|
86
|
-
if (!issuerCountry && !issuerCountryCode) return null;
|
|
87
|
-
|
|
88
|
-
// Need at least some customer info to make a meaningful check
|
|
89
|
-
if (!customerCountry && !customerCountryCode && !customerTaxNumber) return null;
|
|
90
|
-
|
|
91
|
-
return {
|
|
92
|
-
issuer: {
|
|
93
|
-
country: issuerCountry || undefined,
|
|
94
|
-
country_code: issuerCountryCode || undefined,
|
|
95
|
-
is_tax_subject: isTaxSubject,
|
|
96
|
-
},
|
|
97
|
-
customer: {
|
|
98
|
-
country: customerCountry || undefined,
|
|
99
|
-
country_code: customerCountryCode || undefined,
|
|
100
|
-
tax_number: customerTaxNumber || undefined,
|
|
101
|
-
},
|
|
102
|
-
};
|
|
103
|
-
}, [issuerCountry, issuerCountryCode, isTaxSubject, customerCountry, customerCountryCode, customerTaxNumber]);
|
|
104
|
-
|
|
105
|
-
// Debounce the request data to avoid excessive API calls
|
|
106
|
-
const debouncedRequest = useDebounce(requestData, VIES_DEBOUNCE_DELAY);
|
|
107
|
-
|
|
108
|
-
// Create a stable query key
|
|
109
|
-
const queryKey = useMemo(() => [VIES_CHECK_CACHE_KEY, debouncedRequest], [debouncedRequest]);
|
|
110
|
-
|
|
111
|
-
const query = useQuery({
|
|
112
|
-
queryKey,
|
|
113
|
-
queryFn: async () => {
|
|
114
|
-
if (!debouncedRequest) throw new Error("No request data");
|
|
115
|
-
return sdk.vies.checkVies(debouncedRequest);
|
|
116
|
-
},
|
|
117
|
-
enabled: enabled && !!sdk && !!debouncedRequest,
|
|
118
|
-
staleTime: VIES_STALE_TIME,
|
|
119
|
-
refetchOnWindowFocus: false,
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
return {
|
|
123
|
-
data: query.data,
|
|
124
|
-
isLoading: query.isLoading,
|
|
125
|
-
isFetching: query.isFetching,
|
|
126
|
-
error: query.error,
|
|
127
|
-
reverseChargeApplies: query.data?.reverse_charge_applies ?? false,
|
|
128
|
-
transactionType: query.data?.transaction_type,
|
|
129
|
-
customerCountryCode: query.data?.customer_country_code ?? null,
|
|
130
|
-
warning: query.data?.warning ?? null,
|
|
131
|
-
viesValid: query.data?.vies_valid ?? null,
|
|
132
|
-
};
|
|
133
|
-
}
|
|
4
|
+
export {
|
|
5
|
+
TRANSACTION_TYPE_CHECK_CACHE_KEY as VIES_CHECK_CACHE_KEY,
|
|
6
|
+
type UseTransactionTypeCheckParams as UseViesCheckParams,
|
|
7
|
+
type UseTransactionTypeCheckResult as UseViesCheckResult,
|
|
8
|
+
useTransactionTypeCheck as useViesCheck,
|
|
9
|
+
} from "./use-transaction-type-check";
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import type { Entity, Estimate, Invoice } from "@spaceinvoices/js-sdk";
|
|
2
|
+
import { cn } from "@/ui/lib/utils";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Convert snake_case variable name to Title Case for display
|
|
6
|
+
* e.g., "document_number" -> "Document Number", "bank_account.iban" -> "Bank Account Iban"
|
|
7
|
+
*/
|
|
8
|
+
export function formatVariableName(varName: string): string {
|
|
9
|
+
return varName
|
|
10
|
+
.replace(/\./g, "_")
|
|
11
|
+
.split("_")
|
|
12
|
+
.map((word) => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
|
|
13
|
+
.join(" ");
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Resolve a template variable to its actual value from entity/document data.
|
|
18
|
+
* Returns null if the variable can't be resolved (shown as a placeholder in preview).
|
|
19
|
+
*/
|
|
20
|
+
export function getVariableValue(
|
|
21
|
+
varName: string,
|
|
22
|
+
entity?: Entity | null,
|
|
23
|
+
document?: Partial<Invoice | Estimate> | null,
|
|
24
|
+
): string | null {
|
|
25
|
+
if (!entity) return null;
|
|
26
|
+
|
|
27
|
+
// Entity-related variables
|
|
28
|
+
if (varName === "entity_name") return entity.name || null;
|
|
29
|
+
if (varName === "entity_email") return (entity.settings as any)?.email || null;
|
|
30
|
+
if (varName === "entity_address") return entity.address || null;
|
|
31
|
+
if (varName === "entity_post_code") return entity.post_code || null;
|
|
32
|
+
if (varName === "entity_city") return entity.city || null;
|
|
33
|
+
if (varName === "entity_country") return entity.country || null;
|
|
34
|
+
if (varName === "entity_tax_number") return entity.tax_number || null;
|
|
35
|
+
if (varName === "entity_company_number") return entity.company_number || null;
|
|
36
|
+
|
|
37
|
+
// Date variables
|
|
38
|
+
if (varName === "current_date") {
|
|
39
|
+
return new Date().toLocaleDateString("en-US", { month: "long", day: "numeric", year: "numeric" });
|
|
40
|
+
}
|
|
41
|
+
if (varName === "current_year") return new Date().getFullYear().toString();
|
|
42
|
+
|
|
43
|
+
// Document-specific variables
|
|
44
|
+
if (document) {
|
|
45
|
+
if (varName === "document_number") return (document as any).number || null;
|
|
46
|
+
if (varName === "document_date" && (document as any).date) {
|
|
47
|
+
return new Date((document as any).date).toLocaleDateString("en-US", {
|
|
48
|
+
month: "long",
|
|
49
|
+
day: "numeric",
|
|
50
|
+
year: "numeric",
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
if (varName === "document_total" && (document as any).total_with_tax) {
|
|
54
|
+
return new Intl.NumberFormat("en-US", {
|
|
55
|
+
style: "currency",
|
|
56
|
+
currency: (document as any).currency_code || "USD",
|
|
57
|
+
}).format(Number((document as any).total_with_tax));
|
|
58
|
+
}
|
|
59
|
+
if (varName === "document_currency") return (document as any).currency_code || null;
|
|
60
|
+
|
|
61
|
+
// Invoice due date
|
|
62
|
+
if ("date_due" in document && varName === "document_due_date") {
|
|
63
|
+
return document.date_due
|
|
64
|
+
? new Date(document.date_due).toLocaleDateString("en-US", {
|
|
65
|
+
month: "long",
|
|
66
|
+
day: "numeric",
|
|
67
|
+
year: "numeric",
|
|
68
|
+
})
|
|
69
|
+
: null;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Estimate valid until
|
|
73
|
+
if ("date_valid_till" in document && varName === "document_valid_until") {
|
|
74
|
+
return document.date_valid_till
|
|
75
|
+
? new Date(document.date_valid_till).toLocaleDateString("en-US", {
|
|
76
|
+
month: "long",
|
|
77
|
+
day: "numeric",
|
|
78
|
+
year: "numeric",
|
|
79
|
+
})
|
|
80
|
+
: null;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Customer variables
|
|
84
|
+
if ((document as any).customer) {
|
|
85
|
+
if (varName === "customer_name") return (document as any).customer.name || null;
|
|
86
|
+
if (varName === "customer_email") return (document as any).customer.email || null;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Bank account variables (from entity settings)
|
|
91
|
+
const bankAccounts = (entity.settings as any)?.bank_accounts as
|
|
92
|
+
| Array<{
|
|
93
|
+
iban?: string;
|
|
94
|
+
bank_name?: string;
|
|
95
|
+
bic?: string;
|
|
96
|
+
account_number?: string;
|
|
97
|
+
routing_number?: string;
|
|
98
|
+
sort_code?: string;
|
|
99
|
+
is_default?: boolean;
|
|
100
|
+
}>
|
|
101
|
+
| undefined;
|
|
102
|
+
const bankAccount = bankAccounts?.find((acc) => acc.is_default) ?? bankAccounts?.[0];
|
|
103
|
+
|
|
104
|
+
if (varName === "bank_account" && bankAccount) {
|
|
105
|
+
const lines: string[] = [];
|
|
106
|
+
if (bankAccount.bank_name) lines.push(bankAccount.bank_name);
|
|
107
|
+
if (bankAccount.iban) lines.push(`IBAN: ${bankAccount.iban}`);
|
|
108
|
+
else if (bankAccount.account_number) lines.push(`Account: ${bankAccount.account_number}`);
|
|
109
|
+
if (bankAccount.bic) lines.push(`BIC: ${bankAccount.bic}`);
|
|
110
|
+
return lines.join(", ") || null;
|
|
111
|
+
}
|
|
112
|
+
if (varName === "bank_account.iban") return bankAccount?.iban || null;
|
|
113
|
+
if (varName === "bank_account.bank_name") return bankAccount?.bank_name || null;
|
|
114
|
+
if (varName === "bank_account.bic") return bankAccount?.bic || null;
|
|
115
|
+
if (varName === "bank_account.account_number") return bankAccount?.account_number || null;
|
|
116
|
+
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Replace template variables in a string with styled React nodes for preview.
|
|
122
|
+
* Resolved values get a secondary bg, unresolved show as primary-colored placeholders.
|
|
123
|
+
*/
|
|
124
|
+
export function replaceTemplateVariablesForPreview(
|
|
125
|
+
template: string,
|
|
126
|
+
entity?: Entity | null,
|
|
127
|
+
document?: Partial<Invoice | Estimate> | null,
|
|
128
|
+
): React.ReactNode[] {
|
|
129
|
+
if (!template) return [];
|
|
130
|
+
|
|
131
|
+
const parts: React.ReactNode[] = [];
|
|
132
|
+
const regex = /\{([^}]+)\}/g;
|
|
133
|
+
let lastIndex = 0;
|
|
134
|
+
let match: RegExpExecArray | null = null;
|
|
135
|
+
|
|
136
|
+
match = regex.exec(template);
|
|
137
|
+
while (match !== null) {
|
|
138
|
+
if (match.index > lastIndex) {
|
|
139
|
+
parts.push(template.slice(lastIndex, match.index));
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
const varName = match[1];
|
|
143
|
+
const actualValue = getVariableValue(varName, entity, document);
|
|
144
|
+
const displayValue = actualValue || formatVariableName(varName);
|
|
145
|
+
|
|
146
|
+
parts.push(
|
|
147
|
+
<span
|
|
148
|
+
key={match.index}
|
|
149
|
+
className={cn(
|
|
150
|
+
"rounded px-1.5 py-0.5 font-medium text-xs",
|
|
151
|
+
actualValue ? "bg-secondary text-secondary-foreground" : "bg-primary/10 text-primary",
|
|
152
|
+
)}
|
|
153
|
+
>
|
|
154
|
+
{displayValue}
|
|
155
|
+
</span>,
|
|
156
|
+
);
|
|
157
|
+
|
|
158
|
+
lastIndex = regex.lastIndex;
|
|
159
|
+
match = regex.exec(template);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (lastIndex < template.length) {
|
|
163
|
+
parts.push(template.slice(lastIndex));
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return parts;
|
|
167
|
+
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { Entity as SDKEntity } from "@spaceinvoices/js-sdk";
|
|
2
2
|
|
|
3
3
|
import { createContext, useContext } from "react";
|
|
4
4
|
|
|
5
5
|
/** Entity type with country_rules included (from getEntities response) */
|
|
6
|
-
export type Entity =
|
|
6
|
+
export type Entity = SDKEntity;
|
|
7
7
|
|
|
8
8
|
export type EntityEnvironment = "live" | "sandbox";
|
|
9
9
|
|