@spaceinvoices/react-ui 0.4.5 → 0.4.6

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 (115) 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 +91 -35
  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 +91 -35
  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/delivery-notes/create/create-delivery-note-form.tsx +47 -0
  27. package/src/components/delivery-notes/create/locales/de.ts +5 -0
  28. package/src/components/delivery-notes/create/locales/es.ts +5 -0
  29. package/src/components/delivery-notes/create/locales/fr.ts +5 -0
  30. package/src/components/delivery-notes/create/locales/hr.ts +5 -0
  31. package/src/components/delivery-notes/create/locales/it.ts +5 -0
  32. package/src/components/delivery-notes/create/locales/nl.ts +5 -0
  33. package/src/components/delivery-notes/create/locales/pl.ts +5 -0
  34. package/src/components/delivery-notes/create/locales/pt.ts +5 -0
  35. package/src/components/delivery-notes/create/locales/sl.ts +5 -0
  36. package/src/components/documents/create/document-details-section.tsx +472 -346
  37. package/src/components/documents/create/prepare-document-submission.ts +3 -1
  38. package/src/components/documents/create/smart-code-insert-button.tsx +6 -0
  39. package/src/components/documents/view/document-details-card.tsx +6 -0
  40. package/src/components/documents/view/locales/de.ts +1 -0
  41. package/src/components/documents/view/locales/es.ts +1 -0
  42. package/src/components/documents/view/locales/fr.ts +1 -0
  43. package/src/components/documents/view/locales/hr.ts +1 -0
  44. package/src/components/documents/view/locales/it.ts +1 -0
  45. package/src/components/documents/view/locales/nl.ts +1 -0
  46. package/src/components/documents/view/locales/pl.ts +1 -0
  47. package/src/components/documents/view/locales/pt.ts +1 -0
  48. package/src/components/documents/view/locales/sl.ts +1 -0
  49. package/src/components/entities/entity-settings-form/email-template-variables-info.tsx +6 -0
  50. package/src/components/entities/entity-settings-form/input-with-preview.tsx +2 -145
  51. package/src/components/entities/entity-settings-form/locales/de.ts +4 -0
  52. package/src/components/entities/entity-settings-form/locales/es.ts +4 -0
  53. package/src/components/entities/entity-settings-form/locales/fr.ts +4 -0
  54. package/src/components/entities/entity-settings-form/locales/hr.ts +4 -0
  55. package/src/components/entities/entity-settings-form/locales/it.ts +4 -0
  56. package/src/components/entities/entity-settings-form/locales/nl.ts +4 -0
  57. package/src/components/entities/entity-settings-form/locales/pl.ts +4 -0
  58. package/src/components/entities/entity-settings-form/locales/pt.ts +4 -0
  59. package/src/components/entities/entity-settings-form/locales/sl.ts +4 -0
  60. package/src/components/entities/fina-settings-form/fina-settings-form.tsx +15 -0
  61. package/src/components/entities/fina-settings-form/fina-settings.hooks.ts +5 -1
  62. package/src/components/entities/fina-settings-form/locales/de.ts +3 -0
  63. package/src/components/entities/fina-settings-form/locales/en.ts +3 -0
  64. package/src/components/entities/fina-settings-form/locales/es.ts +3 -0
  65. package/src/components/entities/fina-settings-form/locales/fr.ts +3 -0
  66. package/src/components/entities/fina-settings-form/locales/hr.ts +3 -0
  67. package/src/components/entities/fina-settings-form/locales/it.ts +3 -0
  68. package/src/components/entities/fina-settings-form/locales/nl.ts +3 -0
  69. package/src/components/entities/fina-settings-form/locales/pl.ts +3 -0
  70. package/src/components/entities/fina-settings-form/locales/pt.ts +3 -0
  71. package/src/components/entities/fina-settings-form/locales/sl.ts +3 -0
  72. package/src/components/entities/fina-settings-form/sections/premises-management-section.tsx +4 -4
  73. package/src/components/entities/fina-settings-form/sections/register-premise-dialog.tsx +3 -3
  74. package/src/components/entities/settings/defaults-settings-form.tsx +38 -1
  75. package/src/components/entities/settings/tax-rules-settings-form.tsx +1 -2
  76. package/src/components/estimates/create/create-estimate-form.tsx +43 -2
  77. package/src/components/estimates/create/locales/de.ts +5 -0
  78. package/src/components/estimates/create/locales/es.ts +5 -0
  79. package/src/components/estimates/create/locales/fr.ts +5 -0
  80. package/src/components/estimates/create/locales/hr.ts +5 -0
  81. package/src/components/estimates/create/locales/it.ts +5 -0
  82. package/src/components/estimates/create/locales/nl.ts +5 -0
  83. package/src/components/estimates/create/locales/pl.ts +5 -0
  84. package/src/components/estimates/create/locales/pt.ts +5 -0
  85. package/src/components/estimates/create/locales/sl.ts +5 -0
  86. package/src/components/invoices/create/create-invoice-form.tsx +130 -40
  87. package/src/components/invoices/create/locales/de.ts +13 -0
  88. package/src/components/invoices/create/locales/es.ts +13 -0
  89. package/src/components/invoices/create/locales/fr.ts +13 -0
  90. package/src/components/invoices/create/locales/hr.ts +13 -0
  91. package/src/components/invoices/create/locales/it.ts +13 -0
  92. package/src/components/invoices/create/locales/nl.ts +13 -0
  93. package/src/components/invoices/create/locales/pl.ts +13 -0
  94. package/src/components/invoices/create/locales/pt.ts +13 -0
  95. package/src/components/invoices/create/locales/sl.ts +13 -0
  96. package/src/components/invoices/create/prepare-invoice-submission.ts +5 -5
  97. package/src/components/invoices/invoices.hooks.ts +2 -2
  98. package/src/components/table/table-pagination.tsx +1 -1
  99. package/src/generated/schemas/advanceinvoice.ts +2 -0
  100. package/src/generated/schemas/creditnote.ts +1 -0
  101. package/src/generated/schemas/deliverynote.ts +1 -0
  102. package/src/generated/schemas/entity.ts +4 -4
  103. package/src/generated/schemas/entityapikey.ts +19 -0
  104. package/src/generated/schemas/estimate.ts +2 -0
  105. package/src/generated/schemas/index.ts +1 -0
  106. package/src/generated/schemas/invoice.ts +2 -0
  107. package/src/generated/schemas/renderadvanceinvoicepreview_body.ts +1 -1
  108. package/src/generated/schemas/rendercreditnotepreview_body.ts +1 -1
  109. package/src/generated/schemas/renderdeliverynotepreview_body.ts +1 -1
  110. package/src/generated/schemas/renderestimatepreview_body.ts +1 -1
  111. package/src/generated/schemas/renderinvoicepreview_body.ts +1 -1
  112. package/src/generated/schemas/startpdfexport_body.ts +14 -2
  113. package/src/generated/schemas/webhook.ts +4 -0
  114. package/src/lib/template-variables.tsx +167 -0
  115. package/src/providers/entities-context.tsx +2 -2
