@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.
- package/cli/dist/index.js +1 -1
- package/package.json +1 -1
- package/src/components/advance-invoices/list/list-table.tsx +60 -0
- package/src/components/advance-invoices/list/locales/de.ts +1 -0
- package/src/components/advance-invoices/list/locales/en.ts +1 -0
- package/src/components/advance-invoices/list/locales/es.ts +1 -0
- package/src/components/advance-invoices/list/locales/fr.ts +1 -0
- package/src/components/advance-invoices/list/locales/hr.ts +1 -0
- package/src/components/advance-invoices/list/locales/it.ts +1 -0
- package/src/components/advance-invoices/list/locales/nl.ts +1 -0
- package/src/components/advance-invoices/list/locales/pl.ts +1 -0
- package/src/components/advance-invoices/list/locales/pt.ts +1 -0
- package/src/components/advance-invoices/list/locales/sl.ts +1 -0
- package/src/components/credit-notes/list/list-table.tsx +34 -6
- package/src/components/credit-notes/list/locales/de.ts +1 -1
- package/src/components/credit-notes/list/locales/en.ts +2 -0
- package/src/components/credit-notes/list/locales/es.ts +1 -1
- package/src/components/credit-notes/list/locales/fr.ts +1 -1
- package/src/components/credit-notes/list/locales/hr.ts +1 -1
- package/src/components/credit-notes/list/locales/it.ts +1 -1
- package/src/components/credit-notes/list/locales/nl.ts +1 -1
- package/src/components/credit-notes/list/locales/pl.ts +1 -1
- package/src/components/credit-notes/list/locales/pt.ts +1 -1
- package/src/components/credit-notes/list/locales/sl.ts +1 -1
- package/src/components/dashboard/collection-rate-card/use-collection-rate.ts +2 -2
- package/src/components/dashboard/invoice-status-chart/use-invoice-status.ts +3 -3
- package/src/components/dashboard/payment-methods-chart/use-payment-methods.ts +1 -1
- package/src/components/dashboard/payment-trend-chart/use-payment-trend.ts +1 -1
- package/src/components/dashboard/revenue-trend-chart/use-revenue-trend.ts +1 -1
- package/src/components/dashboard/shared/use-revenue-data.ts +4 -4
- package/src/components/dashboard/shared/use-stats-counts.ts +4 -4
- package/src/components/dashboard/shared/use-stats-query.ts +1 -1
- package/src/components/dashboard/tax-collected-card/use-tax-collected.ts +2 -2
- package/src/components/dashboard/top-customers-chart/use-top-customers.ts +1 -1
- package/src/components/delivery-notes/create/create-delivery-note-form.tsx +332 -0
- package/src/components/delivery-notes/create/locales/de.ts +50 -0
- package/src/components/delivery-notes/create/locales/es.ts +49 -0
- package/src/components/delivery-notes/create/locales/fr.ts +50 -0
- package/src/components/delivery-notes/create/locales/hr.ts +49 -0
- package/src/components/delivery-notes/create/locales/it.ts +49 -0
- package/src/components/delivery-notes/create/locales/nl.ts +50 -0
- package/src/components/delivery-notes/create/locales/pl.ts +49 -0
- package/src/components/delivery-notes/create/locales/pt.ts +50 -0
- package/src/components/delivery-notes/create/locales/sl.ts +49 -0
- package/src/components/delivery-notes/create/prepare-delivery-note-submission.ts +38 -0
- package/src/components/delivery-notes/create/use-delivery-note-customer-form.ts +1 -0
- package/src/components/delivery-notes/delivery-notes.hooks.ts +15 -0
- package/src/components/delivery-notes/list/index.ts +3 -0
- package/src/components/delivery-notes/list/list-row-actions.tsx +103 -0
- package/src/components/delivery-notes/list/list-table.tsx +214 -0
- package/src/components/delivery-notes/list/locales/de.ts +9 -0
- package/src/components/delivery-notes/list/locales/en.ts +11 -0
- package/src/components/delivery-notes/list/locales/es.ts +9 -0
- package/src/components/delivery-notes/list/locales/fr.ts +9 -0
- package/src/components/delivery-notes/list/locales/hr.ts +9 -0
- package/src/components/delivery-notes/list/locales/it.ts +9 -0
- package/src/components/delivery-notes/list/locales/nl.ts +9 -0
- package/src/components/delivery-notes/list/locales/pl.ts +9 -0
- package/src/components/delivery-notes/list/locales/pt.ts +9 -0
- package/src/components/delivery-notes/list/locales/sl.ts +9 -0
- package/src/components/delivery-notes/list/use-delivery-note-download.ts +63 -0
- package/src/components/documents/create/document-details-section.tsx +36 -32
- package/src/components/documents/create/live-preview.tsx +37 -10
- package/src/components/documents/create/prepare-document-submission.ts +1 -1
- package/src/components/documents/documents.hooks.ts +2 -1
- package/src/components/documents/shared/document-preview-display.tsx +12 -5
- package/src/components/documents/types.ts +10 -1
- package/src/components/documents/view/document-details-card.tsx +3 -3
- package/src/components/documents/view/document-payments-list.tsx +3 -3
- package/src/components/documents/view/document-relations-list.tsx +105 -0
- package/src/components/documents/view/locales/de.ts +21 -0
- package/src/components/documents/view/locales/es.ts +21 -0
- package/src/components/documents/view/locales/fr.ts +21 -0
- package/src/components/documents/view/locales/hr.ts +21 -0
- package/src/components/documents/view/locales/it.ts +21 -0
- package/src/components/documents/view/locales/nl.ts +21 -0
- package/src/components/documents/view/locales/pl.ts +21 -0
- package/src/components/documents/view/locales/pt.ts +21 -0
- package/src/components/documents/view/locales/sl.ts +21 -0
- package/src/components/documents/view/use-document-download.ts +5 -3
- package/src/components/entities/fina-settings-form/fina-settings-form.tsx +77 -65
- package/src/components/entities/fina-settings-form/fina-settings.hooks.ts +7 -2
- package/src/components/entities/furs-settings-form/furs-settings-form.tsx +10 -1
- package/src/components/entities/furs-settings-form/furs-settings.hooks.ts +7 -2
- package/src/components/entities/furs-settings-form/sections/general-settings-section.tsx +12 -4
- package/src/components/invoices/invoices-furs.hooks.ts +24 -9
- package/src/components/invoices/list/list-row-actions.tsx +3 -3
- package/src/components/invoices/list/list-table.tsx +68 -3
- package/src/components/invoices/list/locales/de.ts +1 -0
- package/src/components/invoices/list/locales/en.ts +1 -0
- package/src/components/invoices/list/locales/es.ts +1 -0
- package/src/components/invoices/list/locales/fr.ts +1 -0
- package/src/components/invoices/list/locales/hr.ts +1 -0
- package/src/components/invoices/list/locales/it.ts +1 -0
- package/src/components/invoices/list/locales/nl.ts +1 -0
- package/src/components/invoices/list/locales/pl.ts +1 -0
- package/src/components/invoices/list/locales/pt.ts +1 -0
- package/src/components/invoices/list/locales/sl.ts +1 -0
- package/src/components/items/item-list-table/item-list-row-actions.tsx +3 -2
- package/src/components/items/item-list-table/item-list-row.tsx +3 -2
- package/src/generated/schemas/deliverynote.ts +134 -0
- package/src/generated/schemas/entity.ts +4 -0
- package/src/generated/schemas/index.ts +3 -0
- package/src/generated/schemas/order.ts +5 -3
- package/src/generated/schemas/payment.ts +22 -2
- package/src/generated/schemas/renderadvanceinvoicepreview_body.ts +1 -0
- package/src/generated/schemas/rendercreditnotepreview_body.ts +1 -0
- package/src/generated/schemas/renderdeliverynotepreview_body.ts +185 -0
- package/src/generated/schemas/renderestimatepreview_body.ts +1 -0
- package/src/generated/schemas/renderinvoicepreview_body.ts +1 -0
- package/src/generated/schemas/startpdfexport_body.ts +18 -2
- package/src/generated/schemas/userfinasettings.ts +19 -0
- package/src/generated/schemas/webhook.ts +12 -0
- package/src/hooks/use-duplicate-document.ts +11 -3
- package/src/hooks/use-next-document-number.ts +2 -2
- 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(
|
|
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
|
-
{
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
<div className="
|
|
562
|
-
<
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
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
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
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
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
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
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
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
|
-
|
|
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 {
|
|
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 {
|
|
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(
|
|
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
|
-
{
|
|
379
|
-
|
|
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
|
|
5
|
-
|
|
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
|
|
12
|
-
* Automatically handles FURS technical cancellation for fiscalized
|
|
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
|
|
15
|
+
export function useVoidDocument() {
|
|
15
16
|
const { sdk } = useSDK();
|
|
16
17
|
const queryClient = useQueryClient();
|
|
17
18
|
|
|
18
19
|
return useMutation({
|
|
19
|
-
mutationFn: async ({
|
|
20
|
-
|
|
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: ["
|
|
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
|
-
<
|
|
142
|
-
|
|
143
|
-
|
|
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
|
+
}
|
|
@@ -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
|
+
});
|