@spaceinvoices/react-ui 0.4.0 → 0.4.2

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 (200) hide show
  1. package/cli/dist/index.js +1 -1
  2. package/package.json +1 -1
  3. package/registry.json +23 -27
  4. package/src/components/advance-invoices/advance-invoices.hooks.ts +32 -2
  5. package/src/components/advance-invoices/create/create-advance-invoice-form.tsx +109 -4
  6. package/src/components/advance-invoices/create/locales/de.ts +2 -0
  7. package/src/components/advance-invoices/create/locales/es.ts +2 -0
  8. package/src/components/advance-invoices/create/locales/fr.ts +2 -0
  9. package/src/components/advance-invoices/create/locales/hr.ts +2 -0
  10. package/src/components/advance-invoices/create/locales/it.ts +2 -0
  11. package/src/components/advance-invoices/create/locales/nl.ts +2 -0
  12. package/src/components/advance-invoices/create/locales/pl.ts +2 -0
  13. package/src/components/advance-invoices/create/locales/pt.ts +2 -0
  14. package/src/components/advance-invoices/create/locales/sl.ts +2 -0
  15. package/src/components/advance-invoices/create/prepare-advance-invoice-submission.ts +17 -0
  16. package/src/components/advance-invoices/list/list-row-actions.tsx +3 -6
  17. package/src/components/advance-invoices/list/list-table.tsx +45 -2
  18. package/src/components/advance-invoices/list/locales/de.ts +3 -0
  19. package/src/components/advance-invoices/list/locales/en.ts +3 -0
  20. package/src/components/advance-invoices/list/locales/es.ts +3 -0
  21. package/src/components/advance-invoices/list/locales/fr.ts +3 -0
  22. package/src/components/advance-invoices/list/locales/hr.ts +3 -0
  23. package/src/components/advance-invoices/list/locales/it.ts +3 -0
  24. package/src/components/advance-invoices/list/locales/nl.ts +3 -0
  25. package/src/components/advance-invoices/list/locales/pl.ts +3 -0
  26. package/src/components/advance-invoices/list/locales/pt.ts +3 -0
  27. package/src/components/advance-invoices/list/locales/sl.ts +3 -0
  28. package/src/components/credit-notes/create/create-credit-note-form.tsx +177 -6
  29. package/src/components/credit-notes/create/locales/de.ts +8 -0
  30. package/src/components/credit-notes/create/locales/es.ts +8 -0
  31. package/src/components/credit-notes/create/locales/fr.ts +7 -0
  32. package/src/components/credit-notes/create/locales/hr.ts +7 -0
  33. package/src/components/credit-notes/create/locales/it.ts +9 -0
  34. package/src/components/credit-notes/create/locales/nl.ts +7 -0
  35. package/src/components/credit-notes/create/locales/pl.ts +7 -0
  36. package/src/components/credit-notes/create/locales/pt.ts +7 -0
  37. package/src/components/credit-notes/create/locales/sl.ts +7 -0
  38. package/src/components/credit-notes/credit-notes.hooks.ts +30 -0
  39. package/src/components/credit-notes/list/list-row-actions.tsx +3 -6
  40. package/src/components/credit-notes/list/list-table.tsx +45 -2
  41. package/src/components/credit-notes/list/locales/de.ts +3 -0
  42. package/src/components/credit-notes/list/locales/en.ts +3 -0
  43. package/src/components/credit-notes/list/locales/es.ts +3 -0
  44. package/src/components/credit-notes/list/locales/fr.ts +3 -0
  45. package/src/components/credit-notes/list/locales/hr.ts +3 -0
  46. package/src/components/credit-notes/list/locales/it.ts +3 -0
  47. package/src/components/credit-notes/list/locales/nl.ts +3 -0
  48. package/src/components/credit-notes/list/locales/pl.ts +3 -0
  49. package/src/components/credit-notes/list/locales/pt.ts +3 -0
  50. package/src/components/credit-notes/list/locales/sl.ts +3 -0
  51. package/src/components/customers/create-customer-form/create-customer-form.tsx +0 -1
  52. package/src/components/documents/create/document-details-section.tsx +67 -1
  53. package/src/components/documents/create/mark-as-paid-section.tsx +11 -2
  54. package/src/components/documents/view/document-actions-bar.tsx +30 -0
  55. package/src/components/documents/view/locales/de.ts +5 -0
  56. package/src/components/documents/view/locales/es.ts +5 -0
  57. package/src/components/documents/view/locales/fr.ts +5 -0
  58. package/src/components/documents/view/locales/hr.ts +5 -0
  59. package/src/components/documents/view/locales/it.ts +5 -0
  60. package/src/components/documents/view/locales/nl.ts +5 -0
  61. package/src/components/documents/view/locales/pl.ts +5 -0
  62. package/src/components/documents/view/locales/pt.ts +5 -0
  63. package/src/components/documents/view/locales/sl.ts +5 -0
  64. package/src/components/entities/create-entity-form.tsx +1 -1
  65. package/src/components/entities/entity-settings-form/entity-settings-form.tsx +2 -3
  66. package/src/components/entities/entity-settings-form/locales/es.ts +2 -0
  67. package/src/components/entities/entity-settings-form/locales/fr.ts +2 -0
  68. package/src/components/entities/entity-settings-form/locales/hr.ts +2 -0
  69. package/src/components/entities/entity-settings-form/locales/it.ts +2 -0
  70. package/src/components/entities/entity-settings-form/locales/nl.ts +2 -0
  71. package/src/components/entities/entity-settings-form/locales/pl.ts +2 -0
  72. package/src/components/entities/entity-settings-form/locales/pt.ts +2 -0
  73. package/src/components/entities/fina-settings-form/fina-operator-required-dialog.tsx +109 -0
  74. package/src/components/entities/fina-settings-form/fina-settings-form.tsx +365 -35
  75. package/src/components/entities/fina-settings-form/fina-settings.hooks.ts +101 -20
  76. package/src/components/entities/fina-settings-form/index.ts +1 -0
  77. package/src/components/entities/fina-settings-form/locales/de.ts +54 -34
  78. package/src/components/entities/fina-settings-form/locales/en.ts +51 -34
  79. package/src/components/entities/fina-settings-form/locales/es.ts +50 -34
  80. package/src/components/entities/fina-settings-form/locales/fr.ts +50 -34
  81. package/src/components/entities/fina-settings-form/locales/hr.ts +50 -34
  82. package/src/components/entities/fina-settings-form/locales/it.ts +50 -34
  83. package/src/components/entities/fina-settings-form/locales/nl.ts +50 -34
  84. package/src/components/entities/fina-settings-form/locales/pl.ts +50 -34
  85. package/src/components/entities/fina-settings-form/locales/pt.ts +50 -34
  86. package/src/components/entities/fina-settings-form/locales/sl.ts +50 -34
  87. package/src/components/entities/fina-settings-form/sections/certificate-settings-section.tsx +18 -0
  88. package/src/components/entities/fina-settings-form/sections/premises-management-section.tsx +64 -89
  89. package/src/components/entities/fina-settings-form/sections/register-premise-dialog.tsx +51 -323
  90. package/src/components/entities/furs-settings-form/furs-operator-required-dialog.tsx +106 -0
  91. package/src/components/entities/furs-settings-form/furs-settings-form.tsx +24 -10
  92. package/src/components/entities/furs-settings-form/furs-settings.hooks.ts +5 -9
  93. package/src/components/entities/furs-settings-form/index.ts +1 -0
  94. package/src/components/entities/furs-settings-form/locales/de.ts +27 -3
  95. package/src/components/entities/furs-settings-form/locales/en.ts +17 -3
  96. package/src/components/entities/furs-settings-form/locales/es.ts +26 -3
  97. package/src/components/entities/furs-settings-form/locales/fr.ts +26 -3
  98. package/src/components/entities/furs-settings-form/locales/hr.ts +26 -3
  99. package/src/components/entities/furs-settings-form/locales/it.ts +26 -3
  100. package/src/components/entities/furs-settings-form/locales/nl.ts +26 -3
  101. package/src/components/entities/furs-settings-form/locales/pl.ts +26 -3
  102. package/src/components/entities/furs-settings-form/locales/pt.ts +26 -3
  103. package/src/components/entities/furs-settings-form/locales/sl.ts +16 -3
  104. package/src/components/entities/furs-settings-form/sections/certificate-settings-section.tsx +22 -0
  105. package/src/components/entities/furs-settings-form/sections/general-settings-section.tsx +15 -2
  106. package/src/components/entities/furs-settings-form/sections/premises-management-section.tsx +1 -0
  107. package/src/components/entities/furs-settings-form/sections/register-premise-dialog.tsx +58 -34
  108. package/src/components/entities/settings/tax-rules-settings-form.tsx +4 -4
  109. package/src/components/estimates/list/list-row-actions.tsx +3 -7
  110. package/src/components/estimates/list/list-table.tsx +35 -2
  111. package/src/components/estimates/list/locales/de.ts +3 -0
  112. package/src/components/estimates/list/locales/en.ts +3 -0
  113. package/src/components/estimates/list/locales/es.ts +3 -0
  114. package/src/components/estimates/list/locales/fr.ts +3 -0
  115. package/src/components/estimates/list/locales/hr.ts +3 -0
  116. package/src/components/estimates/list/locales/it.ts +3 -0
  117. package/src/components/estimates/list/locales/nl.ts +3 -0
  118. package/src/components/estimates/list/locales/pl.ts +3 -0
  119. package/src/components/estimates/list/locales/pt.ts +3 -0
  120. package/src/components/estimates/list/locales/sl.ts +3 -0
  121. package/src/components/export/document-export-form.tsx +34 -34
  122. package/src/components/invoices/create/create-invoice-form.tsx +107 -5
  123. package/src/components/invoices/create/prepare-invoice-submission.ts +17 -0
  124. package/src/components/invoices/invoices.hooks.ts +32 -2
  125. package/src/components/invoices/list/list-row-actions.tsx +23 -8
  126. package/src/components/invoices/list/list-table.tsx +53 -2
  127. package/src/components/invoices/list/locales/de.ts +4 -0
  128. package/src/components/invoices/list/locales/en.ts +4 -0
  129. package/src/components/invoices/list/locales/es.ts +4 -0
  130. package/src/components/invoices/list/locales/fr.ts +4 -0
  131. package/src/components/invoices/list/locales/hr.ts +4 -0
  132. package/src/components/invoices/list/locales/it.ts +4 -0
  133. package/src/components/invoices/list/locales/nl.ts +4 -0
  134. package/src/components/invoices/list/locales/pl.ts +4 -0
  135. package/src/components/invoices/list/locales/pt.ts +4 -0
  136. package/src/components/invoices/list/locales/sl.ts +4 -0
  137. package/src/components/invoices/view/fiscalization-status-card.tsx +4 -1
  138. package/src/components/items/item-list-table/item-list-row-actions.tsx +3 -7
  139. package/src/components/items/item-list-table/item-list-row.tsx +3 -2
  140. package/src/components/items/item-list-table/item-list-table.tsx +5 -1
  141. package/src/components/recurring-invoices/create-recurring-invoice-form/create-recurring-invoice-form.tsx +418 -0
  142. package/src/components/recurring-invoices/create-recurring-invoice-form/locales/de.ts +45 -0
  143. package/src/components/recurring-invoices/create-recurring-invoice-form/locales/es.ts +44 -0
  144. package/src/components/recurring-invoices/create-recurring-invoice-form/locales/fr.ts +44 -0
  145. package/src/components/recurring-invoices/create-recurring-invoice-form/locales/hr.ts +44 -0
  146. package/src/components/recurring-invoices/create-recurring-invoice-form/locales/it.ts +44 -0
  147. package/src/components/recurring-invoices/create-recurring-invoice-form/locales/nl.ts +44 -0
  148. package/src/components/recurring-invoices/create-recurring-invoice-form/locales/pl.ts +44 -0
  149. package/src/components/recurring-invoices/create-recurring-invoice-form/locales/pt.ts +44 -0
  150. package/src/components/recurring-invoices/create-recurring-invoice-form/locales/sl.ts +44 -0
  151. package/src/components/recurring-invoices/index.ts +3 -0
  152. package/src/components/recurring-invoices/list/index.ts +2 -0
  153. package/src/components/recurring-invoices/list/list-row-actions.tsx +139 -0
  154. package/src/components/recurring-invoices/list/list-table.tsx +179 -0
  155. package/src/components/recurring-invoices/list/locales/de.ts +27 -0
  156. package/src/components/recurring-invoices/list/locales/en.ts +5 -0
  157. package/src/components/recurring-invoices/list/locales/es.ts +27 -0
  158. package/src/components/recurring-invoices/list/locales/fr.ts +27 -0
  159. package/src/components/recurring-invoices/list/locales/hr.ts +27 -0
  160. package/src/components/recurring-invoices/list/locales/it.ts +27 -0
  161. package/src/components/recurring-invoices/list/locales/nl.ts +27 -0
  162. package/src/components/recurring-invoices/list/locales/pl.ts +27 -0
  163. package/src/components/recurring-invoices/list/locales/pt.ts +27 -0
  164. package/src/components/recurring-invoices/list/locales/sl.ts +27 -0
  165. package/src/components/recurring-invoices/recurring-invoices.hooks.ts +28 -0
  166. package/src/components/table/data-table.tsx +122 -5
  167. package/src/components/table/selection-toolbar.tsx +36 -0
  168. package/src/components/tax-reports/kir-export-form.tsx +75 -55
  169. package/src/components/taxes/tax-list-table/tax-list-row-actions.tsx +3 -6
  170. package/src/components/taxes/tax-list-table/tax-list-row.tsx +3 -2
  171. package/src/components/taxes/tax-list-table/tax-list-table.tsx +5 -1
  172. package/src/components/ui/checkbox.tsx +5 -5
  173. package/src/generate-schemas.ts +46 -7
  174. package/src/generated/schemas/advanceinvoice.ts +79 -187
  175. package/src/generated/schemas/authorizeshopify_body.ts +22 -0
  176. package/src/generated/schemas/creditnote.ts +60 -88
  177. package/src/generated/schemas/customadvanceinvoice.ts +70 -97
  178. package/src/generated/schemas/customcreditnote.ts +70 -97
  179. package/src/generated/schemas/customestimate.ts +68 -97
  180. package/src/generated/schemas/custominvoice.ts +70 -97
  181. package/src/generated/schemas/entity.ts +1 -1
  182. package/src/generated/schemas/estimate.ts +67 -172
  183. package/src/generated/schemas/index.ts +39 -28
  184. package/src/generated/schemas/invoice.ts +79 -187
  185. package/src/generated/schemas/order.ts +127 -0
  186. package/src/generated/schemas/orderintegration.ts +51 -0
  187. package/src/generated/schemas/payment.ts +2 -0
  188. package/src/generated/schemas/recurringinvoice.ts +61 -0
  189. package/src/generated/schemas/registerfursrealestatepremise_body.ts +11 -7
  190. package/src/generated/schemas/renderadvanceinvoicepreview_body.ts +140 -269
  191. package/src/generated/schemas/rendercreditnotepreview_body.ts +141 -270
  192. package/src/generated/schemas/renderestimatepreview_body.ts +112 -212
  193. package/src/generated/schemas/renderinvoicepreview_body.ts +141 -270
  194. package/src/generated/schemas/webhook.ts +42 -0
  195. package/src/lib/furs-error-utils.ts +36 -0
  196. package/src/lib/schemas/advance-invoice.ts +3 -3
  197. package/src/lib/schemas/credit-note.ts +3 -3
  198. package/src/lib/schemas/estimate.ts +3 -3
  199. package/src/lib/schemas/invoice.ts +3 -3
  200. package/src/providers/white-label-provider.tsx +3 -0
