@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.
Files changed (80) hide show
  1. package/dist/{AuthComponent-B4rNZRYE.d.ts → AuthComponent-DL1D3y7f.d.ts} +1 -1
  2. package/dist/{AuthComponent-nzabiz68.d.mts → AuthComponent-NwQ_ZXsv.d.mts} +1 -1
  3. package/dist/{BlockNoteEditor-Y5XAD6NR.js → BlockNoteEditor-GQM2TZG2.js} +14 -14
  4. package/dist/{BlockNoteEditor-Y5XAD6NR.js.map → BlockNoteEditor-GQM2TZG2.js.map} +1 -1
  5. package/dist/{BlockNoteEditor-SCQGD6F2.mjs → BlockNoteEditor-KCJMA6LW.mjs} +4 -4
  6. package/dist/{auth.interface-C1WjZ0fM.d.ts → auth.interface-BX_1qZZJ.d.ts} +1 -1
  7. package/dist/{auth.interface-fBFqIrw4.d.mts → auth.interface-yeLelxdI.d.mts} +1 -1
  8. package/dist/billing/index.js +346 -346
  9. package/dist/billing/index.mjs +3 -3
  10. package/dist/{chunk-G7PGWMFO.mjs → chunk-35GWVOYZ.mjs} +61 -1
  11. package/dist/{chunk-G7PGWMFO.mjs.map → chunk-35GWVOYZ.mjs.map} +1 -1
  12. package/dist/{chunk-OLNMWVOV.mjs → chunk-4E74ZTRT.mjs} +2115 -1834
  13. package/dist/chunk-4E74ZTRT.mjs.map +1 -0
  14. package/dist/{chunk-LRXJT656.js → chunk-NVXYOQFW.js} +61 -1
  15. package/dist/chunk-NVXYOQFW.js.map +1 -0
  16. package/dist/{chunk-RA4RYKYB.js → chunk-OQRBY22T.js} +11 -11
  17. package/dist/{chunk-RA4RYKYB.js.map → chunk-OQRBY22T.js.map} +1 -1
  18. package/dist/{chunk-5ODPC3YX.js → chunk-QIFM4G7T.js} +1174 -893
  19. package/dist/chunk-QIFM4G7T.js.map +1 -0
  20. package/dist/{chunk-5KMKI23S.mjs → chunk-UXGPZZ6V.mjs} +2 -2
  21. package/dist/client/index.d.mts +6 -6
  22. package/dist/client/index.d.ts +6 -6
  23. package/dist/client/index.js +4 -4
  24. package/dist/client/index.mjs +3 -3
  25. package/dist/components/index.d.mts +42 -7
  26. package/dist/components/index.d.ts +42 -7
  27. package/dist/components/index.js +14 -4
  28. package/dist/components/index.js.map +1 -1
  29. package/dist/components/index.mjs +13 -3
  30. package/dist/{config-DZWAFB7H.d.ts → config-CyCAWW-d.d.ts} +1 -1
  31. package/dist/{config-ndRJIQsP.d.mts → config-D-mqttuF.d.mts} +1 -1
  32. package/dist/{content.interface-B5ySfiOE.d.mts → content.interface-8T5-G84c.d.mts} +1 -1
  33. package/dist/{content.interface-mmz0uMwm.d.ts → content.interface-D-xdYxjt.d.ts} +1 -1
  34. package/dist/contexts/index.d.mts +2 -2
  35. package/dist/contexts/index.d.ts +2 -2
  36. package/dist/contexts/index.js +4 -4
  37. package/dist/contexts/index.mjs +3 -3
  38. package/dist/core/index.d.mts +29 -9
  39. package/dist/core/index.d.ts +29 -9
  40. package/dist/core/index.js +2 -2
  41. package/dist/core/index.mjs +1 -1
  42. package/dist/index.d.mts +8 -8
  43. package/dist/index.d.ts +8 -8
  44. package/dist/index.js +3 -3
  45. package/dist/index.mjs +2 -2
  46. package/dist/{notification.interface-DG7cq9oG.d.mts → notification.interface-C6UcmJqu.d.mts} +20 -0
  47. package/dist/{notification.interface-COKHDQeE.d.ts → notification.interface-ItBxq2au.d.ts} +20 -0
  48. package/dist/{s3.service-ppn9iGJU.d.ts → s3.service-DIR6Su9B.d.ts} +3 -3
  49. package/dist/{s3.service-BoRPFx82.d.mts → s3.service-XchHd3ii.d.mts} +3 -3
  50. package/dist/server/index.d.mts +4 -4
  51. package/dist/server/index.d.ts +4 -4
  52. package/dist/server/index.js +3 -3
  53. package/dist/server/index.mjs +1 -1
  54. package/dist/{useRbacState-DhuYYr0S.d.mts → useRbacState-Btk1gkQg.d.mts} +1 -1
  55. package/dist/{useRbacState-NnzNL2ED.d.ts → useRbacState-CUj0hp8t.d.ts} +1 -1
  56. package/dist/{useSocket-bsV-K4qR.d.ts → useSocket-BSUN9s3p.d.ts} +1 -1
  57. package/dist/{useSocket-CtfuR5wD.d.mts → useSocket-DKI92Fbg.d.mts} +1 -1
  58. package/package.json +2 -1
  59. package/src/components/containers/RoundPageContainer.tsx +1 -1
  60. package/src/components/fiscal/FiscalDataDisplay.tsx +26 -0
  61. package/src/components/fiscal/ItalianFiscalData.tsx +120 -0
  62. package/src/components/fiscal/ItalianFiscalDataDisplay.tsx +24 -0
  63. package/src/components/fiscal/index.ts +4 -0
  64. package/src/components/index.ts +2 -0
  65. package/src/components/navigations/Breadcrumb.tsx +4 -4
  66. package/src/components/navigations/RecentPagesNavigator.tsx +3 -3
  67. package/src/features/company/components/details/CompanyContent.tsx +105 -0
  68. package/src/features/company/components/details/CompanyDetails.tsx +2 -19
  69. package/src/features/company/components/details/index.ts +1 -0
  70. package/src/features/company/components/forms/CompanyConfigurationEditor.tsx +38 -70
  71. package/src/features/company/components/forms/CompanyEditor.tsx +214 -172
  72. package/src/features/company/data/company.interface.ts +20 -0
  73. package/src/features/company/data/company.ts +73 -0
  74. package/src/utils/fiscal-utils.ts +7 -0
  75. package/src/utils/italian-validators.ts +79 -0
  76. package/dist/chunk-5ODPC3YX.js.map +0 -1
  77. package/dist/chunk-LRXJT656.js.map +0 -1
  78. package/dist/chunk-OLNMWVOV.mjs.map +0 -1
  79. /package/dist/{BlockNoteEditor-SCQGD6F2.mjs.map → BlockNoteEditor-KCJMA6LW.mjs.map} +0 -0
  80. /package/dist/{chunk-5KMKI23S.mjs.map → chunk-UXGPZZ6V.mjs.map} +0 -0
