@spaceinvoices/react-ui 0.4.2 → 0.4.3

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 (116) hide show
  1. package/cli/dist/index.js +1 -1
  2. package/package.json +1 -1
  3. package/src/components/advance-invoices/list/list-table.tsx +60 -0
  4. package/src/components/advance-invoices/list/locales/de.ts +1 -0
  5. package/src/components/advance-invoices/list/locales/en.ts +1 -0
  6. package/src/components/advance-invoices/list/locales/es.ts +1 -0
  7. package/src/components/advance-invoices/list/locales/fr.ts +1 -0
  8. package/src/components/advance-invoices/list/locales/hr.ts +1 -0
  9. package/src/components/advance-invoices/list/locales/it.ts +1 -0
  10. package/src/components/advance-invoices/list/locales/nl.ts +1 -0
  11. package/src/components/advance-invoices/list/locales/pl.ts +1 -0
  12. package/src/components/advance-invoices/list/locales/pt.ts +1 -0
  13. package/src/components/advance-invoices/list/locales/sl.ts +1 -0
  14. package/src/components/credit-notes/list/list-table.tsx +34 -6
  15. package/src/components/credit-notes/list/locales/de.ts +1 -1
  16. package/src/components/credit-notes/list/locales/en.ts +2 -0
  17. package/src/components/credit-notes/list/locales/es.ts +1 -1
  18. package/src/components/credit-notes/list/locales/fr.ts +1 -1
  19. package/src/components/credit-notes/list/locales/hr.ts +1 -1
  20. package/src/components/credit-notes/list/locales/it.ts +1 -1
  21. package/src/components/credit-notes/list/locales/nl.ts +1 -1
  22. package/src/components/credit-notes/list/locales/pl.ts +1 -1
  23. package/src/components/credit-notes/list/locales/pt.ts +1 -1
  24. package/src/components/credit-notes/list/locales/sl.ts +1 -1
  25. package/src/components/dashboard/collection-rate-card/use-collection-rate.ts +2 -2
  26. package/src/components/dashboard/invoice-status-chart/use-invoice-status.ts +3 -3
  27. package/src/components/dashboard/payment-methods-chart/use-payment-methods.ts +1 -1
  28. package/src/components/dashboard/payment-trend-chart/use-payment-trend.ts +1 -1
  29. package/src/components/dashboard/revenue-trend-chart/use-revenue-trend.ts +1 -1
  30. package/src/components/dashboard/shared/use-revenue-data.ts +4 -4
  31. package/src/components/dashboard/shared/use-stats-counts.ts +4 -4
  32. package/src/components/dashboard/shared/use-stats-query.ts +1 -1
  33. package/src/components/dashboard/tax-collected-card/use-tax-collected.ts +2 -2
  34. package/src/components/dashboard/top-customers-chart/use-top-customers.ts +1 -1
  35. package/src/components/delivery-notes/create/create-delivery-note-form.tsx +332 -0
  36. package/src/components/delivery-notes/create/locales/de.ts +50 -0
  37. package/src/components/delivery-notes/create/locales/es.ts +49 -0
  38. package/src/components/delivery-notes/create/locales/fr.ts +50 -0
  39. package/src/components/delivery-notes/create/locales/hr.ts +49 -0
  40. package/src/components/delivery-notes/create/locales/it.ts +49 -0
  41. package/src/components/delivery-notes/create/locales/nl.ts +50 -0
  42. package/src/components/delivery-notes/create/locales/pl.ts +49 -0
  43. package/src/components/delivery-notes/create/locales/pt.ts +50 -0
  44. package/src/components/delivery-notes/create/locales/sl.ts +49 -0
  45. package/src/components/delivery-notes/create/prepare-delivery-note-submission.ts +38 -0
  46. package/src/components/delivery-notes/create/use-delivery-note-customer-form.ts +1 -0
  47. package/src/components/delivery-notes/delivery-notes.hooks.ts +15 -0
  48. package/src/components/delivery-notes/list/index.ts +3 -0
  49. package/src/components/delivery-notes/list/list-row-actions.tsx +103 -0
  50. package/src/components/delivery-notes/list/list-table.tsx +214 -0
  51. package/src/components/delivery-notes/list/locales/de.ts +9 -0
  52. package/src/components/delivery-notes/list/locales/en.ts +11 -0
  53. package/src/components/delivery-notes/list/locales/es.ts +9 -0
  54. package/src/components/delivery-notes/list/locales/fr.ts +9 -0
  55. package/src/components/delivery-notes/list/locales/hr.ts +9 -0
  56. package/src/components/delivery-notes/list/locales/it.ts +9 -0
  57. package/src/components/delivery-notes/list/locales/nl.ts +9 -0
  58. package/src/components/delivery-notes/list/locales/pl.ts +9 -0
  59. package/src/components/delivery-notes/list/locales/pt.ts +9 -0
  60. package/src/components/delivery-notes/list/locales/sl.ts +9 -0
  61. package/src/components/delivery-notes/list/use-delivery-note-download.ts +63 -0
  62. package/src/components/documents/create/document-details-section.tsx +36 -32
  63. package/src/components/documents/create/live-preview.tsx +37 -10
  64. package/src/components/documents/create/prepare-document-submission.ts +1 -1
  65. package/src/components/documents/documents.hooks.ts +2 -1
  66. package/src/components/documents/shared/document-preview-display.tsx +12 -5
  67. package/src/components/documents/types.ts +10 -1
  68. package/src/components/documents/view/document-details-card.tsx +3 -3
  69. package/src/components/documents/view/document-payments-list.tsx +3 -3
  70. package/src/components/documents/view/document-relations-list.tsx +105 -0
  71. package/src/components/documents/view/locales/de.ts +21 -0
  72. package/src/components/documents/view/locales/es.ts +21 -0
  73. package/src/components/documents/view/locales/fr.ts +21 -0
  74. package/src/components/documents/view/locales/hr.ts +21 -0
  75. package/src/components/documents/view/locales/it.ts +21 -0
  76. package/src/components/documents/view/locales/nl.ts +21 -0
  77. package/src/components/documents/view/locales/pl.ts +21 -0
  78. package/src/components/documents/view/locales/pt.ts +21 -0
  79. package/src/components/documents/view/locales/sl.ts +21 -0
  80. package/src/components/documents/view/use-document-download.ts +5 -3
  81. package/src/components/entities/fina-settings-form/fina-settings-form.tsx +77 -65
  82. package/src/components/entities/fina-settings-form/fina-settings.hooks.ts +7 -2
  83. package/src/components/entities/furs-settings-form/furs-settings-form.tsx +10 -1
  84. package/src/components/entities/furs-settings-form/furs-settings.hooks.ts +7 -2
  85. package/src/components/entities/furs-settings-form/sections/general-settings-section.tsx +12 -4
  86. package/src/components/invoices/invoices-furs.hooks.ts +24 -9
  87. package/src/components/invoices/list/list-row-actions.tsx +3 -3
  88. package/src/components/invoices/list/list-table.tsx +68 -3
  89. package/src/components/invoices/list/locales/de.ts +1 -0
  90. package/src/components/invoices/list/locales/en.ts +1 -0
  91. package/src/components/invoices/list/locales/es.ts +1 -0
  92. package/src/components/invoices/list/locales/fr.ts +1 -0
  93. package/src/components/invoices/list/locales/hr.ts +1 -0
  94. package/src/components/invoices/list/locales/it.ts +1 -0
  95. package/src/components/invoices/list/locales/nl.ts +1 -0
  96. package/src/components/invoices/list/locales/pl.ts +1 -0
  97. package/src/components/invoices/list/locales/pt.ts +1 -0
  98. package/src/components/invoices/list/locales/sl.ts +1 -0
  99. package/src/components/items/item-list-table/item-list-row-actions.tsx +3 -2
  100. package/src/components/items/item-list-table/item-list-row.tsx +3 -2
  101. package/src/generated/schemas/deliverynote.ts +134 -0
  102. package/src/generated/schemas/entity.ts +4 -0
  103. package/src/generated/schemas/index.ts +3 -0
  104. package/src/generated/schemas/order.ts +5 -3
  105. package/src/generated/schemas/payment.ts +22 -2
  106. package/src/generated/schemas/renderadvanceinvoicepreview_body.ts +1 -0
  107. package/src/generated/schemas/rendercreditnotepreview_body.ts +1 -0
  108. package/src/generated/schemas/renderdeliverynotepreview_body.ts +185 -0
  109. package/src/generated/schemas/renderestimatepreview_body.ts +1 -0
  110. package/src/generated/schemas/renderinvoicepreview_body.ts +1 -0
  111. package/src/generated/schemas/startpdfexport_body.ts +18 -2
  112. package/src/generated/schemas/userfinasettings.ts +19 -0
  113. package/src/generated/schemas/webhook.ts +12 -0
  114. package/src/hooks/use-duplicate-document.ts +11 -3
  115. package/src/hooks/use-next-document-number.ts +2 -2
  116. package/src/providers/sdk-provider.tsx +5 -7
