@carlonicora/nextjs-jsonapi 1.65.0 → 1.66.0
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/dist/{AuthComponent-B4rNZRYE.d.ts → AuthComponent-DL1D3y7f.d.ts} +1 -1
- package/dist/{AuthComponent-nzabiz68.d.mts → AuthComponent-NwQ_ZXsv.d.mts} +1 -1
- package/dist/{BlockNoteEditor-Y5XAD6NR.js → BlockNoteEditor-GQM2TZG2.js} +14 -14
- package/dist/{BlockNoteEditor-Y5XAD6NR.js.map → BlockNoteEditor-GQM2TZG2.js.map} +1 -1
- package/dist/{BlockNoteEditor-SCQGD6F2.mjs → BlockNoteEditor-KCJMA6LW.mjs} +4 -4
- package/dist/{auth.interface-C1WjZ0fM.d.ts → auth.interface-BX_1qZZJ.d.ts} +1 -1
- package/dist/{auth.interface-fBFqIrw4.d.mts → auth.interface-yeLelxdI.d.mts} +1 -1
- package/dist/billing/index.js +346 -346
- package/dist/billing/index.mjs +3 -3
- package/dist/{chunk-G7PGWMFO.mjs → chunk-35GWVOYZ.mjs} +61 -1
- package/dist/{chunk-G7PGWMFO.mjs.map → chunk-35GWVOYZ.mjs.map} +1 -1
- package/dist/{chunk-OLNMWVOV.mjs → chunk-4E74ZTRT.mjs} +2115 -1834
- package/dist/chunk-4E74ZTRT.mjs.map +1 -0
- package/dist/{chunk-LRXJT656.js → chunk-NVXYOQFW.js} +61 -1
- package/dist/chunk-NVXYOQFW.js.map +1 -0
- package/dist/{chunk-RA4RYKYB.js → chunk-OQRBY22T.js} +11 -11
- package/dist/{chunk-RA4RYKYB.js.map → chunk-OQRBY22T.js.map} +1 -1
- package/dist/{chunk-5ODPC3YX.js → chunk-QIFM4G7T.js} +1174 -893
- package/dist/chunk-QIFM4G7T.js.map +1 -0
- package/dist/{chunk-5KMKI23S.mjs → chunk-UXGPZZ6V.mjs} +2 -2
- package/dist/client/index.d.mts +6 -6
- package/dist/client/index.d.ts +6 -6
- package/dist/client/index.js +4 -4
- package/dist/client/index.mjs +3 -3
- package/dist/components/index.d.mts +42 -7
- package/dist/components/index.d.ts +42 -7
- package/dist/components/index.js +14 -4
- package/dist/components/index.js.map +1 -1
- package/dist/components/index.mjs +13 -3
- package/dist/{config-DZWAFB7H.d.ts → config-CyCAWW-d.d.ts} +1 -1
- package/dist/{config-ndRJIQsP.d.mts → config-D-mqttuF.d.mts} +1 -1
- package/dist/{content.interface-B5ySfiOE.d.mts → content.interface-8T5-G84c.d.mts} +1 -1
- package/dist/{content.interface-mmz0uMwm.d.ts → content.interface-D-xdYxjt.d.ts} +1 -1
- package/dist/contexts/index.d.mts +2 -2
- package/dist/contexts/index.d.ts +2 -2
- package/dist/contexts/index.js +4 -4
- package/dist/contexts/index.mjs +3 -3
- package/dist/core/index.d.mts +29 -9
- package/dist/core/index.d.ts +29 -9
- package/dist/core/index.js +2 -2
- package/dist/core/index.mjs +1 -1
- package/dist/index.d.mts +8 -8
- package/dist/index.d.ts +8 -8
- package/dist/index.js +3 -3
- package/dist/index.mjs +2 -2
- package/dist/{notification.interface-DG7cq9oG.d.mts → notification.interface-C6UcmJqu.d.mts} +20 -0
- package/dist/{notification.interface-COKHDQeE.d.ts → notification.interface-ItBxq2au.d.ts} +20 -0
- package/dist/{s3.service-ppn9iGJU.d.ts → s3.service-DIR6Su9B.d.ts} +3 -3
- package/dist/{s3.service-BoRPFx82.d.mts → s3.service-XchHd3ii.d.mts} +3 -3
- package/dist/server/index.d.mts +4 -4
- package/dist/server/index.d.ts +4 -4
- package/dist/server/index.js +3 -3
- package/dist/server/index.mjs +1 -1
- package/dist/{useRbacState-DhuYYr0S.d.mts → useRbacState-Btk1gkQg.d.mts} +1 -1
- package/dist/{useRbacState-NnzNL2ED.d.ts → useRbacState-CUj0hp8t.d.ts} +1 -1
- package/dist/{useSocket-bsV-K4qR.d.ts → useSocket-BSUN9s3p.d.ts} +1 -1
- package/dist/{useSocket-CtfuR5wD.d.mts → useSocket-DKI92Fbg.d.mts} +1 -1
- package/package.json +2 -1
- package/src/components/containers/RoundPageContainer.tsx +1 -1
- package/src/components/fiscal/FiscalDataDisplay.tsx +26 -0
- package/src/components/fiscal/ItalianFiscalData.tsx +120 -0
- package/src/components/fiscal/ItalianFiscalDataDisplay.tsx +24 -0
- package/src/components/fiscal/index.ts +4 -0
- package/src/components/index.ts +2 -0
- package/src/components/navigations/Breadcrumb.tsx +4 -4
- package/src/components/navigations/RecentPagesNavigator.tsx +3 -3
- package/src/features/company/components/details/CompanyContent.tsx +105 -0
- package/src/features/company/components/details/CompanyDetails.tsx +2 -19
- package/src/features/company/components/details/index.ts +1 -0
- package/src/features/company/components/forms/CompanyConfigurationEditor.tsx +38 -70
- package/src/features/company/components/forms/CompanyEditor.tsx +214 -172
- package/src/features/company/data/company.interface.ts +20 -0
- package/src/features/company/data/company.ts +73 -0
- package/src/utils/fiscal-utils.ts +7 -0
- package/src/utils/italian-validators.ts +79 -0
- package/dist/chunk-5ODPC3YX.js.map +0 -1
- package/dist/chunk-LRXJT656.js.map +0 -1
- package/dist/chunk-OLNMWVOV.mjs.map +0 -1
- /package/dist/{BlockNoteEditor-SCQGD6F2.mjs.map → BlockNoteEditor-KCJMA6LW.mjs.map} +0 -0
- /package/dist/{chunk-5KMKI23S.mjs.map → chunk-UXGPZZ6V.mjs.map} +0 -0
|
@@ -3,22 +3,14 @@
|
|
|
3
3
|
import { zodResolver } from "@hookform/resolvers/zod";
|
|
4
4
|
import { Settings2Icon } from "lucide-react";
|
|
5
5
|
import { useTranslations } from "next-intl";
|
|
6
|
-
import { ReactNode
|
|
7
|
-
import {
|
|
6
|
+
import { ReactNode } from "react";
|
|
7
|
+
import { UseFormReturn, useForm } from "react-hook-form";
|
|
8
8
|
import { showToast } from "../../../../utils/toast";
|
|
9
9
|
import z from "zod";
|
|
10
|
-
import {
|
|
10
|
+
import { EditorSheet } from "../../../../components";
|
|
11
|
+
import { Modules } from "../../../../core";
|
|
11
12
|
import { getRoleId } from "../../../../roles";
|
|
12
|
-
import {
|
|
13
|
-
Button,
|
|
14
|
-
Dialog,
|
|
15
|
-
DialogContent,
|
|
16
|
-
DialogDescription,
|
|
17
|
-
DialogHeader,
|
|
18
|
-
DialogTitle,
|
|
19
|
-
DialogTrigger,
|
|
20
|
-
Form,
|
|
21
|
-
} from "../../../../shadcnui";
|
|
13
|
+
import { Button } from "../../../../shadcnui";
|
|
22
14
|
import { UserInterface } from "../../../user";
|
|
23
15
|
import { useCurrentUserContext } from "../../../user/contexts";
|
|
24
16
|
import { UserService } from "../../../user/data/user.service";
|
|
@@ -40,75 +32,51 @@ function CompanyConfigurationEditorInternal({
|
|
|
40
32
|
buildPayload,
|
|
41
33
|
children,
|
|
42
34
|
}: CompanyConfigurationEditorProps) {
|
|
43
|
-
const [open, setOpen] = useState<boolean>(false);
|
|
44
35
|
const t = useTranslations();
|
|
45
36
|
const { setUser } = useCurrentUserContext<UserInterface>();
|
|
46
37
|
|
|
47
|
-
const close = () => {
|
|
48
|
-
setOpen(false);
|
|
49
|
-
form.reset();
|
|
50
|
-
};
|
|
51
|
-
|
|
52
38
|
const form = useForm<z.infer<typeof formSchema>>({
|
|
53
39
|
resolver: zodResolver(formSchema),
|
|
54
40
|
defaultValues: defaultValues,
|
|
55
41
|
shouldUnregister: false,
|
|
56
42
|
});
|
|
57
43
|
|
|
58
|
-
useEffect(() => {
|
|
59
|
-
if (open) {
|
|
60
|
-
form.reset(defaultValues);
|
|
61
|
-
}
|
|
62
|
-
}, [company, open]);
|
|
63
|
-
|
|
64
|
-
const onSubmit: SubmitHandler<z.infer<typeof formSchema>> = async (values) => {
|
|
65
|
-
const payload: CompanyInput = {
|
|
66
|
-
id: company.id,
|
|
67
|
-
configurations: buildPayload(values),
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
try {
|
|
71
|
-
await CompanyService.updateConfigurations(payload);
|
|
72
|
-
|
|
73
|
-
const fullUser = await UserService.findFullUser();
|
|
74
|
-
if (fullUser) {
|
|
75
|
-
setUser(fullUser);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
showToast(t("features.configuration.updated_title"), {
|
|
79
|
-
description: t("features.configuration.updated_description"),
|
|
80
|
-
});
|
|
81
|
-
close();
|
|
82
|
-
} catch (error) {
|
|
83
|
-
errorToast({
|
|
84
|
-
title: t(`common.errors.update`),
|
|
85
|
-
error,
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
};
|
|
89
|
-
|
|
90
44
|
return (
|
|
91
|
-
<
|
|
92
|
-
|
|
93
|
-
|
|
45
|
+
<EditorSheet
|
|
46
|
+
form={form}
|
|
47
|
+
entityType={t(`entities.configurations`, { count: 2 })}
|
|
48
|
+
entityName={company.name}
|
|
49
|
+
isEdit={true}
|
|
50
|
+
module={Modules.Company}
|
|
51
|
+
size="sm"
|
|
52
|
+
trigger={
|
|
53
|
+
<Button render={<div />} nativeButton={false} size="sm" variant="ghost" className="cursor-pointer">
|
|
94
54
|
<Settings2Icon className="h-3.5 w-3.5" />
|
|
95
55
|
</Button>
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
56
|
+
}
|
|
57
|
+
onSubmit={async (values) => {
|
|
58
|
+
const payload: CompanyInput = {
|
|
59
|
+
id: company.id,
|
|
60
|
+
configurations: buildPayload(values),
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
await CompanyService.updateConfigurations(payload);
|
|
64
|
+
|
|
65
|
+
const fullUser = await UserService.findFullUser();
|
|
66
|
+
if (fullUser) {
|
|
67
|
+
setUser(fullUser);
|
|
68
|
+
}
|
|
69
|
+
}}
|
|
70
|
+
onSuccess={async () => {
|
|
71
|
+
showToast(t("features.configuration.updated_title"), {
|
|
72
|
+
description: t("features.configuration.updated_description"),
|
|
73
|
+
});
|
|
74
|
+
}}
|
|
75
|
+
onReset={() => defaultValues}
|
|
76
|
+
>
|
|
77
|
+
<p className="text-destructive text-sm font-medium">{t(`features.configuration.warning_description`)}</p>
|
|
78
|
+
{children(form)}
|
|
79
|
+
</EditorSheet>
|
|
112
80
|
);
|
|
113
81
|
}
|
|
114
82
|
|
|
@@ -1,36 +1,36 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import { zodResolver } from "@hookform/resolvers/zod";
|
|
4
|
-
import { setCookie } from "cookies-next";
|
|
5
4
|
import { UploadIcon } from "lucide-react";
|
|
6
5
|
import { useTranslations } from "next-intl";
|
|
7
6
|
import Image from "next/image";
|
|
8
|
-
import { useEffect, useState } from "react";
|
|
7
|
+
import { ReactNode, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
9
8
|
import { DropzoneOptions } from "react-dropzone";
|
|
10
|
-
import {
|
|
9
|
+
import { useForm } from "react-hook-form";
|
|
11
10
|
import { v4 } from "uuid";
|
|
12
11
|
import { z } from "zod";
|
|
13
12
|
import {
|
|
14
|
-
|
|
15
|
-
CommonEditorHeader,
|
|
16
|
-
CommonEditorTrigger,
|
|
17
|
-
errorToast,
|
|
13
|
+
EditorSheet,
|
|
18
14
|
FileInput,
|
|
19
15
|
FileUploader,
|
|
20
16
|
FormFeatures,
|
|
21
17
|
FormInput,
|
|
18
|
+
FormPlaceAutocomplete,
|
|
19
|
+
ItalianFiscalData,
|
|
20
|
+
parseFiscalData,
|
|
22
21
|
} from "../../../../components";
|
|
22
|
+
import type { FiscalDataHandle } from "../../../../components";
|
|
23
23
|
import { Modules } from "../../../../core";
|
|
24
|
-
import { usePageUrlGenerator } from "../../../../hooks";
|
|
25
24
|
import { useI18nRouter } from "../../../../i18n";
|
|
26
25
|
import { getRoleId } from "../../../../roles";
|
|
27
|
-
import {
|
|
26
|
+
import { ScrollArea } from "../../../../shadcnui";
|
|
28
27
|
import { FeatureInterface } from "../../../feature";
|
|
29
28
|
import { FeatureService } from "../../../feature/data/feature.service";
|
|
30
29
|
import { S3Interface } from "../../../s3";
|
|
31
30
|
import { S3Service } from "../../../s3/data/s3.service";
|
|
32
31
|
import { UserInterface } from "../../../user";
|
|
33
32
|
import { useCurrentUserContext } from "../../../user/contexts";
|
|
33
|
+
import { UserService } from "../../../user/data/user.service";
|
|
34
34
|
import { CompanyInput, CompanyInterface } from "../../data";
|
|
35
35
|
import { CompanyService } from "../../data/company.service";
|
|
36
36
|
|
|
@@ -38,202 +38,244 @@ type CompanyEditorProps = {
|
|
|
38
38
|
company?: CompanyInterface;
|
|
39
39
|
propagateChanges?: (company: CompanyInterface) => void;
|
|
40
40
|
onRevalidate?: (path: string) => Promise<void>;
|
|
41
|
+
trigger?: ReactNode;
|
|
42
|
+
forceShow?: boolean;
|
|
43
|
+
onClose?: () => void;
|
|
44
|
+
dialogOpen?: boolean;
|
|
45
|
+
onDialogOpenChange?: (open: boolean) => void;
|
|
41
46
|
};
|
|
42
47
|
|
|
43
|
-
function CompanyEditorInternal({
|
|
44
|
-
|
|
48
|
+
function CompanyEditorInternal({
|
|
49
|
+
company,
|
|
50
|
+
propagateChanges,
|
|
51
|
+
onRevalidate,
|
|
52
|
+
trigger,
|
|
53
|
+
forceShow,
|
|
54
|
+
onClose,
|
|
55
|
+
dialogOpen,
|
|
56
|
+
onDialogOpenChange,
|
|
57
|
+
}: CompanyEditorProps) {
|
|
58
|
+
const { hasRole, setUser } = useCurrentUserContext<UserInterface>();
|
|
45
59
|
const router = useI18nRouter();
|
|
46
|
-
const [open, setOpen] = useState<boolean>(false);
|
|
47
60
|
const [features, setFeatures] = useState<FeatureInterface[]>([]);
|
|
48
61
|
const [file, setFile] = useState<File | null>(null);
|
|
49
62
|
const [files, setFiles] = useState<File[] | null>(null);
|
|
50
63
|
const [contentType, setContentType] = useState<string | null>(null);
|
|
51
64
|
const t = useTranslations();
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
const formSchema = z.object({
|
|
55
|
-
id: z.uuidv4(),
|
|
56
|
-
name: z.string().min(1, {
|
|
57
|
-
message: t(`company.fields.name.error`),
|
|
58
|
-
}),
|
|
59
|
-
featureIds: z.array(z.string()).optional(),
|
|
60
|
-
moduleIds: z.array(z.string()).optional(),
|
|
61
|
-
logo: z.string().optional(),
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
const form = useForm({
|
|
65
|
-
resolver: zodResolver(formSchema),
|
|
66
|
-
defaultValues: {
|
|
67
|
-
id: company?.id || v4(),
|
|
68
|
-
name: company?.name || "",
|
|
69
|
-
featureIds: company?.features.map((feature) => feature.id) || [],
|
|
70
|
-
moduleIds: company?.modules.map((module) => module.id) || [],
|
|
71
|
-
logo: company?.logo || "",
|
|
72
|
-
},
|
|
73
|
-
});
|
|
65
|
+
const fiscalRef = useRef<FiscalDataHandle>(null);
|
|
66
|
+
const addressComponentsRef = useRef<Record<string, string>>({});
|
|
74
67
|
|
|
75
|
-
const
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
contentType: contentType,
|
|
80
|
-
isPublic: true,
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
await fetch(s3.url, {
|
|
84
|
-
method: "PUT",
|
|
85
|
-
headers: s3.headers,
|
|
86
|
-
body: file,
|
|
87
|
-
});
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
const payload: CompanyInput = {
|
|
91
|
-
id: company?.id ?? v4(),
|
|
92
|
-
name: values.name,
|
|
93
|
-
logo: files && contentType ? values.logo : undefined,
|
|
94
|
-
featureIds: values.featureIds,
|
|
95
|
-
moduleIds: values.moduleIds,
|
|
96
|
-
};
|
|
97
|
-
|
|
98
|
-
try {
|
|
99
|
-
const updatedCompany = company ? await CompanyService.update(payload) : await CompanyService.create(payload);
|
|
68
|
+
const canAccessFeatures =
|
|
69
|
+
hasRole(getRoleId().Administrator) ||
|
|
70
|
+
(hasRole(getRoleId().CompanyAdministrator) &&
|
|
71
|
+
process.env.NEXT_PUBLIC_PRIVATE_INSTALLATION?.toLowerCase() === "true");
|
|
100
72
|
|
|
101
|
-
|
|
102
|
-
|
|
73
|
+
// Fetch features when sheet opens
|
|
74
|
+
const handleDialogOpenChange = useCallback(
|
|
75
|
+
(open: boolean) => {
|
|
76
|
+
if (open && features.length === 0 && canAccessFeatures) {
|
|
77
|
+
async function fetchFeatures() {
|
|
78
|
+
const allfeatures = await FeatureService.findMany({});
|
|
79
|
+
if (hasRole(getRoleId().Administrator)) {
|
|
80
|
+
setFeatures(allfeatures);
|
|
81
|
+
} else {
|
|
82
|
+
setFeatures(allfeatures.filter((feature) => feature.isCore));
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
fetchFeatures();
|
|
103
86
|
}
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
} else {
|
|
109
|
-
router.push(`/administration/companies/${updatedCompany.id}`);
|
|
110
|
-
}
|
|
111
|
-
} catch (error) {
|
|
112
|
-
errorToast({
|
|
113
|
-
title: company ? t(`common.errors.update`) : t(`common.errors.create`),
|
|
114
|
-
error,
|
|
115
|
-
});
|
|
116
|
-
}
|
|
117
|
-
};
|
|
87
|
+
onDialogOpenChange?.(open);
|
|
88
|
+
},
|
|
89
|
+
[features.length, canAccessFeatures, hasRole, onDialogOpenChange],
|
|
90
|
+
);
|
|
118
91
|
|
|
92
|
+
const dropzone = {
|
|
93
|
+
multiple: false,
|
|
94
|
+
maxSize: 100 * 1024 * 1024,
|
|
95
|
+
preventDropOnDocument: false,
|
|
96
|
+
accept: {
|
|
97
|
+
"application/images": [".jpg", ".jpeg", ".png", ".gif", ".webp", ".svg"],
|
|
98
|
+
},
|
|
99
|
+
} satisfies DropzoneOptions;
|
|
100
|
+
|
|
101
|
+
// Handle file selection from dropzone
|
|
119
102
|
useEffect(() => {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
if (hasRole(getRoleId().Administrator)) {
|
|
123
|
-
setFeatures(allfeatures);
|
|
124
|
-
} else {
|
|
125
|
-
setFeatures(allfeatures.filter((feature) => feature.isCore));
|
|
126
|
-
}
|
|
103
|
+
if (files && files.length > 0) {
|
|
104
|
+
setFile(files[0]);
|
|
127
105
|
}
|
|
128
|
-
|
|
129
|
-
open &&
|
|
130
|
-
features.length === 0 &&
|
|
131
|
-
(hasRole(getRoleId().Administrator) ||
|
|
132
|
-
(hasRole(getRoleId().CompanyAdministrator) &&
|
|
133
|
-
process.env.NEXT_PUBLIC_PRIVATE_INSTALLATION?.toLowerCase() === "true"))
|
|
134
|
-
)
|
|
135
|
-
fetchFeatures();
|
|
136
|
-
}, [open, features]);
|
|
106
|
+
}, [files]);
|
|
137
107
|
|
|
108
|
+
// Generate S3 path when file is selected
|
|
138
109
|
useEffect(() => {
|
|
139
110
|
if (file && company) {
|
|
140
111
|
const id = form.getValues("id");
|
|
141
112
|
const fileType = file.type;
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
switch (fileType) {
|
|
145
|
-
default:
|
|
146
|
-
extension = file.type.split("/").pop() ?? "";
|
|
147
|
-
break;
|
|
148
|
-
}
|
|
149
|
-
|
|
113
|
+
const extension = fileType.split("/").pop() ?? "";
|
|
150
114
|
const timestamp = new Date().toISOString().replace(/[-:T]/g, "").split(".")[0];
|
|
151
|
-
|
|
152
115
|
const fileUrl = `companies/${form.getValues("id")}/companies/${id}/${id}.${timestamp}.${extension}`;
|
|
153
116
|
form.setValue("logo", fileUrl);
|
|
154
|
-
|
|
155
117
|
setContentType(fileType);
|
|
156
118
|
} else {
|
|
157
119
|
setContentType(null);
|
|
158
120
|
}
|
|
159
121
|
}, [file]);
|
|
160
122
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
123
|
+
const formSchema = useMemo(
|
|
124
|
+
() =>
|
|
125
|
+
z.object({
|
|
126
|
+
id: z.uuidv4(),
|
|
127
|
+
name: z.string().min(1, {
|
|
128
|
+
message: t(`company.fields.name.error`),
|
|
129
|
+
}),
|
|
130
|
+
featureIds: z.array(z.string()).optional(),
|
|
131
|
+
moduleIds: z.array(z.string()).optional(),
|
|
132
|
+
logo: z.string().optional(),
|
|
133
|
+
legal_address: z.string().optional(),
|
|
134
|
+
}),
|
|
135
|
+
[t],
|
|
136
|
+
);
|
|
175
137
|
|
|
176
|
-
const
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
138
|
+
const getDefaultValues = useCallback(() => {
|
|
139
|
+
return {
|
|
140
|
+
id: company?.id || v4(),
|
|
141
|
+
name: company?.name || "",
|
|
142
|
+
featureIds: company?.features.map((feature) => feature.id) || [],
|
|
143
|
+
moduleIds: company?.modules.map((module) => module.id) || [],
|
|
144
|
+
logo: company?.logo || "",
|
|
145
|
+
legal_address: company?.legal_address || "",
|
|
146
|
+
};
|
|
147
|
+
}, [company]);
|
|
180
148
|
|
|
181
|
-
const
|
|
149
|
+
const form = useForm<z.infer<typeof formSchema>>({
|
|
150
|
+
resolver: zodResolver(formSchema),
|
|
151
|
+
defaultValues: getDefaultValues(),
|
|
152
|
+
});
|
|
182
153
|
|
|
183
154
|
return (
|
|
184
|
-
<
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
155
|
+
<EditorSheet
|
|
156
|
+
form={form}
|
|
157
|
+
entityType={t(`entities.companies`, { count: 1 })}
|
|
158
|
+
entityName={company?.name}
|
|
159
|
+
isEdit={!!company}
|
|
160
|
+
module={Modules.Company}
|
|
161
|
+
propagateChanges={propagateChanges}
|
|
162
|
+
size="lg"
|
|
163
|
+
onSubmit={async (values) => {
|
|
164
|
+
// Upload logo to S3 if present
|
|
165
|
+
if (values.logo && contentType && file) {
|
|
166
|
+
const s3: S3Interface = await S3Service.getPreSignedUrl({
|
|
167
|
+
key: values.logo,
|
|
168
|
+
contentType: contentType,
|
|
169
|
+
isPublic: true,
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
const response = await fetch(s3.url, {
|
|
173
|
+
method: "PUT",
|
|
174
|
+
headers: s3.headers,
|
|
175
|
+
body: file,
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
if (!response.ok) {
|
|
179
|
+
throw new Error(`S3 upload failed: ${response.status}`);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
if (fiscalRef.current && !fiscalRef.current.validate()) {
|
|
184
|
+
throw new Error("Fiscal data validation failed");
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const payload: CompanyInput = {
|
|
188
|
+
id: company?.id ?? v4(),
|
|
189
|
+
name: values.name,
|
|
190
|
+
logo: files && contentType ? values.logo : undefined,
|
|
191
|
+
featureIds: values.featureIds,
|
|
192
|
+
moduleIds: values.moduleIds,
|
|
193
|
+
legal_address: values.legal_address,
|
|
194
|
+
...addressComponentsRef.current,
|
|
195
|
+
fiscal_data: fiscalRef.current ? JSON.stringify(fiscalRef.current.getData()) : undefined,
|
|
196
|
+
};
|
|
197
|
+
|
|
198
|
+
const updatedCompany = company
|
|
199
|
+
? await CompanyService.update(payload)
|
|
200
|
+
: await CompanyService.create(payload);
|
|
201
|
+
|
|
202
|
+
// Refresh user context after company changes
|
|
203
|
+
const fullUser = await UserService.findFullUser();
|
|
204
|
+
if (fullUser) {
|
|
205
|
+
setUser(fullUser);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
return updatedCompany;
|
|
209
|
+
}}
|
|
210
|
+
onRevalidate={onRevalidate}
|
|
211
|
+
onNavigate={(url) => router.push(url)}
|
|
212
|
+
onReset={() => {
|
|
213
|
+
setFile(null);
|
|
214
|
+
setFiles(null);
|
|
215
|
+
setContentType(null);
|
|
216
|
+
addressComponentsRef.current = {};
|
|
217
|
+
return getDefaultValues();
|
|
218
|
+
}}
|
|
219
|
+
onClose={onClose}
|
|
220
|
+
trigger={trigger}
|
|
221
|
+
forceShow={forceShow}
|
|
222
|
+
dialogOpen={dialogOpen}
|
|
223
|
+
onDialogOpenChange={handleDialogOpenChange}
|
|
224
|
+
>
|
|
225
|
+
<div className="flex w-full items-start justify-between gap-x-4">
|
|
226
|
+
<div className="flex w-96 flex-col justify-start gap-y-4">
|
|
227
|
+
<FileUploader value={files} onValueChange={setFiles} dropzoneOptions={dropzone} className="w-full p-4">
|
|
228
|
+
<FileInput className="text-neutral-300 outline-dashed">
|
|
229
|
+
<div className="flex w-full flex-col items-center justify-center pt-3 pb-4">
|
|
230
|
+
<div className="flex w-full flex-col items-center justify-center pt-3 pb-4">
|
|
231
|
+
{file || company?.logo ? (
|
|
232
|
+
<Image
|
|
233
|
+
src={file ? URL.createObjectURL(file) : company?.logo || ""}
|
|
234
|
+
alt="Company Logo"
|
|
235
|
+
width={200}
|
|
236
|
+
height={200}
|
|
237
|
+
/>
|
|
238
|
+
) : (
|
|
239
|
+
<>
|
|
240
|
+
<UploadIcon className="my-4 h-8 w-8" />
|
|
241
|
+
<p className="mb-1 flex w-full text-center text-sm">{t(`company.click_drag_logo`)}</p>
|
|
242
|
+
</>
|
|
243
|
+
)}
|
|
229
244
|
</div>
|
|
230
|
-
|
|
231
|
-
</
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
245
|
+
</div>
|
|
246
|
+
</FileInput>
|
|
247
|
+
</FileUploader>
|
|
248
|
+
</div>
|
|
249
|
+
<div className="flex w-full flex-col justify-start gap-y-4">
|
|
250
|
+
<FormInput
|
|
251
|
+
form={form}
|
|
252
|
+
id="name"
|
|
253
|
+
name={t(`company.fields.name.label`)}
|
|
254
|
+
placeholder={t(`company.fields.name.placeholder`)}
|
|
255
|
+
/>
|
|
256
|
+
<FormPlaceAutocomplete
|
|
257
|
+
form={form}
|
|
258
|
+
id="legal_address"
|
|
259
|
+
name={t(`company.fields.legal_address.label`)}
|
|
260
|
+
placeholder={t(`company.fields.legal_address.placeholder`)}
|
|
261
|
+
onPlaceSelect={(place) => {
|
|
262
|
+
if (place.addressComponents) {
|
|
263
|
+
addressComponentsRef.current = { ...place.addressComponents };
|
|
264
|
+
}
|
|
265
|
+
}}
|
|
266
|
+
/>
|
|
267
|
+
<h3 className="mt-2 text-sm font-semibold">{t(`company.sections.fiscal_data`)}</h3>
|
|
268
|
+
<ItalianFiscalData ref={fiscalRef} initialData={parseFiscalData(company?.fiscal_data)} />
|
|
269
|
+
</div>
|
|
270
|
+
{canAccessFeatures && (
|
|
271
|
+
<div className="flex w-96 flex-col justify-start gap-y-4">
|
|
272
|
+
<ScrollArea className="h-max">
|
|
273
|
+
<FormFeatures form={form} name={t(`company.features_and_modules`)} features={features} />
|
|
274
|
+
</ScrollArea>
|
|
275
|
+
</div>
|
|
276
|
+
)}
|
|
277
|
+
</div>
|
|
278
|
+
</EditorSheet>
|
|
237
279
|
);
|
|
238
280
|
}
|
|
239
281
|
|
|
@@ -14,6 +14,16 @@ export type CompanyInput = {
|
|
|
14
14
|
|
|
15
15
|
featureIds?: string[];
|
|
16
16
|
moduleIds?: string[];
|
|
17
|
+
legal_address?: string;
|
|
18
|
+
street_number?: string;
|
|
19
|
+
street?: string;
|
|
20
|
+
city?: string;
|
|
21
|
+
province?: string;
|
|
22
|
+
region?: string;
|
|
23
|
+
postcode?: string;
|
|
24
|
+
country?: string;
|
|
25
|
+
country_code?: string;
|
|
26
|
+
fiscal_data?: string;
|
|
17
27
|
};
|
|
18
28
|
|
|
19
29
|
export interface CompanyInterface extends ApiDataInterface {
|
|
@@ -30,4 +40,14 @@ export interface CompanyInterface extends ApiDataInterface {
|
|
|
30
40
|
|
|
31
41
|
get features(): FeatureInterface[];
|
|
32
42
|
get modules(): ModuleInterface[];
|
|
43
|
+
get legal_address(): string | undefined;
|
|
44
|
+
get street_number(): string | undefined;
|
|
45
|
+
get street(): string | undefined;
|
|
46
|
+
get city(): string | undefined;
|
|
47
|
+
get province(): string | undefined;
|
|
48
|
+
get region(): string | undefined;
|
|
49
|
+
get postcode(): string | undefined;
|
|
50
|
+
get country(): string | undefined;
|
|
51
|
+
get country_code(): string | undefined;
|
|
52
|
+
get fiscal_data(): string | undefined;
|
|
33
53
|
}
|