@@ -1,17 +1,26 @@
1
- import { AlertCircle, CheckCircle2, Info } from "lucide-react";
1
+ import { AlertCircle, AlertTriangle, Building2, CheckCircle2, ChevronRight, Info, User } from "lucide-react";
2
2
  import { type FC, type ReactNode, useCallback, useEffect, useState } from "react";
3
+ import { useUpdateEntity } from "@/ui/components/entities/entities.hooks";
3
4
  import { Alert, AlertDescription, AlertTitle } from "@/ui/components/ui/alert";
4
5
  import { Button } from "@/ui/components/ui/button";
6
+ import { Input } from "@/ui/components/ui/input";
5
7
  import { Label } from "@/ui/components/ui/label";
6
8
  import { PageLoadingSpinner } from "@/ui/components/ui/loading-spinner";
7
9
  import { RadioGroup, RadioGroupItem } from "@/ui/components/ui/radio-group";
10
+ import { Separator } from "@/ui/components/ui/separator";
8
11
  import { Switch } from "@/ui/components/ui/switch";
9
12
  import { Tabs, TabsList, TabsTrigger } from "@/ui/components/ui/tabs";
10
13
  import { Tooltip, TooltipContent, TooltipTrigger } from "@/ui/components/ui/tooltip";
11
14
  import type { ComponentTranslationProps } from "@/ui/lib/translation";