@@ -118,4 +118,25 @@ export default {
118
118
  Dutch: "Nizozemščina",
119
119
  Polish: "Poljščina",
120
120
  Croatian: "Hrvaščina",
121
+
122
+ // Linked documents
123
+ "Linked documents": "Povezani dokumenti",
124
+ "No linked documents": "Ni povezanih dokumentov",
125
+ Invoice: "Račun",
126
+ Estimate: "Predračun",
127
+ "Credit note": "Dobropis",
128
+ "Advance invoice": "Avansni račun",
129
+ "Delivery note": "Dobavnica",
130
+ "Credit for": "Dobropis za",
131
+ "Converted from": "Pretvorjeno iz",
132
+ "Converted to": "Pretvorjeno v",
133
+ "Has credit": "Ima dobropis",
134
+ "Advance applied": "Avans uporabljen",
135
+ "Applied to": "Uporabljen za",
136
+ "Delivered for": "Dostavljeno za",
137
+ "Has delivery": "Ima dobavnico",
138
+ Fulfills: "Izpolnjuje",
139
+ "Fulfilled by": "Izpolnjeno z",
140
+ References: "Sklicuje se na",
141
+ View: "Poglej",
121
142
  } as const;
@@ -1,10 +1,10 @@
1
- import type { AdvanceInvoice, CreditNote, Estimate, Invoice } from "@spaceinvoices/js-sdk";
1
+ import type { AdvanceInvoice, CreditNote, DeliveryNote, Estimate, Invoice } from "@spaceinvoices/js-sdk";
2
2
  import { useState } from "react";
