@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.
Files changed (138) hide show
  1. package/cli/dist/index.js +1 -1
  2. package/package.json +1 -1
  3. package/src/components/advance-invoices/advance-invoices.hooks.ts +2 -2
  4. package/src/components/advance-invoices/create/create-advance-invoice-form.tsx +146 -74
  5. package/src/components/advance-invoices/create/locales/de.ts +5 -0
  6. package/src/components/advance-invoices/create/locales/es.ts +5 -0
  7. package/src/components/advance-invoices/create/locales/fr.ts +5 -0
  8. package/src/components/advance-invoices/create/locales/hr.ts +5 -0
  9. package/src/components/advance-invoices/create/locales/it.ts +5 -0
  10. package/src/components/advance-invoices/create/locales/nl.ts +5 -0
  11. package/src/components/advance-invoices/create/locales/pl.ts +5 -0
  12. package/src/components/advance-invoices/create/locales/pt.ts +5 -0
  13. package/src/components/advance-invoices/create/locales/sl.ts +5 -0
  14. package/src/components/advance-invoices/create/prepare-advance-invoice-submission.ts +5 -5
  15. package/src/components/credit-notes/create/create-credit-note-form.tsx +138 -72
  16. package/src/components/credit-notes/create/locales/de.ts +5 -0
  17. package/src/components/credit-notes/create/locales/es.ts +5 -0
  18. package/src/components/credit-notes/create/locales/fr.ts +5 -0
  19. package/src/components/credit-notes/create/locales/hr.ts +5 -0
  20. package/src/components/credit-notes/create/locales/it.ts +5 -0
  21. package/src/components/credit-notes/create/locales/nl.ts +5 -0
  22. package/src/components/credit-notes/create/locales/pl.ts +5 -0
  23. package/src/components/credit-notes/create/locales/pt.ts +5 -0
  24. package/src/components/credit-notes/create/locales/sl.ts +5 -0
  25. package/src/components/credit-notes/credit-notes.hooks.ts +2 -2
  26. package/src/components/dashboard/collection-rate-card/use-collection-rate.ts +48 -92
  27. package/src/components/dashboard/invoice-status-chart/use-invoice-status.ts +48 -82
  28. package/src/components/dashboard/payment-methods-chart/use-payment-methods.ts +22 -31
  29. package/src/components/dashboard/payment-trend-chart/use-payment-trend.ts +33 -48
  30. package/src/components/dashboard/revenue-trend-chart/use-revenue-trend.ts +56 -76
  31. package/src/components/dashboard/shared/index.ts +1 -1
  32. package/src/components/dashboard/shared/use-revenue-data.ts +106 -182
  33. package/src/components/dashboard/shared/use-stats-counts.ts +18 -68
  34. package/src/components/dashboard/shared/use-stats-query.ts +35 -5
  35. package/src/components/dashboard/tax-collected-card/use-tax-collected.ts +57 -75
  36. package/src/components/dashboard/top-customers-chart/use-top-customers.ts +38 -49
  37. package/src/components/delivery-notes/create/create-delivery-note-form.tsx +50 -2
  38. package/src/components/delivery-notes/create/locales/de.ts +5 -0
  39. package/src/components/delivery-notes/create/locales/es.ts +5 -0
  40. package/src/components/delivery-notes/create/locales/fr.ts +5 -0
  41. package/src/components/delivery-notes/create/locales/hr.ts +5 -0
  42. package/src/components/delivery-notes/create/locales/it.ts +5 -0
  43. package/src/components/delivery-notes/create/locales/nl.ts +5 -0
  44. package/src/components/delivery-notes/create/locales/pl.ts +5 -0
  45. package/src/components/delivery-notes/create/locales/pt.ts +5 -0
  46. package/src/components/delivery-notes/create/locales/sl.ts +5 -0
  47. package/src/components/documents/create/document-details-section.tsx +478 -350
  48. package/src/components/documents/create/document-recipient-section.tsx +30 -1
  49. package/src/components/documents/create/live-preview.tsx +15 -28
  50. package/src/components/documents/create/prepare-document-submission.ts +4 -1
  51. package/src/components/documents/create/smart-code-insert-button.tsx +6 -0
  52. package/src/components/documents/create/use-document-customer-form.ts +4 -0
  53. package/src/components/documents/shared/document-preview-skeleton.tsx +63 -0
  54. package/src/components/documents/shared/index.ts +1 -0
  55. package/src/components/documents/view/document-actions-bar.tsx +29 -7
  56. package/src/components/documents/view/document-details-card.tsx +6 -0
  57. package/src/components/documents/view/locales/de.ts +1 -0
  58. package/src/components/documents/view/locales/es.ts +1 -0
  59. package/src/components/documents/view/locales/fr.ts +1 -0
  60. package/src/components/documents/view/locales/hr.ts +1 -0
  61. package/src/components/documents/view/locales/it.ts +1 -0
  62. package/src/components/documents/view/locales/nl.ts +1 -0
  63. package/src/components/documents/view/locales/pl.ts +1 -0
  64. package/src/components/documents/view/locales/pt.ts +1 -0
  65. package/src/components/documents/view/locales/sl.ts +1 -0
  66. package/src/components/entities/entity-settings-form/email-template-variables-info.tsx +6 -0
  67. package/src/components/entities/entity-settings-form/input-with-preview.tsx +2 -145
  68. package/src/components/entities/entity-settings-form/locales/de.ts +4 -0
  69. package/src/components/entities/entity-settings-form/locales/es.ts +4 -0
  70. package/src/components/entities/entity-settings-form/locales/fr.ts +4 -0
  71. package/src/components/entities/entity-settings-form/locales/hr.ts +4 -0
  72. package/src/components/entities/entity-settings-form/locales/it.ts +4 -0
  73. package/src/components/entities/entity-settings-form/locales/nl.ts +4 -0
  74. package/src/components/entities/entity-settings-form/locales/pl.ts +4 -0
  75. package/src/components/entities/entity-settings-form/locales/pt.ts +4 -0
  76. package/src/components/entities/entity-settings-form/locales/sl.ts +4 -0
  77. package/src/components/entities/fina-settings-form/fina-settings-form.tsx +15 -0
  78. package/src/components/entities/fina-settings-form/fina-settings.hooks.ts +5 -1
  79. package/src/components/entities/fina-settings-form/locales/de.ts +3 -0
  80. package/src/components/entities/fina-settings-form/locales/en.ts +3 -0
  81. package/src/components/entities/fina-settings-form/locales/es.ts +3 -0
  82. package/src/components/entities/fina-settings-form/locales/fr.ts +3 -0
  83. package/src/components/entities/fina-settings-form/locales/hr.ts +3 -0
  84. package/src/components/entities/fina-settings-form/locales/it.ts +3 -0
  85. package/src/components/entities/fina-settings-form/locales/nl.ts +3 -0
  86. package/src/components/entities/fina-settings-form/locales/pl.ts +3 -0
  87. package/src/components/entities/fina-settings-form/locales/pt.ts +3 -0
  88. package/src/components/entities/fina-settings-form/locales/sl.ts +3 -0
  89. package/src/components/entities/fina-settings-form/sections/premises-management-section.tsx +4 -4
  90. package/src/components/entities/fina-settings-form/sections/register-premise-dialog.tsx +3 -3
  91. package/src/components/entities/settings/defaults-settings-form.tsx +38 -1
  92. package/src/components/entities/settings/tax-rules-settings-form.tsx +32 -15
  93. package/src/components/estimates/create/create-estimate-form.tsx +46 -4
  94. package/src/components/estimates/create/locales/de.ts +5 -0
  95. package/src/components/estimates/create/locales/es.ts +5 -0
  96. package/src/components/estimates/create/locales/fr.ts +5 -0
  97. package/src/components/estimates/create/locales/hr.ts +5 -0
  98. package/src/components/estimates/create/locales/it.ts +5 -0
  99. package/src/components/estimates/create/locales/nl.ts +5 -0
  100. package/src/components/estimates/create/locales/pl.ts +5 -0
  101. package/src/components/estimates/create/locales/pt.ts +5 -0
  102. package/src/components/estimates/create/locales/sl.ts +5 -0
  103. package/src/components/invoices/create/create-invoice-form.tsx +258 -96
  104. package/src/components/invoices/create/locales/de.ts +19 -0
  105. package/src/components/invoices/create/locales/es.ts +19 -0
  106. package/src/components/invoices/create/locales/fr.ts +19 -0
  107. package/src/components/invoices/create/locales/hr.ts +19 -0
  108. package/src/components/invoices/create/locales/it.ts +19 -0
  109. package/src/components/invoices/create/locales/nl.ts +19 -0
  110. package/src/components/invoices/create/locales/pl.ts +19 -0
  111. package/src/components/invoices/create/locales/pt.ts +19 -0
  112. package/src/components/invoices/create/locales/sl.ts +19 -0
  113. package/src/components/invoices/create/prepare-invoice-submission.ts +5 -5
  114. package/src/components/invoices/invoices.hooks.ts +3 -3
  115. package/src/components/table/table-pagination.tsx +1 -1
  116. package/src/components/ui/progress.tsx +27 -0
  117. package/src/generate-schemas.ts +15 -2
  118. package/src/generated/schemas/advanceinvoice.ts +4 -0
  119. package/src/generated/schemas/creditnote.ts +3 -0
  120. package/src/generated/schemas/customer.ts +2 -0
  121. package/src/generated/schemas/deliverynote.ts +3 -0
  122. package/src/generated/schemas/entity.ts +14 -4
  123. package/src/generated/schemas/entityapikey.ts +19 -0
  124. package/src/generated/schemas/estimate.ts +4 -0
  125. package/src/generated/schemas/finasettings.ts +4 -3
  126. package/src/generated/schemas/index.ts +1 -0
  127. package/src/generated/schemas/invoice.ts +4 -0
  128. package/src/generated/schemas/renderadvanceinvoicepreview_body.ts +17 -11
  129. package/src/generated/schemas/rendercreditnotepreview_body.ts +17 -11
  130. package/src/generated/schemas/renderdeliverynotepreview_body.ts +15 -8
  131. package/src/generated/schemas/renderestimatepreview_body.ts +15 -8
  132. package/src/generated/schemas/renderinvoicepreview_body.ts +17 -11
  133. package/src/generated/schemas/startpdfexport_body.ts +12 -5
  134. package/src/generated/schemas/webhook.ts +4 -0
  135. package/src/hooks/use-transaction-type-check.ts +152 -0
  136. package/src/hooks/use-vies-check.ts +7 -131
  137. package/src/lib/template-variables.tsx +167 -0
  138. 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
- * Hook to check VIES status for a given issuer/customer combination.
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 function useViesCheck({
73
- issuerCountry,
74
- issuerCountryCode,
75
- isTaxSubject = true,
76
- customerCountry,
77
- customerCountryCode,
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 { GetEntities200DataItem } from "@spaceinvoices/js-sdk";
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 = GetEntities200DataItem;
6
+ export type Entity = SDKEntity;
7
7
 
8
8
  export type EntityEnvironment = "live" | "sandbox";
9
9