@@ -109,6 +109,16 @@ type CompanyInput = {
109
109
  availableExtraTokens?: number;
110
110
  featureIds?: string[];
111
111
  moduleIds?: string[];
112
+ legal_address?: string;
113
+ street_number?: string;
114
+ street?: string;
115
+ city?: string;
116
+ province?: string;
117
+ region?: string;
118
+ postcode?: string;
119
+ country?: string;
120
+ country_code?: string;
121
+ fiscal_data?: string;
112
122
  };
113
123
  interface CompanyInterface extends ApiDataInterface {
114
124
  get name(): string;
@@ -121,6 +131,16 @@ interface CompanyInterface extends ApiDataInterface {
121
131
  get availableExtraTokens(): number;
122
132
  get features(): FeatureInterface[];
123
133
  get modules(): ModuleInterface[];
134
+ get legal_address(): string | undefined;
135
+ get street_number(): string | undefined;
136
+ get street(): string | undefined;
137
+ get city(): string | undefined;
138
+ get province(): string | undefined;
139
+ get region(): string | undefined;
140
+ get postcode(): string | undefined;
141
+ get country(): string | undefined;
142
+ get country_code(): string | undefined;
143
+ get fiscal_data(): string | undefined;
124
144
  }
125
145
 
126
146
  type RoleInput = {
@@ -109,6 +109,16 @@ type CompanyInput = {
109
109
  availableExtraTokens?: number;
110
110
  featureIds?: string[];
111
111
  moduleIds?: string[];
112
+ legal_address?: string;
113
+ street_number?: string;
114
+ street?: string;
115
+ city?: string;
116
+ province?: string;
117
+ region?: string;
118
+ postcode?: string;
119
+ country?: string;
120
+ country_code?: string;
121
+ fiscal_data?: string;
112
122
  };
113
123
  interface CompanyInterface extends ApiDataInterface {
114
124
  get name(): string;
@@ -121,6 +131,16 @@ interface CompanyInterface extends ApiDataInterface {
121
131
  get availableExtraTokens(): number;
122
132
  get features(): FeatureInterface[];
123
133
  get modules(): ModuleInterface[];
134
+ get legal_address(): string | undefined;
135
+ get street_number(): string | undefined;
136
+ get street(): string | undefined;
137
+ get city(): string | undefined;
138
+ get province(): string | undefined;
139
+ get region(): string | undefined;
140
+ get postcode(): string | undefined;
141
+ get country(): string | undefined;
142
+ get country_code(): string | undefined;
143
+ get fiscal_data(): string | undefined;
124
144
  }
125
145
 
126
146
  type RoleInput = {
@@ -1,8 +1,8 @@
1
1
  import { F as FeatureInterface } from './feature.interface-CIWxo8NP.js';
2
- import { R as RoleInterface, U as UserInterface, k as UserInput, h as CompanyInterface, C as CompanyInput, N as NotificationInterface } from './notification.interface-COKHDQeE.js';
2
+ import { R as RoleInterface, U as UserInterface, k as UserInput, h as CompanyInterface, C as CompanyInput, N as NotificationInterface } from './notification.interface-ItBxq2au.js';
3
3
  import { A as ApiDataInterface, J as JsonApiHydratedDataInterface } from './ApiDataInterface-DPP8s46n.js';
4
- import { A as AuthInput, b as AuthInterface } from './auth.interface-C1WjZ0fM.js';
5
- import { a as ContentInterface } from './content.interface-mmz0uMwm.js';
4
+ import { A as AuthInput, b as AuthInterface } from './auth.interface-BX_1qZZJ.js';
5
+ import { a as ContentInterface } from './content.interface-D-xdYxjt.js';
6
6
  import { A as ApiRequestDataTypeInterface } from './ApiRequestDataTypeInterface-CYEcRUrh.js';
7
7
 
8
8
  declare enum HttpMethod {
@@ -1,8 +1,8 @@
1
1
  import { F as FeatureInterface } from './feature.interface-BxFFOPNq.mjs';
2
- import { R as RoleInterface, U as UserInterface, k as UserInput, h as CompanyInterface, C as CompanyInput, N as NotificationInterface } from './notification.interface-DG7cq9oG.mjs';
2
+ import { R as RoleInterface, U as UserInterface, k as UserInput, h as CompanyInterface, C as CompanyInput, N as NotificationInterface } from './notification.interface-C6UcmJqu.mjs';
3
3
  import { A as ApiDataInterface, J as JsonApiHydratedDataInterface } from './ApiDataInterface-DPP8s46n.mjs';
4
- import { A as AuthInput, b as AuthInterface } from './auth.interface-fBFqIrw4.mjs';
5
- import { a as ContentInterface } from './content.interface-B5ySfiOE.mjs';
4
+ import { A as AuthInput, b as AuthInterface } from './auth.interface-yeLelxdI.mjs';
5
+ import { a as ContentInterface } from './content.interface-8T5-G84c.mjs';
6
6
  import { A as ApiRequestDataTypeInterface } from './ApiRequestDataTypeInterface-CYEcRUrh.mjs';
7
7
 
8
8
  declare enum HttpMethod {
@@ -1,13 +1,13 @@
1
1
  import { A as ApiData } from '../ApiData-DPKNfY-9.mjs';
2
- import { M as ModuleWithPermissions, A as Action } from '../notification.interface-DG7cq9oG.mjs';
2
+ import { M as ModuleWithPermissions, A as Action } from '../notification.interface-C6UcmJqu.mjs';
3
3
  import { A as ApiRequestDataTypeInterface } from '../ApiRequestDataTypeInterface-CYEcRUrh.mjs';
4
4
  import { A as ApiResponseInterface } from '../ApiResponseInterface-zeewugD7.mjs';
5
- export { A as ServerAuthService, C as ServerCompanyService, a as ServerContentService, F as ServerFeatureService, N as ServerNotificationService, P as ServerPushService, R as ServerRoleService, S as ServerS3Service, U as ServerUserService } from '../s3.service-BoRPFx82.mjs';
5
+ export { A as ServerAuthService, C as ServerCompanyService, a as ServerContentService, F as ServerFeatureService, N as ServerNotificationService, P as ServerPushService, R as ServerRoleService, S as ServerS3Service, U as ServerUserService } from '../s3.service-XchHd3ii.mjs';
6
6
  import 'lucide-react';
7
7
  import '../ApiDataInterface-DPP8s46n.mjs';
8
8
  import '../feature.interface-BxFFOPNq.mjs';
9
- import '../auth.interface-fBFqIrw4.mjs';
10
- import '../content.interface-B5ySfiOE.mjs';
9
+ import '../auth.interface-yeLelxdI.mjs';
10
+ import '../content.interface-8T5-G84c.mjs';
11
11
 
12
12
  type CacheProfile = "seconds" | "minutes" | "hours" | "days" | "weeks" | "max" | "default";
13
13
  /**
@@ -1,13 +1,13 @@
1
1
  import { A as ApiData } from '../ApiData-DPKNfY-9.js';
2
- import { M as ModuleWithPermissions, A as Action } from '../notification.interface-COKHDQeE.js';
2
+ import { M as ModuleWithPermissions, A as Action } from '../notification.interface-ItBxq2au.js';
3
3
  import { A as ApiRequestDataTypeInterface } from '../ApiRequestDataTypeInterface-CYEcRUrh.js';
4
4
  import { A as ApiResponseInterface } from '../ApiResponseInterface-CAIAeP5d.js';
5
- export { A as ServerAuthService, C as ServerCompanyService, a as ServerContentService, F as ServerFeatureService, N as ServerNotificationService, P as ServerPushService, R as ServerRoleService, S as ServerS3Service, U as ServerUserService } from '../s3.service-ppn9iGJU.js';
5
+ export { A as ServerAuthService, C as ServerCompanyService, a as ServerContentService, F as ServerFeatureService, N as ServerNotificationService, P as ServerPushService, R as ServerRoleService, S as ServerS3Service, U as ServerUserService } from '../s3.service-DIR6Su9B.js';
6
6
  import 'lucide-react';
7
7
  import '../ApiDataInterface-DPP8s46n.js';
8
8
  import '../feature.interface-CIWxo8NP.js';
9
- import '../auth.interface-C1WjZ0fM.js';
10
- import '../content.interface-mmz0uMwm.js';
9
+ import '../auth.interface-BX_1qZZJ.js';
10
+ import '../content.interface-D-xdYxjt.js';
11
11
 
12
12
  type CacheProfile = "seconds" | "minutes" | "hours" | "days" | "weeks" | "max" | "default";
13
13
  /**
@@ -15,7 +15,7 @@ var _chunk3ZPK4QOBjs = require('../chunk-3ZPK4QOB.js');
15
15
 
16
16
 
17
17
 
18
- var _chunkLRXJT656js = require('../chunk-LRXJT656.js');
18
+ var _chunkNVXYOQFWjs = require('../chunk-NVXYOQFW.js');
19
19
  require('../chunk-LXKSUWAV.js');
20
20
  require('../chunk-IBS6NI7D.js');
21
21
 
@@ -86,7 +86,7 @@ var ServerSession = class {
86
86
  if (!rawModules) return false;
87
87
  const modules = JSON.parse(_pako2.default.ungzip(Buffer.from(rawModules, "base64"), { to: "string" }));
88
88
  const selectedModule = modules.find((module) => module.id === params.module.moduleId);
89
- return _chunkLRXJT656js.checkPermissionsFromServer.call(void 0, {
89
+ return _chunkNVXYOQFWjs.checkPermissionsFromServer.call(void 0, {
90
90
  module: params.module,
91
91
  action: params.action,
92
92
  data: params.data,
@@ -296,5 +296,5 @@ _chunk7QVYU63Ejs.__name.call(void 0, ServerJsonApiDelete, "ServerJsonApiDelete")
296
296
 
297
297
 
298
298
 
299
- exports.ServerAuthService = _chunkLRXJT656js.AuthService; exports.ServerCompanyService = _chunkLRXJT656js.CompanyService; exports.ServerContentService = _chunkLRXJT656js.ContentService; exports.ServerFeatureService = _chunkLRXJT656js.FeatureService; exports.ServerJsonApiDelete = ServerJsonApiDelete; exports.ServerJsonApiGet = ServerJsonApiGet; exports.ServerJsonApiPatch = ServerJsonApiPatch; exports.ServerJsonApiPost = ServerJsonApiPost; exports.ServerJsonApiPut = ServerJsonApiPut; exports.ServerNotificationService = _chunkLRXJT656js.NotificationService; exports.ServerPushService = _chunkLRXJT656js.PushService; exports.ServerRoleService = _chunkLRXJT656js.RoleService; exports.ServerS3Service = _chunkLRXJT656js.S3Service; exports.ServerSession = ServerSession; exports.ServerUserService = _chunkLRXJT656js.UserService; exports.configureServerJsonApi = configureServerJsonApi; exports.getServerApiUrl = getServerApiUrl; exports.getServerAppUrl = getServerAppUrl; exports.getServerToken = _chunkYUO55Q5Ajs.getServerToken; exports.getServerTrackablePages = getServerTrackablePages; exports.invalidateCacheTag = invalidateCacheTag; exports.invalidateCacheTags = invalidateCacheTags; exports.serverRequest = _chunk3ZPK4QOBjs.serverRequest;
299
+ exports.ServerAuthService = _chunkNVXYOQFWjs.AuthService; exports.ServerCompanyService = _chunkNVXYOQFWjs.CompanyService; exports.ServerContentService = _chunkNVXYOQFWjs.ContentService; exports.ServerFeatureService = _chunkNVXYOQFWjs.FeatureService; exports.ServerJsonApiDelete = ServerJsonApiDelete; exports.ServerJsonApiGet = ServerJsonApiGet; exports.ServerJsonApiPatch = ServerJsonApiPatch; exports.ServerJsonApiPost = ServerJsonApiPost; exports.ServerJsonApiPut = ServerJsonApiPut; exports.ServerNotificationService = _chunkNVXYOQFWjs.NotificationService; exports.ServerPushService = _chunkNVXYOQFWjs.PushService; exports.ServerRoleService = _chunkNVXYOQFWjs.RoleService; exports.ServerS3Service = _chunkNVXYOQFWjs.S3Service; exports.ServerSession = ServerSession; exports.ServerUserService = _chunkNVXYOQFWjs.UserService; exports.configureServerJsonApi = configureServerJsonApi; exports.getServerApiUrl = getServerApiUrl; exports.getServerAppUrl = getServerAppUrl; exports.getServerToken = _chunkYUO55Q5Ajs.getServerToken; exports.getServerTrackablePages = getServerTrackablePages; exports.invalidateCacheTag = invalidateCacheTag; exports.invalidateCacheTags = invalidateCacheTags; exports.serverRequest = _chunk3ZPK4QOBjs.serverRequest;
300
300
  //# sourceMappingURL=index.js.map
@@ -15,7 +15,7 @@ import {
15
15
  S3Service,
16
16
  UserService,
17
17
  checkPermissionsFromServer
18
- } from "../chunk-G7PGWMFO.mjs";
18
+ } from "../chunk-35GWVOYZ.mjs";
19
19
  import "../chunk-AUXK7QSA.mjs";
20
20
  import "../chunk-C7C7VY4F.mjs";
21
21
  import {
@@ -1,5 +1,5 @@
1
1
  import { F as FeatureInterface } from './feature.interface-BxFFOPNq.mjs';
2
- import { R as RoleInterface } from './notification.interface-DG7cq9oG.mjs';
2
+ import { R as RoleInterface } from './notification.interface-C6UcmJqu.mjs';
3
3
  import { P as PermissionMappingInterface, M as ModulePathsInterface, A as ActionType, a as PermissionValue, b as PermissionsMap } from './ModulePathsInterface-49EWvbWy.mjs';
4
4
 
5
5
  type PageInfo = {
@@ -1,5 +1,5 @@
1
1
  import { F as FeatureInterface } from './feature.interface-CIWxo8NP.js';
2
- import { R as RoleInterface } from './notification.interface-COKHDQeE.js';
2
+ import { R as RoleInterface } from './notification.interface-ItBxq2au.js';
3
3
  import { P as PermissionMappingInterface, M as ModulePathsInterface, A as ActionType, a as PermissionValue, b as PermissionsMap } from './ModulePathsInterface-wVS5Raa4.js';
4
4
 
5
5
  type PageInfo = {
@@ -1,4 +1,4 @@
1
- import { N as NotificationInterface } from './notification.interface-COKHDQeE.js';
1
+ import { N as NotificationInterface } from './notification.interface-ItBxq2au.js';
2
2
 
3
3
  interface UseSocketOptions {
4
4
  token: string;
@@ -1,4 +1,4 @@
1
- import { N as NotificationInterface } from './notification.interface-DG7cq9oG.mjs';
1
+ import { N as NotificationInterface } from './notification.interface-C6UcmJqu.mjs';
2
2
 
3
3
  interface UseSocketOptions {
4
4
  token: string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@carlonicora/nextjs-jsonapi",
3
- "version": "1.65.0",
3
+ "version": "1.66.0",
4
4
  "description": "Next.js JSON:API client with server/client support and caching",
5
5
  "author": "Carlo Nicora",
6
6
  "license": "GPL-3.0-or-later",
@@ -116,6 +116,7 @@
116
116
  "class-variance-authority": "^0.7.1",
117
117
  "clsx": "^2.1.1",
118
118
  "cmdk": "^1.1.1",
119
+ "codice-fiscale-js": "^2.3.23",
119
120
  "commander": "^14.0.2",
120
121
  "cookies-next": "^6.1.1",
121
122
  "d3": "^7.9.0",
@@ -62,7 +62,7 @@ export function RoundPageContainer({
62
62
  )}
63
63
  <div className="flex h-full w-full overflow-hidden">
64
64
  <div className={cn(`grow overflow-y-auto p-4`, fullWidth && `p-0`)}>
65
- <div className={cn(`mx-auto max-w-6xl space-y-12 p-8`, fullWidth && `max-w-full w-full p-0`)}>
65
+ <div className={cn(`mx-auto max-w-6xl space-y-12 p-8`, fullWidth && `max-w-full w-full p-0 h-full`)}>
66
66
  {tabs ? (
67
67
  <Tabs
68
68
  value={activeTab}
@@ -0,0 +1,26 @@
1
+ "use client";
2
+
3
+ import { parseFiscalData } from "../../utils/fiscal-utils";
4
+ import { ItalianFiscalDataDisplay } from "./ItalianFiscalDataDisplay";
5
+
6
+ type FiscalDataDisplayProps = {
7
+ fiscalData: string;
8
+ country?: string;
9
+ };
10
+
11
+ function hasNonEmptyValues(data: Record<string, string>): boolean {
12
+ return Object.values(data).some((v) => v && v.trim() !== "");
13
+ }
14
+
15
+ export function FiscalDataDisplay({ fiscalData, country = "it" }: FiscalDataDisplayProps) {
16
+ const data = parseFiscalData(fiscalData);
17
+
18
+ if (!hasNonEmptyValues(data)) return null;
19
+
20
+ switch (country) {
21
+ case "it":
22
+ return <ItalianFiscalDataDisplay data={data} />;
23
+ default:
24
+ return <ItalianFiscalDataDisplay data={data} />;
25
+ }
26
+ }
@@ -0,0 +1,120 @@
1
+ "use client";
2
+
3
+ import { validateItalianTaxCode, validatePartitaIva } from "../../utils/italian-validators";
4
+ import { Field, FieldError, FieldLabel, Input } from "../../shadcnui";
5
+ import { useTranslations } from "next-intl";
6
+ import { forwardRef, useCallback, useImperativeHandle, useRef, useState } from "react";
7
+ import { z } from "zod";
8
+
9
+ export interface FiscalDataHandle {
10
+ validate: () => boolean;
11
+ getData: () => Record<string, string>;
12
+ isDirty: () => boolean;
13
+ reset: (initialData: Record<string, string>) => void;
14
+ }
15
+
16
+ export interface ItalianFiscalDataProps {
17
+ initialData: Record<string, string>;
18
+ }
19
+
20
+ const ItalianFiscalData = forwardRef<FiscalDataHandle, ItalianFiscalDataProps>(function ItalianFiscalData(
21
+ { initialData },
22
+ ref,
23
+ ) {
24
+ const t = useTranslations();
25
+ const initialRef = useRef<Record<string, string>>(initialData);
26
+ const [fiscalData, setFiscalData] = useState<Record<string, string>>(initialData);
27
+ const fiscalDataRef = useRef<Record<string, string>>(initialData);
28
+ const [fiscalErrors, setFiscalErrors] = useState<Record<string, string>>({});
29
+
30
+ const updateFiscalField = useCallback((key: string, value: string) => {
31
+ setFiscalData((prev) => {
32
+ const next = { ...prev, [key]: value };
33
+ fiscalDataRef.current = next;
34
+ return next;
35
+ });
36
+ setFiscalErrors((prev) => {
37
+ const next = { ...prev };
38
+ delete next[key];
39
+ return next;
40
+ });
41
+ }, []);
42
+
43
+ useImperativeHandle(ref, () => ({
44
+ validate: (): boolean => {
45
+ const data = fiscalDataRef.current;
46
+ const errors: Record<string, string> = {};
47
+
48
+ if (data.codice_fiscale && !validateItalianTaxCode(data.codice_fiscale, "codiceFiscale")) {
49
+ errors.codice_fiscale = t("common.fields.codice_fiscale.error");
50
+ }
51
+ if (data.partita_iva && !validatePartitaIva(data.partita_iva)) {
52
+ errors.partita_iva = t("common.fields.partita_iva.error");
53
+ }
54
+ if (data.pec && !z.string().email().safeParse(data.pec).success) {
55
+ errors.pec = t("common.fields.pec.error");
56
+ }
57
+
58
+ setFiscalErrors(errors);
59
+ return Object.keys(errors).length === 0;
60
+ },
61
+ getData: (): Record<string, string> => fiscalDataRef.current,
62
+ isDirty: (): boolean => JSON.stringify(fiscalDataRef.current) !== JSON.stringify(initialRef.current),
63
+ reset: (newInitialData: Record<string, string>) => {
64
+ initialRef.current = newInitialData;
65
+ fiscalDataRef.current = newInitialData;
66
+ setFiscalData(newInitialData);
67
+ setFiscalErrors({});
68
+ },
69
+ }));
70
+
71
+ return (
72
+ <div className="grid grid-cols-1 gap-4 md:grid-cols-3">
73
+ <Field data-invalid={!!fiscalErrors.codice_fiscale}>
74
+ <FieldLabel>{t("common.fields.codice_fiscale.label")}</FieldLabel>
75
+ <Input
76
+ value={fiscalData.codice_fiscale || ""}
77
+ onChange={(e) => updateFiscalField("codice_fiscale", e.target.value)}
78
+ placeholder={t("common.fields.codice_fiscale.placeholder")}
79
+ />
80
+ {fiscalErrors.codice_fiscale && <FieldError>{fiscalErrors.codice_fiscale}</FieldError>}
81
+ </Field>
82
+ <Field data-invalid={!!fiscalErrors.partita_iva}>
83
+ <FieldLabel>{t("common.fields.partita_iva.label")}</FieldLabel>
84
+ <Input
85
+ value={fiscalData.partita_iva || ""}
86
+ onChange={(e) => updateFiscalField("partita_iva", e.target.value)}
87
+ placeholder={t("common.fields.partita_iva.placeholder")}
88
+ />
89
+ {fiscalErrors.partita_iva && <FieldError>{fiscalErrors.partita_iva}</FieldError>}
90
+ </Field>
91
+ <Field>
92
+ <FieldLabel>{t("common.fields.sdi.label")}</FieldLabel>
93
+ <Input
94
+ value={fiscalData.sdi || ""}
95
+ onChange={(e) => updateFiscalField("sdi", e.target.value)}
96
+ placeholder={t("common.fields.sdi.placeholder")}
97
+ />
98
+ </Field>
99
+ <Field>
100
+ <FieldLabel>{t("common.fields.rea.label")}</FieldLabel>
101
+ <Input
102
+ value={fiscalData.rea || ""}
103
+ onChange={(e) => updateFiscalField("rea", e.target.value)}
104
+ placeholder={t("common.fields.rea.placeholder")}
105
+ />
106
+ </Field>
107
+ <Field data-invalid={!!fiscalErrors.pec}>
108
+ <FieldLabel>{t("common.fields.pec.label")}</FieldLabel>
109
+ <Input
110
+ value={fiscalData.pec || ""}
111
+ onChange={(e) => updateFiscalField("pec", e.target.value)}
112
+ placeholder={t("common.fields.pec.placeholder")}
113
+ />
114
+ {fiscalErrors.pec && <FieldError>{fiscalErrors.pec}</FieldError>}
115
+ </Field>
116
+ </div>
117
+ );
118
+ });
119
+
120
+ export default ItalianFiscalData;
@@ -0,0 +1,24 @@
1
+ "use client";
2
+
3
+ import { AttributeElement } from "../contents";
4
+ import { useTranslations } from "next-intl";
5
+
6
+ type ItalianFiscalDataDisplayProps = {
7
+ data: Record<string, string>;
8
+ };
9
+
10
+ export function ItalianFiscalDataDisplay({ data }: ItalianFiscalDataDisplayProps) {
11
+ const t = useTranslations();
12
+
13
+ return (
14
+ <div className="grid grid-cols-1 gap-4 md:grid-cols-3">
15
+ {data.codice_fiscale && (
16
+ <AttributeElement title={t("common.fields.codice_fiscale.label")} value={data.codice_fiscale} />
17
+ )}
18
+ {data.partita_iva && <AttributeElement title={t("common.fields.partita_iva.label")} value={data.partita_iva} />}
19
+ {data.sdi && <AttributeElement title={t("common.fields.sdi.label")} value={data.sdi} />}
20
+ {data.rea && <AttributeElement title={t("common.fields.rea.label")} value={data.rea} />}
21
+ {data.pec && <AttributeElement title={t("common.fields.pec.label")} value={data.pec} />}
22
+ </div>
23
+ );
24
+ }
@@ -0,0 +1,4 @@
1
+ export { default as ItalianFiscalData } from "./ItalianFiscalData";
2
+ export type { FiscalDataHandle, ItalianFiscalDataProps } from "./ItalianFiscalData";
3
+ export { ItalianFiscalDataDisplay } from "./ItalianFiscalDataDisplay";
4
+ export { FiscalDataDisplay } from "./FiscalDataDisplay";
@@ -10,6 +10,8 @@ export * from "./forms";
10
10
  export * from "./navigations";
11
11
  export * from "./pages";
12
12
  export * from "./tables";
13
+ export * from "./fiscal";
14
+ export { parseFiscalData } from "../utils/fiscal-utils";
13
15
 
14
16
  // Feature components
15
17
  export * from "../features/auth/components";
@@ -19,7 +19,7 @@ import {
19
19
 
20
20
  type BreadcrumbProps = { items: BreadcrumbItemData[] };
21
21
 
22
- const ITEMS_TO_DISPLAY = 3;
22
+ const ITEMS_TO_DISPLAY = 4;
23
23
 
24
24
  export function BreadcrumbNavigation({ items }: BreadcrumbProps) {
25
25
  const generateUrl = usePageUrlGenerator();
@@ -47,7 +47,7 @@ export function BreadcrumbNavigation({ items }: BreadcrumbProps) {
47
47
  <BreadcrumbEllipsis className="h-4 w-4" />
48
48
  </DropdownMenuTrigger>
49
49
  <DropdownMenuContent align="start">
50
- {items.slice(1, -ITEMS_TO_DISPLAY + 1).map((item, index) => (
50
+ {items.slice(1, -ITEMS_TO_DISPLAY + 2).map((item, index) => (
51
51
  <DropdownMenuItem key={index}>
52
52
  <Link href={item.href ? item.href : "#"}>{item.name}</Link>
53
53
  </DropdownMenuItem>
@@ -56,12 +56,12 @@ export function BreadcrumbNavigation({ items }: BreadcrumbProps) {
56
56
  </DropdownMenu>
57
57
  </BreadcrumbItem>
58
58
  <BreadcrumbSeparator />
59
- {items.slice(-ITEMS_TO_DISPLAY + 1).map((item, index) => (
59
+ {items.slice(-ITEMS_TO_DISPLAY + 2).map((item, index) => (
60
60
  <Fragment key={index}>
61
61
  <BreadcrumbItem>
62
62
  {item.href ? <Link href={item.href}>{item.name}</Link> : <>{item.name}</>}
63
63
  </BreadcrumbItem>
64
- {index < items.slice(-ITEMS_TO_DISPLAY + 1).length - 1 && <BreadcrumbSeparator />}
64
+ {index < items.slice(-ITEMS_TO_DISPLAY + 2).length - 1 && <BreadcrumbSeparator />}
65
65
  </Fragment>
66
66
  ))}
67
67
  </>
@@ -26,10 +26,10 @@ export function RecentPagesNavigator() {
26
26
 
27
27
  return (
28
28
  <DropdownMenu>
29
- <DropdownMenuTrigger>
30
- <div className="flex w-full cursor-pointer items-center gap-2">
29
+ <DropdownMenuTrigger render={<span />} nativeButton={false}>
30
+ <span className="flex w-full cursor-pointer items-center gap-2">
31
31
  {state === "collapsed" ? <HistoryIcon className="h-4 w-4" /> : <span>{t(`common.recent_pages`)}</span>}
32
- </div>
32
+ </span>
33
33
  </DropdownMenuTrigger>
34
34
  <DropdownMenuContent align="start" className="w-96">
35
35
  <DropdownMenuLabel>{t(`common.recent_pages`)}</DropdownMenuLabel>
@@ -0,0 +1,105 @@
1
+ "use client";
2
+
3
+ import { MapPinIcon } from "lucide-react";
4
+ import { useTranslations } from "next-intl";
5
+ import Image from "next/image";
6
+ import { ReactNode } from "react";
7
+ import { AttributeElement } from "../../../../components";
8
+ import { FiscalDataDisplay } from "../../../../components/fiscal/FiscalDataDisplay";
9
+ import { CompanyInterface } from "../../data";
10
+
11
+ type CompanyContentProps = {
12
+ company?: CompanyInterface;
13
+ actions?: ReactNode;
14
+ };
15
+
16
+ function hasAddressDetails(company: CompanyInterface): boolean {
17
+ return !!(
18
+ company.street ||
19
+ company.street_number ||
20
+ company.city ||
21
+ company.province ||
22
+ company.region ||
23
+ company.postcode ||
24
+ company.country
25
+ );
26
+ }
27
+
28
+ export function CompanyContent({ company, actions }: CompanyContentProps) {
29
+ const t = useTranslations();
30
+
31
+ if (!company) return null;
32
+
33
+ return (
34
+ <div className="flex flex-col gap-y-8">
35
+ {/* Title Row */}
36
+ <div className="flex w-full items-center justify-between">
37
+ <h2 className="text-lg font-semibold">{company.name}</h2>
38
+ {actions && <div className="flex items-center gap-x-2">{actions}</div>}
39
+ </div>
40
+
41
+ {/* Hero Section */}
42
+ <div className="flex items-start gap-x-6">
43
+ {company.logo && (
44
+ <Image src={company.logo} alt={company.name} width={150} height={150} className="rounded-md" />
45
+ )}
46
+ <div className="flex flex-col gap-y-2">
47
+ {company.legal_address && (
48
+ <div className="text-muted-foreground flex items-center gap-x-2 text-sm">
49
+ <MapPinIcon className="h-4 w-4 shrink-0" />
50
+ {company.legal_address}
51
+ </div>
52
+ )}
53
+ <div className="flex flex-col gap-y-1">
54
+ {company.configurations?.country && (
55
+ <div className="text-muted-foreground text-sm">
56
+ <span className="font-medium">{t("features.configuration.country")}:</span>{" "}
57
+ {company.configurations.country}
58
+ </div>
59
+ )}
60
+ {company.configurations?.currency && (
61
+ <div className="text-muted-foreground text-sm">
62
+ <span className="font-medium">{t("features.configuration.currency")}:</span>{" "}
63
+ {company.configurations.currency}
64
+ </div>
65
+ )}
66
+ </div>
67
+ </div>
68
+ </div>
69
+
70
+ {/* Fiscal Data Section */}
71
+ {company.fiscal_data && (
72
+ <div className="flex flex-col gap-y-3">
73
+ <h3 className="text-muted-foreground text-xs font-semibold tracking-wider uppercase">
74
+ {t("company.sections.fiscal_data")}
75
+ </h3>
76
+ <FiscalDataDisplay fiscalData={company.fiscal_data} />
77
+ </div>
78
+ )}
79
+
80
+ {/* Address Details Section */}
81
+ {hasAddressDetails(company) && (
82
+ <div className="flex flex-col gap-y-3">
83
+ <h3 className="text-muted-foreground text-xs font-semibold tracking-wider uppercase">
84
+ {t("company.sections.address_details")}
85
+ </h3>
86
+ <div className="grid grid-cols-1 gap-4 md:grid-cols-3">
87
+ {company.street && <AttributeElement title={t("company.fields.street.label")} value={company.street} />}
88
+ {company.street_number && (
89
+ <AttributeElement title={t("company.fields.street_number.label")} value={company.street_number} />
90
+ )}
91
+ {company.city && <AttributeElement title={t("company.fields.city.label")} value={company.city} />}
92
+ {company.province && (
93
+ <AttributeElement title={t("company.fields.province.label")} value={company.province} />
94
+ )}
95
+ {company.region && <AttributeElement title={t("company.fields.region.label")} value={company.region} />}
96
+ {company.postcode && (
97
+ <AttributeElement title={t("company.fields.postcode.label")} value={company.postcode} />
98
+ )}
99
+ {company.country && <AttributeElement title={t("company.fields.country.label")} value={company.country} />}
100
+ </div>
101
+ </div>
102
+ )}
103
+ </div>
104
+ );
105
+ }
@@ -1,16 +1,14 @@
1
1
  "use client";
2
2
 
3
- import { useTranslations } from "next-intl";
4
- import Image from "next/image";
5
3
  import { Modules } from "../../../../";
6
4
  import { ContentTitle } from "../../../../components";
7
5
  import { useSharedContext } from "../../../../contexts";
8
6
  import { usePageUrlGenerator } from "../../../../hooks";
9
7
  import { useCompanyContext } from "../../contexts/CompanyContext";
8
+ import { CompanyContent } from "./CompanyContent";
10
9
  import { TokenStatusIndicator } from "./TokenStatusIndicator";
11
10
 
12
11
  export function CompanyDetails() {
13
- const t = useTranslations();
14
12
  const { title } = useSharedContext();
15
13
  const _generateUrl = usePageUrlGenerator();
16
14
 
@@ -20,23 +18,8 @@ export function CompanyDetails() {
20
18
  return (
21
19
  <div className="flex w-full flex-col gap-y-2">
22
20
  <ContentTitle module={Modules.Company} type={title.type} element={title.element} functions={title.functions} />
23
- {company.logo && (
24
- <Image src={company.logo} alt={company.name} width={150} height={150} className="mb-4 rounded-md" />
25
- )}
26
21
  <TokenStatusIndicator size="md" />
27
- <div className="flex flex-col gap-y-1">
28
- {company.configurations?.country && (
29
- <div className="text-muted-foreground text-sm">
30
- <span className="font-medium">{t("features.configuration.country")}:</span> {company.configurations.country}
31
- </div>
32
- )}
33
- {company.configurations?.currency && (
34
- <div className="text-muted-foreground text-sm">
35
- <span className="font-medium">{t("features.configuration.currency")}:</span>{" "}
36
- {company.configurations.currency}
37
- </div>
38
- )}
39
- </div>
22
+ <CompanyContent company={company} />
40
23
  </div>
41
24
  );
42
25
  }
@@ -1,2 +1,3 @@
1
+ export * from "./CompanyContent";
1
2
  export * from "./CompanyDetails";
2
3
  export * from "./TokenStatusIndicator";