3
3
  import { useEntities } from "@/ui/providers/entities-context";
4
4
  import { useSDK } from "@/ui/providers/sdk-provider";
5
5
 
6
- type Document = Invoice | Estimate | CreditNote | AdvanceInvoice;
7
- type DocumentType = "invoice" | "estimate" | "credit_note" | "advance_invoice";
6
+ type Document = Invoice | Estimate | CreditNote | AdvanceInvoice | DeliveryNote;
7
+ type DocumentType = "invoice" | "estimate" | "credit_note" | "advance_invoice" | "delivery_note";
8
8
 
9
9
  // Document type labels for PDF filename
10
10
  const TYPE_LABELS: Record<string, string> = {
@@ -12,6 +12,7 @@ const TYPE_LABELS: Record<string, string> = {
12
12
  estimate: "Estimate",
13
13
  credit_note: "Credit Note",
14
14
  advance_invoice: "Advance Invoice",
15
+ delivery_note: "Delivery Note",
15
16
  };
16
17
 
17
18
  interface UseDocumentDownloadOptions {
@@ -90,6 +91,7 @@ export function useDocumentDownload({
90
91
  advance_invoice: "advance_invoice",
91
92
  credit_note: "credit_note",
92
93
  estimate: "estimate",
94
+ delivery_note: "delivery_note",
93
95
  };
94
96
 
95
97
  // e-SLOG download - cast to any since the SDK structure may vary
@@ -54,6 +54,11 @@ interface FinaSettingsFormProps extends ComponentTranslationProps {
54
54
  initialStep?: FinaStepType;
55
55
  onStepChange?: (step: FinaStepType) => void;
56
56
  renderSection?: (section: FinaSectionType, content: ReactNode) => ReactNode;
57
+ /**
58
+ * Hide user-specific operator section (for embed/API key contexts without user session).
59
+ * When true, the "Advanced Settings" entity-level operator fields are auto-expanded instead.
60
+ */
61
+ hideUserOperatorSection?: boolean;
57
62
  }
58
63
 
59
64
  /**
@@ -75,6 +80,7 @@ export const FinaSettingsForm: FC<FinaSettingsFormProps> = ({
75
80
  initialStep = "settings",
76
81
  onStepChange,
77
82
  renderSection,
83
+ hideUserOperatorSection,
78
84
  }) => {
79
85
  const [activeStep, setActiveStep] = useState<FinaStepType>(initialStep);
80
86
  const [hasInitializedStep, setHasInitializedStep] = useState(false);
@@ -138,7 +144,9 @@ export const FinaSettingsForm: FC<FinaSettingsFormProps> = ({
138
144
  });
139
145
 
140
146
  // User FINA operator settings (per-user, stored in user.settings)
141
- const { data: userFinaSettings, isLoading: userSettingsLoading } = useUserFinaSettings(entity.id);
147
+ const { data: userFinaSettings, isLoading: userSettingsLoading } = useUserFinaSettings(entity.id, {
148
+ enabled: !hideUserOperatorSection,
149
+ });
142
150
  const [userOperatorOib, setUserOperatorOib] = useState("");
143
151
  const [userOperatorLabel, setUserOperatorLabel] = useState("");
144
152
 
@@ -166,7 +174,7 @@ export const FinaSettingsForm: FC<FinaSettingsFormProps> = ({
166
174
  };
167
175
 
168
176
  // Form state for entity-level settings (API default)
169
- const [isAdvancedOpen, setIsAdvancedOpen] = useState(false);
177
+ const [isAdvancedOpen, setIsAdvancedOpen] = useState(!!hideUserOperatorSection);
170
178
  const [formData, setFormData] = useState({
171
179
  enabled: false,
172
180
  numbering_sequence: "N" as "N" | "P",
@@ -553,73 +561,77 @@ export const FinaSettingsForm: FC<FinaSettingsFormProps> = ({
553
561
 
554
562
  <Separator />
555
563
 
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>
564
+ {/* Per-user operator settings (hidden in embed/API key mode) */}
565
+ {!hideUserOperatorSection && (
566
+ <>
567
+ {wrapSection(
568
+ "user-operator",
569
+ <div className="space-y-4">
570
+ <div className="flex items-center gap-3">
571
+ <div className="flex h-10 w-10 items-center justify-center rounded-lg bg-blue-500/10">
572
+ <User className="h-5 w-5 text-blue-600 dark:text-blue-400" />
573
+ </div>
574
+ <div>
575
+ <h3 className="font-semibold text-lg">{translate("Your Operator Settings")}</h3>
576
+ <p className="text-muted-foreground text-sm">
577
+ {translate("Your personal operator info for FINA invoices")}
578
+ </p>
579
+ </div>
580
+ </div>
571
581
 
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
- )}
582
+ {(!userFinaSettings?.operator_oib || !userFinaSettings?.operator_label) && (
583
+ <Alert variant="destructive">
584
+ <AlertTriangle className="h-4 w-4" />
585
+ <AlertDescription>
586
+ {translate("Operator OIB and label are required for FINA fiscalization")}
587
+ </AlertDescription>
588
+ </Alert>
589
+ )}
580
590
 
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>
591
+ <div className="space-y-4">
592
+ <div>
593
+ <Label className="font-medium text-sm">{translate("Operator OIB")}</Label>
594
+ <Input
595
+ type="text"
596
+ value={userOperatorOib}
597
+ onChange={(e) => {
598
+ const val = e.target.value.replace(/[^0-9]/g, "");
599
+ setUserOperatorOib(val);
600
+ }}
601
+ placeholder={translate("OIB of the operator (11 digits)")}
602
+ className={cn("mt-1", userOperatorOibError && "border-destructive")}
603
+ maxLength={11}
604
+ disabled={userSettingsLoading}
605
+ />
606
+ {userOperatorOibError && <p className="mt-1 text-destructive text-xs">{userOperatorOibError}</p>}
607
+ </div>
608
+ <div>
609
+ <Label className="font-medium text-sm">{translate("Operator Label")}</Label>
610
+ <Input
611
+ type="text"
612
+ value={userOperatorLabel}
613
+ onChange={(e) => setUserOperatorLabel(e.target.value)}
614
+ placeholder={translate("e.g. Cashier 1")}
615
+ className="mt-1"
616
+ disabled={userSettingsLoading}
617
+ />
618
+ </div>
609
619
 
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
- )}
620
+ <Button
621
+ type="button"
622
+ onClick={handleSaveUserSettings}
623
+ disabled={isUserSettingsPending || userSettingsLoading || !!userOperatorOibError}
624
+ className="cursor-pointer"
625
+ >
626
+ {isUserSettingsPending ? translate("Saving...") : translate("Save Operator Settings")}
627
+ </Button>
628
+ </div>
629
+ </div>,
630
+ )}
621
631
 
