@spaceinvoices/react-ui 0.3.0 → 0.4.1
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/registry.json +0 -230
- package/src/components/advance-invoices/advance-invoices.hooks.ts +2 -2
- package/src/components/documents/documents.hooks.ts +5 -48
- package/src/components/documents/shared/document-preview-display.tsx +12 -1
- package/src/components/documents/view/document-actions-bar.tsx +20 -12
- package/src/components/documents/view/document-activities-list.tsx +166 -0
- package/src/components/documents/view/document-details-card.tsx +6 -6
- package/src/components/documents/view/index.ts +1 -0
- package/src/components/documents/view/locales/de.ts +32 -0
- package/src/components/documents/view/locales/es.ts +32 -0
- package/src/components/documents/view/locales/fr.ts +32 -0
- package/src/components/documents/view/locales/hr.ts +32 -0
- package/src/components/documents/view/locales/it.ts +32 -0
- package/src/components/documents/view/locales/nl.ts +32 -0
- package/src/components/documents/view/locales/pl.ts +32 -0
- package/src/components/documents/view/locales/pt.ts +32 -0
- package/src/components/documents/view/locales/sl.ts +32 -0
- 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/furs-settings-form/furs-settings-form.tsx +15 -7
- package/src/components/entities/furs-settings-form/furs-settings.hooks.ts +1 -1
- package/src/components/entities/furs-settings-form/locales/de.ts +2 -0
- package/src/components/entities/furs-settings-form/locales/en.ts +12 -0
- package/src/components/entities/furs-settings-form/locales/es.ts +2 -0
- package/src/components/entities/furs-settings-form/locales/fr.ts +2 -0
- package/src/components/entities/furs-settings-form/locales/hr.ts +2 -0
- package/src/components/entities/furs-settings-form/locales/it.ts +2 -0
- package/src/components/entities/furs-settings-form/locales/nl.ts +2 -0
- package/src/components/entities/furs-settings-form/locales/pl.ts +2 -0
- package/src/components/entities/furs-settings-form/locales/pt.ts +2 -0
- package/src/components/entities/furs-settings-form/locales/sl.ts +14 -0
- package/src/components/entities/furs-settings-form/sections/general-settings-section.tsx +121 -1
- package/src/components/entities/furs-settings-form/sections/premises-management-section.tsx +1 -0
- package/src/components/entities/furs-settings-form/sections/register-premise-dialog.tsx +44 -32
- package/src/components/invoices/index.ts +1 -1
- package/src/components/invoices/send-email-dialog/send-email-dialog.tsx +2 -2
- package/src/components/invoices/view/fiscalization-status-card.tsx +121 -0
- package/src/generate-schemas.ts +13 -1
- package/src/generated/schemas/advanceinvoice.ts +79 -187
- package/src/generated/schemas/creditnote.ts +63 -86
- package/src/generated/schemas/customadvanceinvoice.ts +70 -97
- package/src/generated/schemas/customcreditnote.ts +70 -97
- package/src/generated/schemas/customestimate.ts +68 -97
- package/src/generated/schemas/custominvoice.ts +70 -97
- package/src/generated/schemas/estimate.ts +67 -172
- package/src/generated/schemas/invoice.ts +79 -187
- package/src/generated/schemas/registerfursrealestatepremise_body.ts +11 -7
- package/src/generated/schemas/renderadvanceinvoicepreview_body.ts +61 -157
- package/src/generated/schemas/rendercreditnotepreview_body.ts +61 -157
- package/src/generated/schemas/renderestimatepreview_body.ts +61 -157
- package/src/generated/schemas/renderinvoicepreview_body.ts +61 -157
- package/src/hooks/use-duplicate-document.ts +19 -11
- package/src/providers/entities-provider.tsx +21 -0
- package/src/components/invoices/view/fina-info-display.tsx +0 -196
- package/src/components/invoices/view/furs-info-display.tsx +0 -213
|
@@ -15,6 +15,56 @@ const LineDiscount = z.object({
|
|
|
15
15
|
});
|
|
16
16
|
|
|
17
17
|
|
|
18
|
+
// Dependency schema for renderinvoicepreview_body
|
|
19
|
+
const DocumentItemTax = z
|
|
20
|
+
.object({
|
|
21
|
+
rate: z.number(),
|
|
22
|
+
tax_id: z.string(),
|
|
23
|
+
classification: z.string(),
|
|
24
|
+
reverse_charge: z.boolean(),
|
|
25
|
+
amount: z.number(),
|
|
26
|
+
})
|
|
27
|
+
.partial();
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
// Dependency schema for renderinvoicepreview_body
|
|
31
|
+
const DocumentEntity = z
|
|
32
|
+
.object({
|
|
33
|
+
name: z.union([z.string(), z.null()]),
|
|
34
|
+
email: z.union([z.string(), z.null()]),
|
|
35
|
+
address: z.union([z.string(), z.null()]),
|
|
36
|
+
address_2: z.union([z.string(), z.null()]),
|
|
37
|
+
post_code: z.union([z.string(), z.null()]),
|
|
38
|
+
city: z.union([z.string(), z.null()]),
|
|
39
|
+
state: z.union([z.string(), z.null()]),
|
|
40
|
+
country: z.union([z.string(), z.null()]),
|
|
41
|
+
country_code: z.union([z.string(), z.null()]),
|
|
42
|
+
tax_number: z.union([z.string(), z.null()]),
|
|
43
|
+
tax_number_2: z.union([z.string(), z.null()]),
|
|
44
|
+
company_number: z.union([z.string(), z.null()]),
|
|
45
|
+
bank_account: z.union([
|
|
46
|
+
z
|
|
47
|
+
.object({
|
|
48
|
+
type: z
|
|
49
|
+
.enum(["iban", "us_domestic", "uk_domestic", "other"])
|
|
50
|
+
.default("iban"),
|
|
51
|
+
name: z.string(),
|
|
52
|
+
bank_name: z.string(),
|
|
53
|
+
iban: z.string(),
|
|
54
|
+
account_number: z.string(),
|
|
55
|
+
bic: z.string(),
|
|
56
|
+
routing_number: z.string(),
|
|
57
|
+
sort_code: z.string(),
|
|
58
|
+
})
|
|
59
|
+
.partial()
|
|
60
|
+
.passthrough(),
|
|
61
|
+
z.null(),
|
|
62
|
+
]),
|
|
63
|
+
})
|
|
64
|
+
.partial()
|
|
65
|
+
.passthrough();
|
|
66
|
+
|
|
67
|
+
|
|
18
68
|
// Dependency schema for renderinvoicepreview_body
|
|
19
69
|
const CompleteInvoicePreview = z.object({
|
|
20
70
|
is_draft: z.boolean().optional(),
|
|
@@ -22,84 +72,17 @@ const CompleteInvoicePreview = z.object({
|
|
|
22
72
|
.string()
|
|
23
73
|
.regex(/^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}:\d{2}(\.\d{3})?Z?)?$/)
|
|
24
74
|
.optional(),
|
|
25
|
-
issuer:
|
|
26
|
-
.object({
|
|
27
|
-
name: z.union([z.string(), z.null()]),
|
|
28
|
-
email: z.union([z.string(), z.null()]),
|
|
29
|
-
address: z.union([z.string(), z.null()]),
|
|
30
|
-
address_2: z.union([z.string(), z.null()]),
|
|
31
|
-
post_code: z.union([z.string(), z.null()]),
|
|
32
|
-
city: z.union([z.string(), z.null()]),
|
|
33
|
-
state: z.union([z.string(), z.null()]),
|
|
34
|
-
country: z.union([z.string(), z.null()]),
|
|
35
|
-
country_code: z.union([z.string(), z.null()]),
|
|
36
|
-
tax_number: z.union([z.string(), z.null()]),
|
|
37
|
-
tax_number_2: z.union([z.string(), z.null()]),
|
|
38
|
-
company_number: z.union([z.string(), z.null()]),
|
|
39
|
-
bank_account: z.union([
|
|
40
|
-
z
|
|
41
|
-
.object({
|
|
42
|
-
type: z
|
|
43
|
-
.enum(["iban", "us_domestic", "uk_domestic", "other"])
|
|
44
|
-
.default("iban"),
|
|
45
|
-
name: z.string(),
|
|
46
|
-
bank_name: z.string(),
|
|
47
|
-
iban: z.string(),
|
|
48
|
-
account_number: z.string(),
|
|
49
|
-
bic: z.string(),
|
|
50
|
-
routing_number: z.string(),
|
|
51
|
-
sort_code: z.string(),
|
|
52
|
-
})
|
|
53
|
-
.partial()
|
|
54
|
-
.passthrough(),
|
|
55
|
-
z.null(),
|
|
56
|
-
]),
|
|
57
|
-
})
|
|
58
|
-
.partial()
|
|
59
|
-
.passthrough()
|
|
60
|
-
.optional(),
|
|
75
|
+
issuer: DocumentEntity.optional(),
|
|
61
76
|
customer_id: z.union([z.string(), z.null()]).optional(),
|
|
62
|
-
customer:
|
|
63
|
-
.union([
|
|
77
|
+
customer: DocumentEntity.and(
|
|
78
|
+
z.union([
|
|
64
79
|
z
|
|
65
|
-
.object({
|
|
66
|
-
name: z.union([z.string(), z.null()]),
|
|
67
|
-
email: z.union([z.string(), z.null()]),
|
|
68
|
-
address: z.union([z.string(), z.null()]),
|
|
69
|
-
address_2: z.union([z.string(), z.null()]),
|
|
70
|
-
post_code: z.union([z.string(), z.null()]),
|
|
71
|
-
city: z.union([z.string(), z.null()]),
|
|
72
|
-
state: z.union([z.string(), z.null()]),
|
|
73
|
-
country: z.union([z.string(), z.null()]),
|
|
74
|
-
country_code: z.union([z.string(), z.null()]),
|
|
75
|
-
tax_number: z.union([z.string(), z.null()]),
|
|
76
|
-
tax_number_2: z.union([z.string(), z.null()]),
|
|
77
|
-
company_number: z.union([z.string(), z.null()]),
|
|
78
|
-
bank_account: z.union([
|
|
79
|
-
z
|
|
80
|
-
.object({
|
|
81
|
-
type: z
|
|
82
|
-
.enum(["iban", "us_domestic", "uk_domestic", "other"])
|
|
83
|
-
.default("iban"),
|
|
84
|
-
name: z.string(),
|
|
85
|
-
bank_name: z.string(),
|
|
86
|
-
iban: z.string(),
|
|
87
|
-
account_number: z.string(),
|
|
88
|
-
bic: z.string(),
|
|
89
|
-
routing_number: z.string(),
|
|
90
|
-
sort_code: z.string(),
|
|
91
|
-
})
|
|
92
|
-
.partial()
|
|
93
|
-
.passthrough(),
|
|
94
|
-
z.null(),
|
|
95
|
-
]),
|
|
96
|
-
save_customer: z.boolean().default(true),
|
|
97
|
-
})
|
|
80
|
+
.object({ save_customer: z.boolean().default(true) })
|
|
98
81
|
.partial()
|
|
99
82
|
.passthrough(),
|
|
100
83
|
z.null(),
|
|
101
84
|
])
|
|
102
|
-
|
|
85
|
+
).optional(),
|
|
103
86
|
note: z.union([z.string(), z.null()]).optional(),
|
|
104
87
|
payment_terms: z.union([z.string(), z.null()]).optional(),
|
|
105
88
|
tax_clause: z.union([z.string(), z.null()]).optional(),
|
|
@@ -117,19 +100,7 @@ const CompleteInvoicePreview = z.object({
|
|
|
117
100
|
gross_price: z.number().optional(),
|
|
118
101
|
quantity: z.number().gte(-140737488355328).lte(140737488355327),
|
|
119
102
|
unit: z.union([z.string(), z.null()]).optional(),
|
|
120
|
-
taxes: z
|
|
121
|
-
.array(
|
|
122
|
-
z
|
|
123
|
-
.object({
|
|
124
|
-
rate: z.number(),
|
|
125
|
-
tax_id: z.string(),
|
|
126
|
-
classification: z.string(),
|
|
127
|
-
reverse_charge: z.boolean(),
|
|
128
|
-
amount: z.number(),
|
|
129
|
-
})
|
|
130
|
-
.partial()
|
|
131
|
-
)
|
|
132
|
-
.optional(),
|
|
103
|
+
taxes: z.array(DocumentItemTax).optional(),
|
|
133
104
|
discounts: z.array(LineDiscount).max(5).optional(),
|
|
134
105
|
metadata: z
|
|
135
106
|
.union([
|
|
@@ -189,84 +160,17 @@ const PartialInvoicePreview = z.object({
|
|
|
189
160
|
.string()
|
|
190
161
|
.regex(/^\d{4}-\d{2}-\d{2}(T\d{2}:\d{2}:\d{2}(\.\d{3})?Z?)?$/)
|
|
191
162
|
.optional(),
|
|
192
|
-
issuer:
|
|
193
|
-
.object({
|
|
194
|
-
name: z.union([z.string(), z.null()]),
|
|
195
|
-
email: z.union([z.string(), z.null()]),
|
|
196
|
-
address: z.union([z.string(), z.null()]),
|
|
197
|
-
address_2: z.union([z.string(), z.null()]),
|
|
198
|
-
post_code: z.union([z.string(), z.null()]),
|
|
199
|
-
city: z.union([z.string(), z.null()]),
|
|
200
|
-
state: z.union([z.string(), z.null()]),
|
|
201
|
-
country: z.union([z.string(), z.null()]),
|
|
202
|
-
country_code: z.union([z.string(), z.null()]),
|
|
203
|
-
tax_number: z.union([z.string(), z.null()]),
|
|
204
|
-
tax_number_2: z.union([z.string(), z.null()]),
|
|
205
|
-
company_number: z.union([z.string(), z.null()]),
|
|
206
|
-
bank_account: z.union([
|
|
207
|
-
z
|
|
208
|
-
.object({
|
|
209
|
-
type: z
|
|
210
|
-
.enum(["iban", "us_domestic", "uk_domestic", "other"])
|
|
211
|
-
.default("iban"),
|
|
212
|
-
name: z.string(),
|
|
213
|
-
bank_name: z.string(),
|
|
214
|
-
iban: z.string(),
|
|
215
|
-
account_number: z.string(),
|
|
216
|
-
bic: z.string(),
|
|
217
|
-
routing_number: z.string(),
|
|
218
|
-
sort_code: z.string(),
|
|
219
|
-
})
|
|
220
|
-
.partial()
|
|
221
|
-
.passthrough(),
|
|
222
|
-
z.null(),
|
|
223
|
-
]),
|
|
224
|
-
})
|
|
225
|
-
.partial()
|
|
226
|
-
.passthrough()
|
|
227
|
-
.optional(),
|
|
163
|
+
issuer: DocumentEntity.optional(),
|
|
228
164
|
customer_id: z.union([z.string(), z.null()]).optional(),
|
|
229
|
-
customer:
|
|
230
|
-
.union([
|
|
165
|
+
customer: DocumentEntity.and(
|
|
166
|
+
z.union([
|
|
231
167
|
z
|
|
232
|
-
.object({
|
|
233
|
-
name: z.union([z.string(), z.null()]),
|
|
234
|
-
email: z.union([z.string(), z.null()]),
|
|
235
|
-
address: z.union([z.string(), z.null()]),
|
|
236
|
-
address_2: z.union([z.string(), z.null()]),
|
|
237
|
-
post_code: z.union([z.string(), z.null()]),
|
|
238
|
-
city: z.union([z.string(), z.null()]),
|
|
239
|
-
state: z.union([z.string(), z.null()]),
|
|
240
|
-
country: z.union([z.string(), z.null()]),
|
|
241
|
-
country_code: z.union([z.string(), z.null()]),
|
|
242
|
-
tax_number: z.union([z.string(), z.null()]),
|
|
243
|
-
tax_number_2: z.union([z.string(), z.null()]),
|
|
244
|
-
company_number: z.union([z.string(), z.null()]),
|
|
245
|
-
bank_account: z.union([
|
|
246
|
-
z
|
|
247
|
-
.object({
|
|
248
|
-
type: z
|
|
249
|
-
.enum(["iban", "us_domestic", "uk_domestic", "other"])
|
|
250
|
-
.default("iban"),
|
|
251
|
-
name: z.string(),
|
|
252
|
-
bank_name: z.string(),
|
|
253
|
-
iban: z.string(),
|
|
254
|
-
account_number: z.string(),
|
|
255
|
-
bic: z.string(),
|
|
256
|
-
routing_number: z.string(),
|
|
257
|
-
sort_code: z.string(),
|
|
258
|
-
})
|
|
259
|
-
.partial()
|
|
260
|
-
.passthrough(),
|
|
261
|
-
z.null(),
|
|
262
|
-
]),
|
|
263
|
-
save_customer: z.boolean().default(true),
|
|
264
|
-
})
|
|
168
|
+
.object({ save_customer: z.boolean().default(true) })
|
|
265
169
|
.partial()
|
|
266
170
|
.passthrough(),
|
|
267
171
|
z.null(),
|
|
268
172
|
])
|
|
269
|
-
|
|
173
|
+
).optional(),
|
|
270
174
|
note: z.union([z.string(), z.null()]).optional(),
|
|
271
175
|
payment_terms: z.union([z.string(), z.null()]).optional(),
|
|
272
176
|
tax_clause: z.union([z.string(), z.null()]).optional(),
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import type {
|
|
2
|
-
|
|
2
|
+
AdvanceInvoice,
|
|
3
3
|
CreateAdvanceInvoiceRequest,
|
|
4
4
|
CreateCreditNoteRequest,
|
|
5
5
|
CreateEstimateRequest,
|
|
6
6
|
CreateInvoiceRequest,
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
CreditNote,
|
|
8
|
+
Estimate,
|
|
9
|
+
Invoice,
|
|
10
10
|
} from "@spaceinvoices/js-sdk";
|
|
11
11
|
import { useQuery } from "@tanstack/react-query";
|
|
12
12
|
|
|
@@ -14,7 +14,7 @@ import { useEntities } from "@/ui/providers/entities-context";
|
|
|
14
14
|
import { useSDK } from "@/ui/providers/sdk-provider";
|
|
15
15
|
|
|
16
16
|
export type DocumentType = "invoice" | "estimate" | "credit_note" | "advance_invoice";
|
|
17
|
-
type Document =
|
|
17
|
+
type Document = Invoice | Estimate | CreditNote | AdvanceInvoice;
|
|
18
18
|
type CreateRequest =
|
|
19
19
|
| CreateInvoiceRequest
|
|
20
20
|
| CreateEstimateRequest
|
|
@@ -56,16 +56,25 @@ export function getAllowedDuplicateTargets(sourceType: DocumentType): DocumentTy
|
|
|
56
56
|
*/
|
|
57
57
|
function transformDocumentForDuplication(source: Document, _targetType: DocumentType): Partial<CreateRequest> {
|
|
58
58
|
// Transform items - copy only the fields needed for creation
|
|
59
|
-
|
|
59
|
+
// Use type assertion for items since all document item types share the same shape
|
|
60
|
+
const sourceItems = source.items as Array<{
|
|
61
|
+
name: string;
|
|
62
|
+
description: string | null;
|
|
63
|
+
quantity: number;
|
|
64
|
+
price: number;
|
|
65
|
+
gross_price?: number | null;
|
|
66
|
+
taxes: Array<{ tax_id?: string }>;
|
|
67
|
+
}>;
|
|
68
|
+
const items = sourceItems?.map((item) => ({
|
|
60
69
|
name: item.name,
|
|
61
70
|
description: item.description,
|
|
62
71
|
quantity: item.quantity,
|
|
63
72
|
// Use gross_price if set, otherwise use price. The form uses is_gross_price as a UI toggle.
|
|
64
|
-
price:
|
|
73
|
+
price: item.gross_price ?? item.price,
|
|
65
74
|
// Copy tax references (tax_id), not computed tax data
|
|
66
|
-
taxes: item.taxes?.map((tax
|
|
75
|
+
taxes: item.taxes?.map((tax) => ({ tax_id: tax.tax_id })),
|
|
67
76
|
// Derive is_gross_price from whether gross_price is set
|
|
68
|
-
gross_price:
|
|
77
|
+
gross_price: item.gross_price ?? undefined,
|
|
69
78
|
}));
|
|
70
79
|
|
|
71
80
|
// Build customer data - always copy if available (form needs this for display)
|
|
@@ -158,8 +167,7 @@ export function useDuplicateDocument({
|
|
|
158
167
|
if (sourceType === "invoice") {
|
|
159
168
|
source = await sdk.invoices.get(sourceId, undefined, { entity_id: activeEntity.id });
|
|
160
169
|
} else if (sourceType === "estimate") {
|
|
161
|
-
|
|
162
|
-
source = await sdk.estimates.get(sourceId, { entity_id: activeEntity.id });
|
|
170
|
+
source = await sdk.estimates.get(sourceId, undefined, { entity_id: activeEntity.id });
|
|
163
171
|
} else if (sourceType === "advance_invoice") {
|
|
164
172
|
source = await sdk.advanceInvoices.get(sourceId, undefined, { entity_id: activeEntity.id });
|
|
165
173
|
} else {
|
|
@@ -180,6 +180,27 @@ export function EntitiesProvider({
|
|
|
180
180
|
}
|
|
181
181
|
}, [memoizedEntities, urlEntityId]); // Re-run when URL entity changes
|
|
182
182
|
|
|
183
|
+
// When urlEntityId is provided but not found in current environment, auto-switch
|
|
184
|
+
const urlEntityFallbackAttempted = useRef<string | null>(null);
|
|
185
|
+
useEffect(() => {
|
|
186
|
+
if (!urlEntityId || isLoading) return;
|
|
187
|
+
if (memoizedEntities.length === 0) return;
|
|
188
|
+
|
|
189
|
+
const found = memoizedEntities.some((e) => e.id === urlEntityId);
|
|
190
|
+
if (found) {
|
|
191
|
+
urlEntityFallbackAttempted.current = null;
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Prevent infinite switching between environments
|
|
196
|
+
if (urlEntityFallbackAttempted.current === urlEntityId) return;
|
|
197
|
+
urlEntityFallbackAttempted.current = urlEntityId;
|
|
198
|
+
|
|
199
|
+
// URL entity not in current environment — try the other
|
|
200
|
+
const altEnv: EntityEnvironment = environment === "live" ? "sandbox" : "live";
|
|
201
|
+
setEnvironmentState(altEnv);
|
|
202
|
+
}, [urlEntityId, memoizedEntities, isLoading, environment]);
|
|
203
|
+
|
|
183
204
|
const cookieOpts = useMemo(
|
|
184
205
|
() => ({
|
|
185
206
|
path: "/",
|
|
@@ -1,196 +0,0 @@
|
|
|
1
|
-
import type { Invoice } from "@spaceinvoices/js-sdk";
|
|
2
|
-
import { AlertCircle, Check, CheckCircle2, Clock, Copy, ExternalLink, XCircle } from "lucide-react";
|
|
3
|
-
import { useState } from "react";
|
|
4
|
-
import { Alert, AlertDescription, AlertTitle } from "@/ui/components/ui/alert";
|
|
5
|
-
import { Badge } from "@/ui/components/ui/badge";
|
|
6
|
-
import { Button } from "@/ui/components/ui/button";
|
|
7
|
-
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/ui/components/ui/card";
|
|
8
|
-
import type { ComponentTranslationProps } from "@/ui/lib/translation";
|
|
9
|
-
import { createTranslation } from "@/ui/lib/translation";
|
|
10
|
-
import de from "../../entities/fina-settings-form/locales/de";
|
|
11
|
-
import en from "../../entities/fina-settings-form/locales/en";
|
|
12
|
-
import sl from "../../entities/fina-settings-form/locales/sl";
|
|
13
|
-
|
|
14
|
-
const translations = { de, sl, en } as const;
|
|
15
|
-
|
|
16
|
-
type FinaData = {
|
|
17
|
-
status?: "success" | "pending" | "failed";
|
|
18
|
-
error?: string;
|
|
19
|
-
fiscalized_at?: string;
|
|
20
|
-
data?: {
|
|
21
|
-
zki?: string;
|
|
22
|
-
jir?: string;
|
|
23
|
-
premise_id?: string;
|
|
24
|
-
device_id?: string;
|
|
25
|
-
invoice_number?: string;
|
|
26
|
-
qr_code_url?: string;
|
|
27
|
-
};
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
interface FinaInfoDisplayProps extends ComponentTranslationProps {
|
|
31
|
-
invoice: Invoice;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export function FinaInfoDisplay({ invoice, t: translateFn, namespace, locale }: FinaInfoDisplayProps) {
|
|
35
|
-
const [copiedField, setCopiedField] = useState<string | null>(null);
|
|
36
|
-
|
|
37
|
-
const t = createTranslation({
|
|
38
|
-
t: translateFn,
|
|
39
|
-
namespace,
|
|
40
|
-
locale,
|
|
41
|
-
translations,
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
const fina = (invoice as any).fina as FinaData | undefined;
|
|
45
|
-
|
|
46
|
-
if (!fina) {
|
|
47
|
-
return null;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
const copyToClipboard = async (text: string, fieldName: string) => {
|
|
51
|
-
try {
|
|
52
|
-
await navigator.clipboard.writeText(text);
|
|
53
|
-
setCopiedField(fieldName);
|
|
54
|
-
setTimeout(() => setCopiedField(null), 2000);
|
|
55
|
-
} catch (err) {
|
|
56
|
-
console.error("Failed to copy:", err);
|
|
57
|
-
}
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
const getStatusBadge = () => {
|
|
61
|
-
switch (fina.status) {
|
|
62
|
-
case "success":
|
|
63
|
-
return (
|
|
64
|
-
<Badge className="bg-green-100 text-green-800 dark:bg-green-900 dark:text-green-100">
|
|
65
|
-
<CheckCircle2 className="mr-1 h-3 w-3" />
|
|
66
|
-
{t("Fiscalized")}
|
|
67
|
-
</Badge>
|
|
68
|
-
);
|
|
69
|
-
case "pending":
|
|
70
|
-
return (
|
|
71
|
-
<Badge className="bg-yellow-100 text-yellow-800 dark:bg-yellow-900 dark:text-yellow-100">
|
|
72
|
-
<Clock className="mr-1 h-3 w-3" />
|
|
73
|
-
{t("Pending")}
|
|
74
|
-
</Badge>
|
|
75
|
-
);
|
|
76
|
-
case "failed":
|
|
77
|
-
return (
|
|
78
|
-
<Badge className="bg-red-100 text-red-800 dark:bg-red-900 dark:text-red-100">
|
|
79
|
-
<XCircle className="mr-1 h-3 w-3" />
|
|
80
|
-
{t("Failed")}
|
|
81
|
-
</Badge>
|
|
82
|
-
);
|
|
83
|
-
default:
|
|
84
|
-
return null;
|
|
85
|
-
}
|
|
86
|
-
};
|
|
87
|
-
|
|
88
|
-
return (
|
|
89
|
-
<Card>
|
|
90
|
-
<CardHeader>
|
|
91
|
-
<CardTitle className="flex items-center gap-2">
|
|
92
|
-
{t("FINA Fiscalization")}
|
|
93
|
-
{getStatusBadge()}
|
|
94
|
-
</CardTitle>
|
|
95
|
-
<CardDescription>{t("Croatian tax authority fiscalization details")}</CardDescription>
|
|
96
|
-
</CardHeader>
|
|
97
|
-
<CardContent className="space-y-4">
|
|
98
|
-
{/* Error Message */}
|
|
99
|
-
{fina.status === "failed" && fina.error && (
|
|
100
|
-
<Alert variant="destructive">
|
|
101
|
-
<AlertCircle className="h-4 w-4" />
|
|
102
|
-
<AlertTitle>{t("Fiscalization Error")}</AlertTitle>
|
|
103
|
-
<AlertDescription>{fina.error}</AlertDescription>
|
|
104
|
-
</Alert>
|
|
105
|
-
)}
|
|
106
|
-
|
|
107
|
-
{/* FINA Data */}
|
|
108
|
-
{fina.data && (
|
|
109
|
-
<div className="space-y-3">
|
|
110
|
-
{/* ZKI Code */}
|
|
111
|
-
{fina.data.zki && (
|
|
112
|
-
<div className="flex items-center justify-between rounded-lg border p-3">
|
|
113
|
-
<div className="space-y-1">
|
|
114
|
-
<p className="font-medium text-sm">{t("ZKI")}</p>
|
|
115
|
-
<p className="font-mono text-muted-foreground text-xs">{fina.data.zki}</p>
|
|
116
|
-
</div>
|
|
117
|
-
<Button variant="ghost" size="sm" onClick={() => copyToClipboard(fina.data!.zki!, "zki")}>
|
|
118
|
-
{copiedField === "zki" ? <Check className="h-4 w-4 text-green-600" /> : <Copy className="h-4 w-4" />}
|
|
119
|
-
</Button>
|
|
120
|
-
</div>
|
|
121
|
-
)}
|
|
122
|
-
|
|
123
|
-
{/* JIR Code */}
|
|
124
|
-
{fina.data.jir && (
|
|
125
|
-
<div className="flex items-center justify-between rounded-lg border p-3">
|
|
126
|
-
<div className="space-y-1">
|
|
127
|
-
<p className="font-medium text-sm">{t("JIR")}</p>
|
|
128
|
-
<p className="font-mono text-muted-foreground text-xs">{fina.data.jir}</p>
|
|
129
|
-
</div>
|
|
130
|
-
<Button variant="ghost" size="sm" onClick={() => copyToClipboard(fina.data!.jir!, "jir")}>
|
|
131
|
-
{copiedField === "jir" ? <Check className="h-4 w-4 text-green-600" /> : <Copy className="h-4 w-4" />}
|
|
132
|
-
</Button>
|
|
133
|
-
</div>
|
|
134
|
-
)}
|
|
135
|
-
|
|
136
|
-
{/* Business Premise & Device */}
|
|
137
|
-
<div className="grid grid-cols-2 gap-3">
|
|
138
|
-
{fina.data.premise_id && (
|
|
139
|
-
<div className="space-y-1">
|
|
140
|
-
<p className="font-medium text-sm">{t("Business Premise")}</p>
|
|
141
|
-
<p className="text-muted-foreground text-sm">{fina.data.premise_id}</p>
|
|
142
|
-
</div>
|
|
143
|
-
)}
|
|
144
|
-
{fina.data.device_id && (
|
|
145
|
-
<div className="space-y-1">
|
|
146
|
-
<p className="font-medium text-sm">{t("Electronic Device")}</p>
|
|
147
|
-
<p className="text-muted-foreground text-sm">{fina.data.device_id}</p>
|
|
148
|
-
</div>
|
|
149
|
-
)}
|
|
150
|
-
</div>
|
|
151
|
-
|
|
152
|
-
{/* Invoice Number */}
|
|
153
|
-
{fina.data.invoice_number && (
|
|
154
|
-
<div className="space-y-1">
|
|
155
|
-
<p className="font-medium text-sm">{t("Invoice Number")}</p>
|
|
156
|
-
<p className="text-muted-foreground text-sm">{fina.data.invoice_number}</p>
|
|
157
|
-
</div>
|
|
158
|
-
)}
|
|
159
|
-
|
|
160
|
-
{/* QR Code URL */}
|
|
161
|
-
{fina.data.qr_code_url && (
|
|
162
|
-
<div className="flex items-center justify-between rounded-lg border p-3">
|
|
163
|
-
<div className="space-y-1">
|
|
164
|
-
<p className="font-medium text-sm">{t("QR Code")}</p>
|
|
165
|
-
<a
|
|
166
|
-
href={fina.data.qr_code_url}
|
|
167
|
-
target="_blank"
|
|
168
|
-
rel="noopener noreferrer"
|
|
169
|
-
className="flex items-center gap-1 text-primary text-xs hover:underline"
|
|
170
|
-
>
|
|
171
|
-
{t("QR Code")}
|
|
172
|
-
<ExternalLink className="h-3 w-3" />
|
|
173
|
-
</a>
|
|
174
|
-
</div>
|
|
175
|
-
<Button variant="ghost" size="sm" onClick={() => copyToClipboard(fina.data!.qr_code_url!, "qr_url")}>
|
|
176
|
-
{copiedField === "qr_url" ? (
|
|
177
|
-
<Check className="h-4 w-4 text-green-600" />
|
|
178
|
-
) : (
|
|
179
|
-
<Copy className="h-4 w-4" />
|
|
180
|
-
)}
|
|
181
|
-
</Button>
|
|
182
|
-
</div>
|
|
183
|
-
)}
|
|
184
|
-
</div>
|
|
185
|
-
)}
|
|
186
|
-
|
|
187
|
-
{/* Fiscalized Timestamp */}
|
|
188
|
-
{fina.fiscalized_at && (
|
|
189
|
-
<div className="pt-2 text-muted-foreground text-sm">
|
|
190
|
-
{t("Fiscalized at")}: {new Date(fina.fiscalized_at).toLocaleString()}
|
|
191
|
-
</div>
|
|
192
|
-
)}
|
|
193
|
-
</CardContent>
|
|
194
|
-
</Card>
|
|
195
|
-
);
|
|
196
|
-
}
|