@@ -116,8 +116,8 @@ export function setLastUsedFursCombo(entityId: string, combo: FursCombo): void {
116
116
  const FINA_LAST_USED_KEY = "hr:fina:last-used";
117
117
 
118
118
  export type FinaCombo = {
119
- premise_id: string;
120
- device_id: string;
119
+ business_premise_name: string;
120
+ electronic_device_name: string;
121
121
  };
122
122
 
123
123
  export function getLastUsedFinaCombo(entityId: string): FinaCombo | null {
@@ -16,7 +16,7 @@ export function Pagination({ prevCursor, nextCursor, onPageChange }: PaginationP
16
16
  const hasNext = Boolean(nextCursor);
17
17
 
18
18
  return (
19
- <div className="flex items-center justify-end space-x-2">
19
+ <div className="flex items-center justify-start space-x-2">
20
20
  <Button
21
21
  variant="outline"
22
22
  size="sm"
@@ -96,6 +96,7 @@ const createAdvanceInvoiceSchemaDefinition = z.object({
96
96
  ).optional(),
97
97
  note: z.union([z.string(), z.null()]).optional(),
98
98
  tax_clause: z.union([z.string(), z.null()]).optional(),
99
+ footer: z.union([z.string(), z.null()]).optional(),
99
100
  currency_code: z.string().max(3).optional(),
100
101
  metadata: z.union([z.record(z.string(), z.any()), z.null()]).optional(),
101
102
  date_due: z.union([z.string(), z.null()]).optional(),
@@ -228,6 +229,7 @@ const updateAdvanceInvoiceSchemaDefinition = z
228
229
  )
229
230
  .min(1),
230
231
  note: z.union([z.string(), z.null()]),
232
+ footer: z.union([z.string(), z.null()]),
231
233
  currency_code: z.string(),
232
234
  metadata: z.union([z.object({}).partial().passthrough(), z.null()]),
233
235
  change_reason: z.string().max(500),
@@ -110,6 +110,7 @@ const updateCreditNoteSchemaDefinition = z
110
110
  )
111
111
  .min(1),
112
112
  note: z.union([z.string(), z.null()]),
113
+ footer: z.union([z.string(), z.null()]),
113
114
  payment_terms: z.union([z.string(), z.null()]),
114
115
  currency_code: z.string(),
115
116
  metadata: z.union([z.object({}).partial().passthrough(), z.null()]),
@@ -84,6 +84,7 @@ const createDeliveryNoteSchemaDefinition = z.object({
84
84
  note: z.union([z.string(), z.null()]).optional(),
85
85
  payment_terms: z.union([z.string(), z.null()]).optional(),
86
86
  tax_clause: z.union([z.string(), z.null()]).optional(),
87
+ footer: z.union([z.string(), z.null()]).optional(),
87
88
  currency_code: z.string().max(3).optional(),
88
89
  metadata: z.union([z.record(z.string(), z.any()), z.null()]).optional(),
89
90
  hide_prices: z.boolean().optional(),
@@ -30,7 +30,7 @@ const createEntitySchemaDefinition = z.object({
30
30
  settings: z
31
31
  .object({
32
32
  pdf_template: z.union([
33
- z.enum(["modern", "classic", "minimal", "fashion"]),
33
+ z.enum(["modern", "classic", "condensed", "minimal", "fashion"]),
34
34
  z.null(),
35
35
  ]),
36
36
  number_formats: z.union([
@@ -90,7 +90,7 @@ const createEntitySchemaDefinition = z.object({
90
90
  operator_oib: z.string().min(11).max(11),
91
91
  operator_label: z.string(),
92
92
  u_sust_pdv: z.boolean().default(true),
93
- numbering_sequence: z.enum(["N", "P"]).default("N"),
93
+ numbering_sequence: z.enum(["N", "P"]).default("P"),
94
94
  certificate_expiry: z.string(),
95
95
  })
96
96
  .partial()
@@ -181,7 +181,7 @@ const patchEntitySchemaDefinition = z
181
181
  settings: z
182
182
  .object({
183
183
  pdf_template: z.union([
184
- z.enum(["modern", "classic", "minimal", "fashion"]),
184
+ z.enum(["modern", "classic", "condensed", "minimal", "fashion"]),
185
185
  z.null(),
186
186
  ]),
187
187
  number_formats: z.union([
@@ -241,7 +241,7 @@ const patchEntitySchemaDefinition = z
241
241
  operator_oib: z.string().min(11).max(11),
242
242
  operator_label: z.string(),
243
243
  u_sust_pdv: z.boolean().default(true),
244
- numbering_sequence: z.enum(["N", "P"]).default("N"),
244
+ numbering_sequence: z.enum(["N", "P"]).default("P"),
245
245
  certificate_expiry: z.string(),
246
246
  })
247
247
  .partial()
@@ -0,0 +1,19 @@
1
+ /**
2
+ * This file was automatically generated using 'bun generate-schemas'.
3
+ * Do not edit this file manually. To update, run the generator again.
4
+ * @generated
5
+ */
6
+
7
+ import { z } from 'zod';
8
+
9
+ // Schemas for entityapikey endpoints
10
+
11
+ // Schema for create entityapikey operation
12
+ const createEntityApiKeySchemaDefinition = z
13
+ .object({ name: z.string().max(255), ttl: z.number().int().gte(60) })
14
+ .partial();
15
+
16
+ // Type for create entityapikey operation
17
+ export type CreateEntityApiKeySchema = z.infer<typeof createEntityApiKeySchemaDefinition>;
18
+
19
+ export const createEntityApiKeySchema = createEntityApiKeySchemaDefinition;
@@ -86,6 +86,7 @@ const createEstimateSchemaDefinition = z.object({
86
86
  note: z.union([z.string(), z.null()]).optional(),
87
87
  payment_terms: z.union([z.string(), z.null()]).optional(),
88
88
  tax_clause: z.union([z.string(), z.null()]).optional(),
89
+ footer: z.union([z.string(), z.null()]).optional(),
89
90
  currency_code: z.string().max(3).optional(),
90
91
  metadata: z.union([z.record(z.string(), z.any()), z.null()]).optional(),
91
92
  date_valid_till: z.union([z.string(), z.null()]).optional(),
@@ -181,6 +182,7 @@ const updateEstimateSchemaDefinition = z
181
182
  )
182
183
  .min(1),
183
184
  note: z.union([z.string(), z.null()]),
185
+ footer: z.union([z.string(), z.null()]),
184
186
  payment_terms: z.union([z.string(), z.null()]),
185
187
  currency_code: z.string(),
186
188
  metadata: z.union([z.object({}).partial().passthrough(), z.null()]),
@@ -16,6 +16,7 @@ export * from './customestimate';
16
16
  export * from './custominvoice';
17
17
  export * from './deliverynote';
18
18
  export * from './entity';
19
+ export * from './entityapikey';
19
20
  export * from './entityuserrole';
20
21
  export * from './estimate';
21
22
  export * from './finasettings';
@@ -97,6 +97,7 @@ const createInvoiceSchemaDefinition = z.object({
97
97
  note: z.union([z.string(), z.null()]).optional(),
98
98
  payment_terms: z.union([z.string(), z.null()]).optional(),
99
99
  tax_clause: z.union([z.string(), z.null()]).optional(),
100
+ footer: z.union([z.string(), z.null()]).optional(),
100
101
  currency_code: z.string().max(3).optional(),
101
102
  metadata: z.union([z.record(z.string(), z.any()), z.null()]).optional(),
102
103
  date_due: z.union([z.string(), z.null()]).optional(),
@@ -237,6 +238,7 @@ const updateInvoiceSchemaDefinition = z
237
238
  )
238
239
  .min(1),
239
240
  note: z.union([z.string(), z.null()]),
241
+ footer: z.union([z.string(), z.null()]),
240
242
  payment_terms: z.union([z.string(), z.null()]),
241
243
  currency_code: z.string(),
242
244
  metadata: z.union([z.object({}).partial().passthrough(), z.null()]),
@@ -162,7 +162,7 @@ const CreateDocumentItem = z
162
162
  description: z.union([z.string(), z.null()]),
163
163
  price: z.number(),
164
164
  gross_price: z.number(),
165
- quantity: z.union([z.number(), z.null()]),
165
+ quantity: z.number().gte(-140737488355328).lte(140737488355327),
166
166
  unit: z.union([z.string(), z.null()]),
167
167
  taxes: z.array(DocumentItemTax),
168
168
  discounts: z.array(LineDiscount).max(5),
@@ -163,7 +163,7 @@ const CreateDocumentItem = z
163
163
  description: z.union([z.string(), z.null()]),
164
164
  price: z.number(),
165
165
  gross_price: z.number(),
166
- quantity: z.union([z.number(), z.null()]),
166
+ quantity: z.number().gte(-140737488355328).lte(140737488355327),
167
167
  unit: z.union([z.string(), z.null()]),
168
168
  taxes: z.array(DocumentItemTax),
169
169
  discounts: z.array(LineDiscount).max(5),
@@ -131,7 +131,7 @@ const CreateDocumentItem = z
131
131
  description: z.union([z.string(), z.null()]),
132
132
  price: z.number(),
133
133
  gross_price: z.number(),
134
- quantity: z.union([z.number(), z.null()]),
134
+ quantity: z.number().gte(-140737488355328).lte(140737488355327),
135
135
  unit: z.union([z.string(), z.null()]),
136
136
  taxes: z.array(DocumentItemTax),
137
137
  discounts: z.array(LineDiscount).max(5),
@@ -131,7 +131,7 @@ const CreateDocumentItem = z
131
131
  description: z.union([z.string(), z.null()]),
132
132
  price: z.number(),
133
133
  gross_price: z.number(),
134
- quantity: z.union([z.number(), z.null()]),
134
+ quantity: z.number().gte(-140737488355328).lte(140737488355327),
135
135
  unit: z.union([z.string(), z.null()]),
136
136
  taxes: z.array(DocumentItemTax),
137
137
  discounts: z.array(LineDiscount).max(5),
@@ -164,7 +164,7 @@ const CreateDocumentItem = z
164
164
  description: z.union([z.string(), z.null()]),
165
165
  price: z.number(),
166
166
  gross_price: z.number(),
167
- quantity: z.union([z.number(), z.null()]),
167
+ quantity: z.number().gte(-140737488355328).lte(140737488355327),
168
168
  unit: z.union([z.string(), z.null()]),
169
169
  taxes: z.array(DocumentItemTax),
170
170
  discounts: z.array(LineDiscount).max(5),
@@ -11,7 +11,13 @@ import { z } from 'zod';
11
11
  // Dependency schema for startpdfexport_body
12
12
  const PdfExportByDocumentIds = z
13
13
  .object({
14
- type: z.enum(["invoice", "estimate", "credit_note", "advance_invoice"]),
14
+ type: z.enum([
15
+ "invoice",
16
+ "estimate",
17
+ "credit_note",
18
+ "advance_invoice",
19
+ "delivery_note",
20
+ ]),
15
21
  document_ids: z.array(z.string()).min(1),
16
22
  })
17
23
  .passthrough();
@@ -20,7 +26,13 @@ const PdfExportByDocumentIds = z
20
26
  // Dependency schema for startpdfexport_body
21
27
  const PdfExportByDateRange = z
22
28
  .object({
23
- type: z.enum(["invoice", "estimate", "credit_note", "advance_invoice"]),
29
+ type: z.enum([
30
+ "invoice",
31
+ "estimate",
32
+ "credit_note",
33
+ "advance_invoice",
34
+ "delivery_note",
35
+ ]),
24
36
  date_from: z.string().optional(),
25
37
  date_to: z.string().optional(),
26
38
  })
@@ -49,6 +49,7 @@ const createWebhookSchemaDefinition = z.object({
49
49
  "advance_invoice.created",
50
50
  "advance_invoice.paid",
51
51
  "advance_invoice.applied",
52
+ "advance_invoice.voided",
52
53
  "advance_invoice.deleted",
53
54
  "advance_invoice.restored",
54
55
  "item.created",
@@ -87,6 +88,7 @@ const createWebhookSchemaDefinition = z.object({
87
88
  "delivery_note.created",
88
89
  "delivery_note.sent",
89
90
  "delivery_note.cancelled",
91
+ "delivery_note.voided",
90
92
  "delivery_note.deleted",
91
93
  "delivery_note.restored",
92
94
  ])
@@ -142,6 +144,7 @@ const updateWebhookSchemaDefinition = z
142
144
  "advance_invoice.created",
143
145
  "advance_invoice.paid",
144
146
  "advance_invoice.applied",
147
+ "advance_invoice.voided",
145
148
  "advance_invoice.deleted",
146
149
  "advance_invoice.restored",
147
150
  "item.created",
@@ -180,6 +183,7 @@ const updateWebhookSchemaDefinition = z
180
183
  "delivery_note.created",
181
184
  "delivery_note.sent",
182
185
  "delivery_note.cancelled",
186
+ "delivery_note.voided",
183
187
  "delivery_note.deleted",
184
188
  "delivery_note.restored",
185
189
  ])
@@ -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