12
15
  import { createTranslation } from "@/ui/lib/translation";
13
16
  import { cn } from "@/ui/lib/utils";
14
- import { useFinaPremises, useFinaSettings, useUpdateFinaSettings } from "./fina-settings.hooks";
17
+ import {
18
+ useFinaPremises,
19
+ useFinaSettings,
20
+ useUpdateFinaSettings,
21
+ useUpdateUserFinaSettings,
22
+ useUserFinaSettings,
23
+ } from "./fina-settings.hooks";
15
24
  import de from "./locales/de";
16
25
  import en from "./locales/en";
17
26
  import es from "./locales/es";
@@ -28,7 +37,15 @@ import { PremisesManagementSection } from "./sections/premises-management-sectio
28
37
  const translations = { sl, de, en, it, fr, es, pt, nl, pl, hr } as const;
29
38
 
30
39
  export type FinaStepType = "settings" | "certificate" | "premises" | "enable";
31
- export type FinaSectionType = "operator" | "numbering" | "certificate-upload" | "premises-list" | "enable-toggle";
40
+ export type FinaSectionType =
41
+ | "entity-info"
42
+ | "operator"
43
+ | "numbering"
44
+ | "certificate-upload"
45
+ | "premises-list"
46
+ | "enable-toggle"
47
+ | "user-operator"
48
+ | "advanced";
32
49
 