622
- <Separator />
632
+ <Separator />
633
+ </>
634
+ )}
623
635
 
624
636
  {/* API Default Operator (advanced/entity-level) */}
625
637
  {wrapSection(
@@ -251,8 +251,13 @@ function useCurrentUser(options?: Omit<UseQueryOptions<any>, "queryKey" | "query
251
251
  * Hook: Get user FINA settings for a specific entity
252
252
  * Extracts FINA settings from user.settings using the fina_<entity_id> key
253
253
  */
254
- export function useUserFinaSettings(entityId: string) {
255
- const { data: user, isLoading, error, ...rest } = useCurrentUser();
254
+ export function useUserFinaSettings(entityId: string, options?: { enabled?: boolean }) {
255
+ const {
256
+ data: user,
257
+ isLoading,
258
+ error,
259
+ ...rest
260
+ } = useCurrentUser(options?.enabled === false ? { enabled: false } : undefined);
256
261
 
257
262
  const userFinaSettings = useMemo<UserFinaSettings | null>(() => {
258
263
  if (!user?.settings) return null;
@@ -69,6 +69,11 @@ interface FursSettingsFormProps extends ComponentTranslationProps {
69
69
  * Optional render prop to wrap each section with help content
70
70
  */
71
71
  renderSection?: (section: SectionType, content: ReactNode) => ReactNode;
72
+ /**
73
+ * Hide user-specific operator section (for embed/API key contexts without user session).
74
+ * When true, the "Advanced Settings" entity-level operator fields are auto-expanded instead.
75
+ */
76
+ hideUserOperatorSection?: boolean;
72
77
  }
73
78
 
74
79
  /**
@@ -92,6 +97,7 @@ export const FursSettingsForm: FC<FursSettingsFormProps> = ({
92
97
  initialStep = "settings",
93
98
  onStepChange,
94
99
  renderSection,
100
+ hideUserOperatorSection,
95
101
  }) => {
96
102
  // Step navigation state (can be controlled via props for URL sync)
97
103
  const [activeStep, setActiveStep] = useState<StepType>(initialStep);
@@ -117,7 +123,9 @@ export const FursSettingsForm: FC<FursSettingsFormProps> = ({
117
123
  // Fetch FURS settings and premises
118
124
  const { data: fursSettings, isLoading: settingsLoading } = useFursSettings(entity.id);
119
125
  const { data: premises, isLoading: premisesLoading } = useFursPremises(entity.id);
120
- const { data: userFursSettings } = useUserFursSettings(entity.id);
126
+ const { data: userFursSettings } = useUserFursSettings(entity.id, {
127
+ enabled: !hideUserOperatorSection,
128
+ });
121
129
 
122
130
  const { mutate: updateSettings, isPending } = useUpdateFursSettings({
123
131
  onSuccess: () => {
@@ -388,6 +396,7 @@ export const FursSettingsForm: FC<FursSettingsFormProps> = ({
388
396
  onSuccess={onSuccess}
389
397
  onError={onError}
390
398
  wrapSection={wrapSection}
399
+ hideUserOperatorSection={hideUserOperatorSection}
391
400
  />
392
401
  )}
393
402
 
@@ -277,8 +277,13 @@ export function useCurrentUser(options?: Omit<UseQueryOptions<User>, "queryKey"
277
277
  * Hook: Get user FURS settings for a specific entity
278
278
  * Extracts FURS settings from user.settings using the furs_<entity_id> key
279
279
  */
280
- export function useUserFursSettings(entityId: string) {
281
- const { data: user, isLoading, error, ...rest } = useCurrentUser();
280
+ export function useUserFursSettings(entityId: string, options?: { enabled?: boolean }) {
281
+ const {
282
+ data: user,
283
+ isLoading,
284
+ error,
285
+ ...rest
286
+ } = useCurrentUser(options?.enabled === false ? { enabled: false } : undefined);
282
287
 
283
288
  const userFursSettings = useMemo<UserFursSettings | null>(() => {
284
289
  if (!user?.settings) return null;
@@ -20,6 +20,7 @@ interface GeneralSettingsSectionProps {
20
20
  onSuccess?: () => void;
21
21
  onError?: (error: unknown) => void;
22
22
  wrapSection?: (section: SectionType, content: ReactNode) => ReactNode;
23
+ hideUserOperatorSection?: boolean;
23
24
  }
24
25
 
25
26
  export const GeneralSettingsSection: FC<GeneralSettingsSectionProps> = ({
@@ -29,9 +30,10 @@ export const GeneralSettingsSection: FC<GeneralSettingsSectionProps> = ({
29
30
  onSuccess,
30
31
  onError,
31
32
  wrapSection,
33
+ hideUserOperatorSection,
32
34
  }) => {
33
35
  const wrap = (section: SectionType, content: ReactNode) => (wrapSection ? wrapSection(section, content) : content);
34
- const [isAdvancedOpen, setIsAdvancedOpen] = useState(false);
36
+ const [isAdvancedOpen, setIsAdvancedOpen] = useState(!!hideUserOperatorSection);
35
37
 
36
38
  // Entity info (local state for form)
37
39
  const [entityTaxNumber, setEntityTaxNumber] = useState("");
@@ -65,7 +67,9 @@ export const GeneralSettingsSection: FC<GeneralSettingsSectionProps> = ({
65
67
  };
66
68
 
67
69
  // User operator settings (local state for form)
68
- const { data: userFursSettings, isLoading: userSettingsLoading } = useUserFursSettings(entity.id);
70
+ const { data: userFursSettings, isLoading: userSettingsLoading } = useUserFursSettings(entity.id, {
71
+ enabled: !hideUserOperatorSection,
72
+ });
69
73
  const [operatorTaxNumber, setOperatorTaxNumber] = useState("");
70
74
  const [operatorLabel, setOperatorLabel] = useState("");
71
75
 
@@ -375,8 +379,12 @@ export const GeneralSettingsSection: FC<GeneralSettingsSectionProps> = ({
375
379
  <div className="space-y-6">
376
380
  {wrap("entity-info", entityInfoContent)}
377
381
  <Separator />
378
- {wrap("operator", operatorContent)}
379
- <Separator />
382
+ {!hideUserOperatorSection && (
383
+ <>
384
+ {wrap("operator", operatorContent)}
385
+ <Separator />
386
+ </>
387
+ )}
380
388
  {wrap("fiscalization", fiscalizationContent)}
381
389
  <Separator />
382
390
  {wrap("advanced", advancedContent)}
@@ -1,28 +1,43 @@
1
1
  import { useMutation, useQueryClient } from "@tanstack/react-query";
2
2
  import { useSDK } from "@/ui/providers/sdk-provider";
3
3
 
4
- interface VoidInvoiceParams {
5
- invoiceId: string;
4
+ interface VoidDocumentParams {
5
+ documentId: string;
6
+ documentType: "invoice" | "credit_note" | "advance_invoice";
6
7
  entityId: string;
7
8
  reason?: string;
8
9
  }
9
10
 
10
11
  /**
11
- * Hook to void an invoice
12
- * Automatically handles FURS technical cancellation for fiscalized invoices
12
+ * Hook to void a document (invoice, credit note, or advance invoice)
13
+ * Automatically handles FURS/FINA technical cancellation for fiscalized documents
13
14
  */
14
- export function useVoidInvoice() {
15
+ export function useVoidDocument() {
15
16
  const { sdk } = useSDK();
16
17
  const queryClient = useQueryClient();
17
18
 
18
19
  return useMutation({
19
- mutationFn: async ({ invoiceId, entityId, reason }: VoidInvoiceParams) => {
20
- return sdk.invoices.void(invoiceId, { reason: reason || undefined }, { entity_id: entityId });
20
+ mutationFn: async ({ documentId, documentType, entityId, reason }: VoidDocumentParams) => {
21
+ const body = { reason: reason || undefined };
22
+ const opts = { entity_id: entityId };
23
+
24
+ switch (documentType) {
25
+ case "invoice":
26
+ return sdk.invoices.void(documentId, body, opts);
27
+ case "credit_note":
28
+ return sdk.creditNotes.void(documentId, body, opts);
29
+ case "advance_invoice":
30
+ return sdk.advanceInvoices.void(documentId, body, opts);
31
+ }
21
32
  },
22
33
  onSuccess: (_, variables) => {
23
- // Invalidate invoice queries to refresh the data
24
34
  queryClient.invalidateQueries({ queryKey: ["invoices"] });
25
- queryClient.invalidateQueries({ queryKey: ["documents", "invoice", variables.invoiceId] });
35
+ queryClient.invalidateQueries({ queryKey: ["credit-notes"] });
36
+ queryClient.invalidateQueries({ queryKey: ["advance-invoices"] });
37
+ queryClient.invalidateQueries({ queryKey: ["documents", variables.documentType, variables.documentId] });
26
38
  },
27
39
  });
28
40
  }
41
+
42
+ /** @deprecated Use useVoidDocument instead */
43
+ export const useVoidInvoice = useVoidDocument;
@@ -1,7 +1,7 @@
1
1
  import type { Invoice } from "@spaceinvoices/js-sdk";
2
2
 
3
3
  import { Ban, Copy, Download, Eye, Link2Off, Loader2, Mail, MoreHorizontal, Plus } from "lucide-react";
4
- import { useState } from "react";
4
+ import { memo, useState } from "react";
5
5
  import { Button } from "@/ui/components/ui/button";
6
6
  import {
7
7
  DropdownMenu,
@@ -31,7 +31,7 @@ type InvoiceListRowActionsProps = {
31
31
  isVoiding?: boolean;
32
32
  } & ComponentTranslationProps;
33
33
 
34
- export default function InvoiceListRowActions({
34
+ export default memo(function InvoiceListRowActions({
35
35
  invoice,
36
36
  onView,
37
37
  onAddPayment,
@@ -144,4 +144,4 @@ export default function InvoiceListRowActions({
144
144
  />
145
145
  </>
146
146
  );
147
- }
147
+ });
@@ -11,6 +11,7 @@ import type {
11
11
  TableQueryParams,
12
12
  TableQueryResponse,
13
13
  } from "@/ui/components/table/types";
14
+ import { Badge } from "@/ui/components/ui/badge";
14
15
  import { Button } from "@/ui/components/ui/button";
15
16
  import { createTranslation } from "@/ui/lib/translation";
16
17
  import { useSDK } from "@/ui/providers/sdk-provider";
@@ -138,9 +139,19 @@ export default function InvoiceListTable({
138
139
  header: t("Number"),
139
140
  sortable: true,
140
141
  cell: (invoice) => (
141
- <Button variant="link" className="cursor-pointer py-0 underline" onClick={() => onRowClick?.(invoice)}>
142
- {(invoice as any).is_draft ? t("Draft") : invoice.number}
143
- </Button>
142
+ <div className="flex items-center gap-2">
143
+ <Button variant="link" className="cursor-pointer py-0 underline" onClick={() => onRowClick?.(invoice)}>
144
+ {invoice.number}
145
+ </Button>
146
+ {(invoice as any).is_draft && (
147
+ <Badge
148
+ variant="outline"
149
+ className="border-amber-500 bg-amber-50 text-amber-700 dark:bg-amber-950 dark:text-amber-400"
150
+ >
151
+ {t("Draft")}
152
+ </Badge>
153
+ )}
154
+ </div>
144
155
  ),
145
156
  },
146
157
  {
@@ -174,6 +185,11 @@ export default function InvoiceListTable({
174
185
  align: "right",
175
186
  cell: (invoice) => invoice.total_with_tax,
176
187
  },
188
+ {
189
+ id: "status",
190
+ header: t("Status"),
191
+ cell: (invoice) => <InvoiceStatusBadge invoice={invoice} t={t} />,
192
+ },
177
193
  {
178
194
  id: "actions",
179
195
  header: "",
@@ -231,3 +247,52 @@ export default function InvoiceListTable({
231
247
  />
232
248
  );
233
249
  }
250
+
251
+ /** Status badge for invoices */
252
+ function InvoiceStatusBadge({ invoice, t }: { invoice: Invoice; t: (key: string) => string }) {
253
+ if ((invoice as any).voided_at) {
254
+ return (
255
+ <Badge variant="outline" className="border-red-500 bg-red-50 text-red-700 dark:bg-red-950 dark:text-red-400">
256
+ {t("Voided")}
257
+ </Badge>
258
+ );
259
+ }
260
+ if ((invoice as any).is_draft) {
261
+ return null;
262
+ }
263
+ if (invoice.paid_in_full) {
264
+ return (
265
+ <Badge
266
+ variant="outline"
267
+ className="border-green-500 bg-green-50 text-green-700 dark:bg-green-950 dark:text-green-400"
268
+ >
269
+ {t("Paid")}
270
+ </Badge>
271
+ );
272
+ }
273
+ if (invoice.date_due && new Date(invoice.date_due) < new Date()) {
274
+ return (
275
+ <Badge
276
+ variant="outline"
277
+ className="border-orange-500 bg-orange-50 text-orange-700 dark:bg-orange-950 dark:text-orange-400"
278
+ >
279
+ {t("Overdue")}
280
+ </Badge>
281
+ );
282
+ }
283
+ if (invoice.total_paid > 0) {
284
+ return (
285
+ <Badge
286
+ variant="outline"
287
+ className="border-yellow-500 bg-yellow-50 text-yellow-700 dark:bg-yellow-950 dark:text-yellow-400"
288
+ >
289
+ {t("Partially Paid")}
290
+ </Badge>
291
+ );
292
+ }
293
+ return (
294
+ <Badge variant="outline" className="border-gray-500 bg-gray-50 text-gray-700 dark:bg-gray-800 dark:text-gray-400">
295
+ {t("Unpaid")}
296
+ </Badge>
297
+ );
298
+ }
@@ -43,4 +43,5 @@ export default {
43
43
  "Export PDFs": "PDFs exportieren",
44
44
  "Deselect all": "Auswahl aufheben",
45
45
  Void: "Stornieren",
46
+ "Partially Paid": "Teilweise bezahlt",
46
47
  } as const;
@@ -9,4 +9,5 @@ export default {
9
9
  "Export PDFs": "Export PDFs",
10
10
  "Deselect all": "Deselect all",
11
11
  Void: "Void",
12
+ "Partially Paid": "Partially Paid",
12
13
  } as const;
@@ -43,4 +43,5 @@ export default {
43
43
  "Export PDFs": "Exportar PDFs",
44
44
  "Deselect all": "Deseleccionar todo",
45
45
  Void: "Anular",
46
+ "Partially Paid": "Parcialmente pagado",
46
47
  } as const;
@@ -43,4 +43,5 @@ export default {
43
43
  "Export PDFs": "Exporter les PDF",
44
44
  "Deselect all": "Tout désélectionner",
45
45
  Void: "Annuler",
46
+ "Partially Paid": "Partiellement payé",
46
47
  } as const;
@@ -43,4 +43,5 @@ export default {
43
43
  "Export PDFs": "Izvezi PDF-ove",
44
44
  "Deselect all": "Poništi odabir",
45
45
  Void: "Storniraj",
46
+ "Partially Paid": "Djelomično plaćeno",
46
47
  } as const;
@@ -43,4 +43,5 @@ export default {
43
43
  "Export PDFs": "Esporta PDF",
44
44
  "Deselect all": "Deseleziona tutto",
45
45
  Void: "Annulla",
46
+ "Partially Paid": "Parzialmente pagato",
46
47
  } as const;
@@ -43,4 +43,5 @@ export default {
43
43
  "Export PDFs": "PDFs exporteren",
44
44
  "Deselect all": "Alles deselecteren",
45
45
  Void: "Nietig verklaren",
46
+ "Partially Paid": "Gedeeltelijk betaald",
46
47
  } as const;
@@ -43,4 +43,5 @@ export default {
43
43
  "Export PDFs": "Eksportuj PDF-y",
44
44
  "Deselect all": "Odznacz wszystko",
45
45
  Void: "Anuluj",
46
+ "Partially Paid": "Częściowo opłacona",
46
47
  } as const;
@@ -43,4 +43,5 @@ export default {
43
43
  "Export PDFs": "Exportar PDFs",
44
44
  "Deselect all": "Desselecionar tudo",
45
45
  Void: "Anular",
46
+ "Partially Paid": "Parcialmente pago",
46
47
  } as const;
@@ -43,4 +43,5 @@ export default {
43
43
  "Export PDFs": "Izvozi PDF-je",
44
44
  "Deselect all": "Počisti izbiro",
45
45
  Void: "Storniraj",
46
+ "Partially Paid": "Delno plačano",
46
47
  } as const;
@@ -1,6 +1,7 @@
1
1
  import type { Item } from "@spaceinvoices/js-sdk";
2
2
 
3
3
  import { MoreHorizontal } from "lucide-react";
4
+ import { memo } from "react";
4
5
  import { Button } from "@/ui/components/ui/button";
5
6
  import {
6
7
  DropdownMenu,
@@ -18,7 +19,7 @@ type ItemListRowActionsProps = {
18
19
  onView?: (item: Item) => void;
19
20
  } & ComponentTranslationProps;
20
21
 
21
- export default function ItemListRowActions({ item, onView, ...i18nProps }: ItemListRowActionsProps) {
22
+ export default memo(function ItemListRowActions({ item, onView, ...i18nProps }: ItemListRowActionsProps) {
22
23
  const t = createTranslation(i18nProps);
23
24
 
24
25
  return (
@@ -41,4 +42,4 @@ export default function ItemListRowActions({ item, onView, ...i18nProps }: ItemL
41
42
  </DropdownMenuContent>
42
43
  </DropdownMenu>
43
44
  );
44
- }
45
+ });
@@ -1,5 +1,6 @@
1
1
  import type { Item } from "@spaceinvoices/js-sdk";
2
2
  import { Package } from "lucide-react";
3
+ import { memo } from "react";
3
4
  import { TableCell, TableRow } from "@/ui/components/ui/table";
4
5
  import type { ComponentTranslationProps } from "@/ui/lib/translation";
5
6
  import { createTranslation } from "@/ui/lib/translation";
@@ -12,7 +13,7 @@ type ItemListRowProps = {
12
13
  onView?: (item: Item) => void;
13
14
  } & ComponentTranslationProps;
14
15
 
15
- export default function ItemListRow({ item, onRowClick, onView, ...i18nProps }: ItemListRowProps) {
16
+ export default memo(function ItemListRow({ item, onRowClick, onView, ...i18nProps }: ItemListRowProps) {
16
17
  const t = createTranslation(i18nProps);
17
18
 
18
19
  return (
@@ -30,4 +31,4 @@ export default function ItemListRow({ item, onRowClick, onView, ...i18nProps }:
30
31
  </TableCell>
31
32
  </TableRow>
32
33
  );
33
- }
34
+ });