33
50
  interface FinaSettingsFormProps extends ComponentTranslationProps {
34
51
  entity: any;
@@ -77,6 +94,36 @@ export const FinaSettingsForm: FC<FinaSettingsFormProps> = ({
77
94
  [onStepChange],
78
95
  );
79
96
 
97
+ // Entity info state
98
+ const [entityTaxNumber, setEntityTaxNumber] = useState("");
99
+ const [entityAddress, setEntityAddress] = useState("");
100
+ const [entityCity, setEntityCity] = useState("");
101
+ const [entityPostCode, setEntityPostCode] = useState("");
102
+
103
+ useEffect(() => {
104
+ setEntityTaxNumber(entity.tax_number || "");
105
+ setEntityAddress(entity.address || "");
106
+ setEntityCity(entity.city || "");
107
+ setEntityPostCode(entity.post_code || "");
108
+ }, [entity.tax_number, entity.address, entity.city, entity.post_code]);
109
+
110
+ const { mutate: updateEntity, isPending: isEntityUpdatePending } = useUpdateEntity({
111
+ onSuccess: () => onSuccess?.(),
112
+ onError: (error) => onError?.(error),
113
+ });
114
+
115
+ const handleSaveEntityInfo = () => {
116
+ updateEntity({
117
+ id: entity.id,
118
+ data: {
119
+ tax_number: entityTaxNumber || null,
120
+ address: entityAddress || null,
121
+ city: entityCity || null,
122
+ post_code: entityPostCode || null,
123
+ },
124
+ });
125
+ };
126
+
80
127
  // Fetch FINA settings and premises
81
128
  const { data: finaSettings, isLoading: settingsLoading } = useFinaSettings(entity.id);
82
129
  const { data: premises, isLoading: premisesLoading } = useFinaPremises(entity.id);
@@ -90,7 +137,36 @@ export const FinaSettingsForm: FC<FinaSettingsFormProps> = ({
90
137
  },
91
138
  });
92
139
 
93
- // Form state for settings
140
+ // User FINA operator settings (per-user, stored in user.settings)
141
+ const { data: userFinaSettings, isLoading: userSettingsLoading } = useUserFinaSettings(entity.id);
142
+ const [userOperatorOib, setUserOperatorOib] = useState("");
143
+ const [userOperatorLabel, setUserOperatorLabel] = useState("");
144
+
145
+ useEffect(() => {
146
+ if (userFinaSettings) {
147
+ setUserOperatorOib(userFinaSettings.operator_oib || "");
148
+ setUserOperatorLabel(userFinaSettings.operator_label || "");
149
+ }
150
+ }, [userFinaSettings]);
151
+
152
+ const { mutate: updateUserSettings, isPending: isUserSettingsPending } = useUpdateUserFinaSettings({
153
+ onSuccess: () => onSuccess?.(),
154
+ onError: (error) => onError?.(error),
155
+ });
156
+
157
+ const handleSaveUserSettings = () => {
158
+ if (userOperatorOibError) return;
159
+ updateUserSettings({
160
+ entityId: entity.id,
161
+ data: {
162
+ operator_oib: userOperatorOib || undefined,
163
+ operator_label: userOperatorLabel || undefined,
164
+ },
165
+ });
166
+ };
167
+
168
+ // Form state for entity-level settings (API default)
169
+ const [isAdvancedOpen, setIsAdvancedOpen] = useState(false);
94
170
  const [formData, setFormData] = useState({
95
171
  enabled: false,
96
172
  numbering_sequence: "N" as "N" | "P",
@@ -113,6 +189,12 @@ export const FinaSettingsForm: FC<FinaSettingsFormProps> = ({
113
189
  }, [finaSettings]);
114
190
 
115
191
  // Determine completion status
192
+ const hasEntityTaxNumber = !!entity.tax_number;
193
+ // Operator OIB is required by CIS protocol (minOccurs="1" in FiskalizacijaSchema.xsd)
194
+ // Can come from user settings or entity-level FINA settings
195
+ const hasOperatorSettings =
196
+ (!!userFinaSettings?.operator_oib && !!userFinaSettings?.operator_label) ||
197
+ (!!finaSettings?.operator_oib && !!finaSettings?.operator_label);
116
198
  const hasCertificate = finaSettings?.has_certificate || false;
117
199
  const certificateValid = finaSettings?.certificate_status === "valid";
118
200
  const hasPremises = (premises?.length || 0) > 0;
@@ -120,16 +202,23 @@ export const FinaSettingsForm: FC<FinaSettingsFormProps> = ({
120
202
  hasPremises && premises?.some((premise: any) => premise.Devices && premise.Devices.length > 0);
121
203
 
122
204
  const finaEnabled = finaSettings?.enabled || false;
123
- const canAccessPremises = hasCertificate && certificateValid;
124
- const canAccessEnable = certificateValid && hasPremises && hasPremiseWithDevice;
205
+ const canAccessCertificate = hasEntityTaxNumber && hasOperatorSettings;
206
+ const canAccessPremises = hasEntityTaxNumber && hasOperatorSettings && hasCertificate && certificateValid;
207
+ const canAccessEnable =
208
+ hasEntityTaxNumber && hasOperatorSettings && certificateValid && hasPremises && hasPremiseWithDevice;
125
209
 
126
210
  const steps = [
127
- { id: "settings" as const, title: translate("General Settings"), complete: true, unlocked: true },
211
+ {
212
+ id: "settings" as const,
213
+ title: translate("General Settings"),
214
+ complete: hasEntityTaxNumber && hasOperatorSettings,
215
+ unlocked: true,
216
+ },
128
217
  {
129
218
  id: "certificate" as const,
130
219
  title: translate("Certificate"),
131
220
  complete: hasCertificate && certificateValid,
132
- unlocked: true,
221
+ unlocked: canAccessCertificate,
133
222
  },
134
223
  {
135
224
  id: "premises" as const,
@@ -196,10 +285,23 @@ export const FinaSettingsForm: FC<FinaSettingsFormProps> = ({
196
285
 
197
286
  const isSandboxMode = entity.environment === "sandbox";
198
287
 
288
+ const userOperatorOibError =
289
+ userOperatorOib !== "" && !/^\d{11}$/.test(userOperatorOib) ? translate("OIB must be exactly 11 digits") : "";
290
+
291
+ const operatorOibError =
292
+ formData.operator_oib !== "" && !/^\d{11}$/.test(formData.operator_oib)
293
+ ? translate("OIB must be exactly 11 digits")
294
+ : "";
295
+
199
296
  const handleSaveSettings = () => {
297
+ if (operatorOibError) return;
200
298
  updateSettings({
201
299
  entityId: entity.id,
202
- data: formData,
300
+ data: {
301
+ ...formData,
302
+ operator_oib: formData.operator_oib || undefined,
303
+ operator_label: formData.operator_label || undefined,
304
+ },
203
305
  });
204
306
  };
205
307
 
@@ -218,10 +320,24 @@ export const FinaSettingsForm: FC<FinaSettingsFormProps> = ({
218
320
  const isLocked = !step.unlocked;
219
321
  let tooltipText = "";
220
322
  if (isLocked) {
221
- if (step.id === "premises") {
222
- tooltipText = translate("Upload and validate digital certificate first");
323
+ if (step.id === "certificate") {
324
+ if (!hasEntityTaxNumber) {
325
+ tooltipText = translate("Set entity OIB in General Settings first");
326
+ } else {
327
+ tooltipText = translate("Set operator OIB and label in General Settings first");
328
+ }
329
+ } else if (step.id === "premises") {
330
+ if (!hasEntityTaxNumber) {
331
+ tooltipText = translate("Set entity OIB in General Settings first");
332
+ } else if (!hasOperatorSettings) {
333
+ tooltipText = translate("Set operator OIB and label in General Settings first");
334
+ } else {
335
+ tooltipText = translate("Upload and validate digital certificate first");
336
+ }
223
337
  } else if (step.id === "enable") {
224
- if (!certificateValid) {
338
+ if (!hasEntityTaxNumber || !hasOperatorSettings) {
339
+ tooltipText = translate("Complete General Settings first");
340
+ } else if (!certificateValid) {
225
341
  tooltipText = translate("Upload and validate digital certificate first");
226
342
  } else if (!hasPremises) {
227
343
  tooltipText = translate("Register at least one business premise first");
@@ -295,6 +411,97 @@ export const FinaSettingsForm: FC<FinaSettingsFormProps> = ({
295
411
  {/* Settings step */}
296
412
  {activeStep === "settings" && (
297
413
  <div className="space-y-6">
414
+ {wrapSection(
415
+ "entity-info",
416
+ <div className="space-y-4">
417
+ <div className="flex items-center gap-3">
418
+ <div className="flex h-10 w-10 items-center justify-center rounded-lg bg-orange-500/10">
419
+ <Building2 className="h-5 w-5 text-orange-600 dark:text-orange-400" />
420
+ </div>
421
+ <div>
422
+ <h3 className="font-semibold text-lg">{translate("Entity Information")}</h3>
423
+ <p className="text-muted-foreground text-sm">
424
+ {translate("Required company details for FINA fiscalization")}
425
+ </p>
426
+ </div>
427
+ </div>
428
+
429
+ {!entity.tax_number && (
430
+ <Alert variant="destructive">
431
+ <AlertTriangle className="h-4 w-4" />
432
+ <AlertDescription>{translate("Entity OIB is required for FINA fiscalization")}</AlertDescription>
433
+ </Alert>
434
+ )}
435
+
436
+ <div className="space-y-4">
437
+ <div>
438
+ <label htmlFor="entity-tax-number" className="font-medium text-sm">
439
+ {translate("Entity OIB")}
440
+ </label>
441
+ <Input
442
+ id="entity-tax-number"
443
+ placeholder="12345678901"
444
+ value={entityTaxNumber}
445
+ onChange={(e) => setEntityTaxNumber(e.target.value)}
446
+ className={cn("mt-2 h-10", !entity.tax_number && "border-destructive")}
447
+ maxLength={11}
448
+ />
449
+ <p className="mt-1 text-muted-foreground text-xs">
450
+ {translate("Your company's OIB (must match FINA certificate)")}
451
+ </p>
452
+ </div>
453
+
454
+ <div>
455
+ <label htmlFor="entity-address" className="font-medium text-sm">
456
+ {translate("Address")}
457
+ </label>
458
+ <Input
459
+ id="entity-address"
460
+ value={entityAddress}
461
+ onChange={(e) => setEntityAddress(e.target.value)}
462
+ className="mt-2 h-10"
463
+ />
464
+ </div>
465
+
466
+ <div className="grid grid-cols-2 gap-4">
467
+ <div>
468
+ <label htmlFor="entity-post-code" className="font-medium text-sm">
469
+ {translate("Post Code")}
470
+ </label>
471
+ <Input
472
+ id="entity-post-code"
473
+ value={entityPostCode}
474
+ onChange={(e) => setEntityPostCode(e.target.value)}
475
+ className="mt-2 h-10"
476
+ />
477
+ </div>
478
+ <div>
479
+ <label htmlFor="entity-city" className="font-medium text-sm">
480
+ {translate("City")}
481
+ </label>
482
+ <Input
483
+ id="entity-city"
484
+ value={entityCity}
485
+ onChange={(e) => setEntityCity(e.target.value)}
486
+ className="mt-2 h-10"
487
+ />
488
+ </div>
489
+ </div>
490
+
491
+ <Button
492
+ type="button"
493
+ onClick={handleSaveEntityInfo}
494
+ disabled={isEntityUpdatePending}
495
+ className="cursor-pointer"
496
+ >
497
+ {isEntityUpdatePending ? translate("Saving...") : translate("Save Entity Info")}
498
+ </Button>
499
+ </div>
500
+ </div>,
501
+ )}
502
+
503
+ <Separator />
504
+
298
505
  {wrapSection(
299
506
  "numbering",
300
507
  <div className="space-y-4">
@@ -319,6 +526,8 @@ export const FinaSettingsForm: FC<FinaSettingsFormProps> = ({
319
526
  </div>,
320
527
  )}
321
528
 
529
+ <Separator />
530
+
322
531
  {wrapSection(
323
532
  "operator",
324
533
  <div className="space-y-4">
@@ -332,36 +541,150 @@ export const FinaSettingsForm: FC<FinaSettingsFormProps> = ({
332
541
  <Label>{translate("Entity is registered in the Croatian PDV (VAT) system")}</Label>
333
542
  </div>
334
543
  </div>
335
- <div>
336
- <Label className="font-medium text-sm">{translate("Operator OIB")}</Label>
337
- <input
338
- type="text"
339
- value={formData.operator_oib}
340
- onChange={(e) => setFormData((prev) => ({ ...prev, operator_oib: e.target.value }))}
341
- placeholder={translate("OIB of the operator (11 digits, optional)")}
342
- className="mt-1 block w-full rounded-md border border-input bg-background px-3 py-2 text-sm"
343
- maxLength={11}
344
- />
345
- </div>
346
- <div>
347
- <Label className="font-medium text-sm">{translate("Operator Label")}</Label>
348
- <input
349
- type="text"
350
- value={formData.operator_label}
351
- onChange={(e) => setFormData((prev) => ({ ...prev, operator_label: e.target.value }))}
352
- placeholder={translate("Descriptive label for the operator (optional)")}
353
- className="mt-1 block w-full rounded-md border border-input bg-background px-3 py-2 text-sm"
354
- />
355
- </div>
356
544
  </div>,
357
545
  )}
358
546
 
359
547
  <div className="grid items-start gap-6 lg:grid-cols-[1fr_280px]">
360
- <Button onClick={handleSaveSettings} disabled={isPending}>
548
+ <Button onClick={handleSaveSettings} disabled={isPending || !!operatorOibError}>
361
549
  {isPending ? translate("Saving...") : translate("Save Settings")}
362
550
  </Button>
363
551
  <div className="hidden lg:block" />
364
552
  </div>
553
+
554
+ <Separator />
555
+
556
+ {/* Per-user operator settings */}
557
+ {wrapSection(
558
+ "user-operator",
559
+ <div className="space-y-4">
560
+ <div className="flex items-center gap-3">
561
+ <div className="flex h-10 w-10 items-center justify-center rounded-lg bg-blue-500/10">
562
+ <User className="h-5 w-5 text-blue-600 dark:text-blue-400" />
563
+ </div>
564
+ <div>
565
+ <h3 className="font-semibold text-lg">{translate("Your Operator Settings")}</h3>
566
+ <p className="text-muted-foreground text-sm">
567
+ {translate("Your personal operator info for FINA invoices")}
568
+ </p>
569
+ </div>
570
+ </div>
571
+
572
+ {(!userFinaSettings?.operator_oib || !userFinaSettings?.operator_label) && (
573
+ <Alert variant="destructive">
574
+ <AlertTriangle className="h-4 w-4" />
575
+ <AlertDescription>
576
+ {translate("Operator OIB and label are required for FINA fiscalization")}
577
+ </AlertDescription>
578
+ </Alert>
579
+ )}
580
+
581
+ <div className="space-y-4">
582
+ <div>
583
+ <Label className="font-medium text-sm">{translate("Operator OIB")}</Label>
584
+ <Input
585
+ type="text"
586
+ value={userOperatorOib}
587
+ onChange={(e) => {
588
+ const val = e.target.value.replace(/[^0-9]/g, "");
589
+ setUserOperatorOib(val);
590
+ }}
591
+ placeholder={translate("OIB of the operator (11 digits)")}
592
+ className={cn("mt-1", userOperatorOibError && "border-destructive")}
593
+ maxLength={11}
594
+ disabled={userSettingsLoading}
595
+ />
596
+ {userOperatorOibError && <p className="mt-1 text-destructive text-xs">{userOperatorOibError}</p>}
597
+ </div>
598
+ <div>
599
+ <Label className="font-medium text-sm">{translate("Operator Label")}</Label>
600
+ <Input
601
+ type="text"
602
+ value={userOperatorLabel}
603
+ onChange={(e) => setUserOperatorLabel(e.target.value)}
604
+ placeholder={translate("e.g. Cashier 1")}
605
+ className="mt-1"
606
+ disabled={userSettingsLoading}
607
+ />
608
+ </div>
609
+
610
+ <Button
611
+ type="button"
612
+ onClick={handleSaveUserSettings}
613
+ disabled={isUserSettingsPending || userSettingsLoading || !!userOperatorOibError}
614
+ className="cursor-pointer"
615
+ >
616
+ {isUserSettingsPending ? translate("Saving...") : translate("Save Operator Settings")}
617
+ </Button>
618
+ </div>
619
+ </div>,
620
+ )}
621
+
622
+ <Separator />
623
+
624
+ {/* API Default Operator (advanced/entity-level) */}
625
+ {wrapSection(
626
+ "advanced",
627
+ <div>
628
+ <button
629
+ type="button"
630
+ onClick={() => setIsAdvancedOpen(!isAdvancedOpen)}
631
+ className="flex w-full items-center gap-2 py-2 text-muted-foreground hover:text-foreground"
632
+ >
633
+ <ChevronRight className={cn("h-4 w-4 transition-transform", isAdvancedOpen && "rotate-90")} />
634
+ <span className="font-medium text-sm">{translate("Advanced Settings")}</span>
635
+ </button>
636
+ {isAdvancedOpen && (
637
+ <div className="pt-4">
638
+ <div className="space-y-4 rounded-lg border p-4">
639
+ <div>
640
+ <h4 className="font-medium text-sm">{translate("API Default Operator")}</h4>
641
+ <p className="text-muted-foreground text-xs">
642
+ {translate("Default operator settings for API key usage (when no user context)")}
643
+ </p>
644
+ </div>
645
+
646
+ <div className="space-y-4">
647
+ <div>
648
+ <Label className="text-sm">{translate("Operator OIB")}</Label>
649
+ <Input
650
+ type="text"
651
+ value={formData.operator_oib}
652
+ onChange={(e) => {
653
+ const val = e.target.value.replace(/[^0-9]/g, "");
654
+ setFormData((prev) => ({ ...prev, operator_oib: val }));
655
+ }}
656
+ placeholder={translate("OIB of the operator (11 digits)")}
657
+ className={cn("mt-1", operatorOibError && "border-destructive")}
658
+ maxLength={11}
659
+ />
660
+ {operatorOibError && <p className="mt-1 text-destructive text-xs">{operatorOibError}</p>}
661
+ <p className="mt-1 text-muted-foreground text-xs">
662
+ {translate("OIB for API key usage (optional)")}
663
+ </p>
664
+ </div>
665
+ <div>
666
+ <Label className="text-sm">{translate("Operator Label")}</Label>
667
+ <Input
668
+ type="text"
669
+ value={formData.operator_label}
670
+ onChange={(e) => setFormData((prev) => ({ ...prev, operator_label: e.target.value }))}
671
+ placeholder="API Default"
672
+ className="mt-1"
673
+ />
674
+ <p className="mt-1 text-muted-foreground text-xs">
675
+ {translate("Operator label for API key usage (optional)")}
676
+ </p>
677
+ </div>
678
+ </div>
679
+
680
+ <Button onClick={handleSaveSettings} disabled={isPending || !!operatorOibError} size="sm">
681
+ {isPending ? translate("Saving...") : translate("Save Settings")}
682
+ </Button>
683
+ </div>
684
+ </div>
685
+ )}
686
+ </div>,
687
+ )}
365
688
  </div>
366
689
  )}
367
690
 
@@ -431,7 +754,14 @@ export const FinaSettingsForm: FC<FinaSettingsFormProps> = ({
431
754
  setFormData((prev) => ({ ...prev, enabled: checked }));
432
755
  updateSettings({
433
756
  entityId: entity.id,
434
- data: { ...formData, enabled: checked },
757
+ data: checked
758
+ ? {
759
+ ...formData,
760
+ enabled: true,
761
+ operator_oib: formData.operator_oib || undefined,
762
+ operator_label: formData.operator_label || undefined,
763
+ }
764
+ : { enabled: false },
435
765
  });
436
766
  }}
437
767
  />