@greatapps/common 1.1.791 → 1.1.793

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 (59) hide show
  1. package/dist/components/account/AccountHubLinkHandler.mjs +16 -4
  2. package/dist/components/account/AccountHubLinkHandler.mjs.map +1 -1
  3. package/dist/components/account/AccountUnsavedChangesDialog.mjs +34 -0
  4. package/dist/components/account/AccountUnsavedChangesDialog.mjs.map +1 -0
  5. package/dist/components/account/MyAccountModal.mjs +28 -22
  6. package/dist/components/account/MyAccountModal.mjs.map +1 -1
  7. package/dist/components/account/hooks/useAccountHubActions.mjs +15 -0
  8. package/dist/components/account/hooks/useAccountHubActions.mjs.map +1 -1
  9. package/dist/components/account/sections/MyProfileSection.mjs +20 -22
  10. package/dist/components/account/sections/MyProfileSection.mjs.map +1 -1
  11. package/dist/components/account/sections/PreferencesSection.mjs +15 -20
  12. package/dist/components/account/sections/PreferencesSection.mjs.map +1 -1
  13. package/dist/components/account/sections/subscription/PaymentConfirmationDialog.mjs +3 -2
  14. package/dist/components/account/sections/subscription/PaymentConfirmationDialog.mjs.map +1 -1
  15. package/dist/components/ui/data-display/SuccessConfettiIcon.mjs +40 -0
  16. package/dist/components/ui/data-display/SuccessConfettiIcon.mjs.map +1 -0
  17. package/dist/enums/AccountHubFlow.mjs +1 -0
  18. package/dist/enums/AccountHubFlow.mjs.map +1 -1
  19. package/dist/i18n/messages/en-us.mjs +5 -0
  20. package/dist/i18n/messages/en-us.mjs.map +1 -1
  21. package/dist/i18n/messages/es-es.mjs +5 -0
  22. package/dist/i18n/messages/es-es.mjs.map +1 -1
  23. package/dist/i18n/messages/pt-br.mjs +5 -0
  24. package/dist/i18n/messages/pt-br.mjs.map +1 -1
  25. package/dist/index.mjs +9 -1
  26. package/dist/index.mjs.map +1 -1
  27. package/dist/middlewares/create-auth-middleware.mjs +1 -1
  28. package/dist/middlewares/create-auth-middleware.mjs.map +1 -1
  29. package/dist/modules/accounts/constants/account-hub-link.constants.mjs +9 -1
  30. package/dist/modules/accounts/constants/account-hub-link.constants.mjs.map +1 -1
  31. package/dist/modules/accounts/utils/build-account-hub-url.mjs +13 -1
  32. package/dist/modules/accounts/utils/build-account-hub-url.mjs.map +1 -1
  33. package/dist/modules/accounts/utils/is-billing-period.mjs +8 -0
  34. package/dist/modules/accounts/utils/is-billing-period.mjs.map +1 -0
  35. package/dist/modules/accounts/utils/parse-account-hub-link.mjs +21 -2
  36. package/dist/modules/accounts/utils/parse-account-hub-link.mjs.map +1 -1
  37. package/dist/store/useAccountUnsavedChanges.mjs +31 -0
  38. package/dist/store/useAccountUnsavedChanges.mjs.map +1 -0
  39. package/package.json +1 -1
  40. package/src/components/account/AccountHubLinkHandler.tsx +15 -3
  41. package/src/components/account/AccountUnsavedChangesDialog.tsx +49 -0
  42. package/src/components/account/MyAccountModal.tsx +41 -34
  43. package/src/components/account/hooks/useAccountHubActions.ts +17 -1
  44. package/src/components/account/sections/MyProfileSection.tsx +24 -20
  45. package/src/components/account/sections/PreferencesSection.tsx +17 -21
  46. package/src/components/account/sections/subscription/PaymentConfirmationDialog.tsx +3 -2
  47. package/src/components/ui/data-display/SuccessConfettiIcon.tsx +33 -0
  48. package/src/enums/AccountHubFlow.ts +1 -0
  49. package/src/i18n/messages/en-us.ts +5 -0
  50. package/src/i18n/messages/es-es.ts +5 -0
  51. package/src/i18n/messages/pt-br.ts +5 -0
  52. package/src/index.ts +5 -1
  53. package/src/middlewares/create-auth-middleware.ts +1 -1
  54. package/src/modules/accounts/constants/account-hub-link.constants.ts +4 -0
  55. package/src/modules/accounts/types/account-hub-link.type.ts +9 -0
  56. package/src/modules/accounts/utils/build-account-hub-url.ts +14 -0
  57. package/src/modules/accounts/utils/is-billing-period.ts +7 -0
  58. package/src/modules/accounts/utils/parse-account-hub-link.ts +25 -2
  59. package/src/store/useAccountUnsavedChanges.ts +52 -0
@@ -3,9 +3,13 @@ import { useEffect, useRef } from "react";
3
3
  import { usePathname, useRouter, useSearchParams } from "next/navigation";
4
4
  import useAccountHubActions from "./hooks/useAccountHubActions";
5
5
  import {
6
+ ACCOUNT_HUB_CHARGE_PARAM,
6
7
  ACCOUNT_HUB_COUPON_PARAM,
7
8
  ACCOUNT_HUB_FLOW_PARAM,
8
- ACCOUNT_HUB_SECTION_PARAM
9
+ ACCOUNT_HUB_PERIOD_PARAM,
10
+ ACCOUNT_HUB_PLAN_PARAM,
11
+ ACCOUNT_HUB_SECTION_PARAM,
12
+ ACCOUNT_HUB_TOTAL_PARAM
9
13
  } from "../../modules/accounts/constants/account-hub-link.constants";
10
14
  import { parseAccountHubLink } from "../../modules/accounts/utils/parse-account-hub-link";
11
15
  function AccountHubLinkHandler() {
@@ -22,9 +26,17 @@ function AccountHubLinkHandler() {
22
26
  if (!link) return;
23
27
  openLink(link);
24
28
  const remaining = new URLSearchParams(query);
25
- remaining.delete(ACCOUNT_HUB_SECTION_PARAM);
26
- remaining.delete(ACCOUNT_HUB_FLOW_PARAM);
27
- remaining.delete(ACCOUNT_HUB_COUPON_PARAM);
29
+ for (const param of [
30
+ ACCOUNT_HUB_SECTION_PARAM,
31
+ ACCOUNT_HUB_FLOW_PARAM,
32
+ ACCOUNT_HUB_COUPON_PARAM,
33
+ ACCOUNT_HUB_PLAN_PARAM,
34
+ ACCOUNT_HUB_TOTAL_PARAM,
35
+ ACCOUNT_HUB_PERIOD_PARAM,
36
+ ACCOUNT_HUB_CHARGE_PARAM
37
+ ]) {
38
+ remaining.delete(param);
39
+ }
28
40
  const remainingQuery = remaining.toString();
29
41
  router.replace(remainingQuery ? `${pathname}?${remainingQuery}` : pathname, { scroll: false });
30
42
  }, [query, pathname, router, openLink]);
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/account/AccountHubLinkHandler.tsx"],"sourcesContent":["'use client';\n\nimport { useEffect, useRef } from 'react';\nimport { usePathname, useRouter, useSearchParams } from 'next/navigation';\nimport useAccountHubActions from './hooks/useAccountHubActions';\nimport {\n ACCOUNT_HUB_COUPON_PARAM,\n ACCOUNT_HUB_FLOW_PARAM,\n ACCOUNT_HUB_SECTION_PARAM,\n} from '../../modules/accounts/constants/account-hub-link.constants';\nimport { parseAccountHubLink } from '../../modules/accounts/utils/parse-account-hub-link';\n\nexport function AccountHubLinkHandler() {\n const searchParams = useSearchParams();\n const pathname = usePathname();\n const router = useRouter();\n const { openLink } = useAccountHubActions();\n const handledQueryRef = useRef<string | null>(null);\n const query = searchParams.toString();\n\n useEffect(() => {\n if (handledQueryRef.current === query) return;\n handledQueryRef.current = query;\n\n const link = parseAccountHubLink(new URLSearchParams(query));\n if (!link) return;\n\n openLink(link);\n\n const remaining = new URLSearchParams(query);\n remaining.delete(ACCOUNT_HUB_SECTION_PARAM);\n remaining.delete(ACCOUNT_HUB_FLOW_PARAM);\n remaining.delete(ACCOUNT_HUB_COUPON_PARAM);\n const remainingQuery = remaining.toString();\n router.replace(remainingQuery ? `${pathname}?${remainingQuery}` : pathname, { scroll: false });\n }, [query, pathname, router, openLink]);\n\n return null;\n}\n"],"mappings":";AAEA,SAAS,WAAW,cAAc;AAClC,SAAS,aAAa,WAAW,uBAAuB;AACxD,OAAO,0BAA0B;AACjC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,2BAA2B;AAE7B,SAAS,wBAAwB;AACtC,QAAM,eAAe,gBAAgB;AACrC,QAAM,WAAW,YAAY;AAC7B,QAAM,SAAS,UAAU;AACzB,QAAM,EAAE,SAAS,IAAI,qBAAqB;AAC1C,QAAM,kBAAkB,OAAsB,IAAI;AAClD,QAAM,QAAQ,aAAa,SAAS;AAEpC,YAAU,MAAM;AACd,QAAI,gBAAgB,YAAY,MAAO;AACvC,oBAAgB,UAAU;AAE1B,UAAM,OAAO,oBAAoB,IAAI,gBAAgB,KAAK,CAAC;AAC3D,QAAI,CAAC,KAAM;AAEX,aAAS,IAAI;AAEb,UAAM,YAAY,IAAI,gBAAgB,KAAK;AAC3C,cAAU,OAAO,yBAAyB;AAC1C,cAAU,OAAO,sBAAsB;AACvC,cAAU,OAAO,wBAAwB;AACzC,UAAM,iBAAiB,UAAU,SAAS;AAC1C,WAAO,QAAQ,iBAAiB,GAAG,QAAQ,IAAI,cAAc,KAAK,UAAU,EAAE,QAAQ,MAAM,CAAC;AAAA,EAC/F,GAAG,CAAC,OAAO,UAAU,QAAQ,QAAQ,CAAC;AAEtC,SAAO;AACT;","names":[]}
1
+ {"version":3,"sources":["../../../src/components/account/AccountHubLinkHandler.tsx"],"sourcesContent":["'use client';\n\nimport { useEffect, useRef } from 'react';\nimport { usePathname, useRouter, useSearchParams } from 'next/navigation';\nimport useAccountHubActions from './hooks/useAccountHubActions';\nimport {\n ACCOUNT_HUB_CHARGE_PARAM,\n ACCOUNT_HUB_COUPON_PARAM,\n ACCOUNT_HUB_FLOW_PARAM,\n ACCOUNT_HUB_PERIOD_PARAM,\n ACCOUNT_HUB_PLAN_PARAM,\n ACCOUNT_HUB_SECTION_PARAM,\n ACCOUNT_HUB_TOTAL_PARAM,\n} from '../../modules/accounts/constants/account-hub-link.constants';\nimport { parseAccountHubLink } from '../../modules/accounts/utils/parse-account-hub-link';\n\nexport function AccountHubLinkHandler() {\n const searchParams = useSearchParams();\n const pathname = usePathname();\n const router = useRouter();\n const { openLink } = useAccountHubActions();\n const handledQueryRef = useRef<string | null>(null);\n const query = searchParams.toString();\n\n useEffect(() => {\n if (handledQueryRef.current === query) return;\n handledQueryRef.current = query;\n\n const link = parseAccountHubLink(new URLSearchParams(query));\n if (!link) return;\n\n openLink(link);\n\n const remaining = new URLSearchParams(query);\n for (const param of [\n ACCOUNT_HUB_SECTION_PARAM,\n ACCOUNT_HUB_FLOW_PARAM,\n ACCOUNT_HUB_COUPON_PARAM,\n ACCOUNT_HUB_PLAN_PARAM,\n ACCOUNT_HUB_TOTAL_PARAM,\n ACCOUNT_HUB_PERIOD_PARAM,\n ACCOUNT_HUB_CHARGE_PARAM,\n ]) {\n remaining.delete(param);\n }\n const remainingQuery = remaining.toString();\n router.replace(remainingQuery ? `${pathname}?${remainingQuery}` : pathname, { scroll: false });\n }, [query, pathname, router, openLink]);\n\n return null;\n}\n"],"mappings":";AAEA,SAAS,WAAW,cAAc;AAClC,SAAS,aAAa,WAAW,uBAAuB;AACxD,OAAO,0BAA0B;AACjC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,2BAA2B;AAE7B,SAAS,wBAAwB;AACtC,QAAM,eAAe,gBAAgB;AACrC,QAAM,WAAW,YAAY;AAC7B,QAAM,SAAS,UAAU;AACzB,QAAM,EAAE,SAAS,IAAI,qBAAqB;AAC1C,QAAM,kBAAkB,OAAsB,IAAI;AAClD,QAAM,QAAQ,aAAa,SAAS;AAEpC,YAAU,MAAM;AACd,QAAI,gBAAgB,YAAY,MAAO;AACvC,oBAAgB,UAAU;AAE1B,UAAM,OAAO,oBAAoB,IAAI,gBAAgB,KAAK,CAAC;AAC3D,QAAI,CAAC,KAAM;AAEX,aAAS,IAAI;AAEb,UAAM,YAAY,IAAI,gBAAgB,KAAK;AAC3C,eAAW,SAAS;AAAA,MAClB;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF,GAAG;AACD,gBAAU,OAAO,KAAK;AAAA,IACxB;AACA,UAAM,iBAAiB,UAAU,SAAS;AAC1C,WAAO,QAAQ,iBAAiB,GAAG,QAAQ,IAAI,cAAc,KAAK,UAAU,EAAE,QAAQ,MAAM,CAAC;AAAA,EAC/F,GAAG,CAAC,OAAO,UAAU,QAAQ,QAAQ,CAAC;AAEtC,SAAO;AACT;","names":[]}
@@ -0,0 +1,34 @@
1
+ "use client";
2
+ import { jsx, jsxs } from "react/jsx-runtime";
3
+ import { useTranslations } from "next-intl";
4
+ import { IconAlertTriangle } from "@tabler/icons-react";
5
+ import { Button } from "../ui/buttons/Button";
6
+ import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "../ui/overlay/Dialog";
7
+ import { Separator } from "../ui/data-display/Separator";
8
+ import { useAccountUnsavedChanges } from "../../store/useAccountUnsavedChanges";
9
+ function AccountUnsavedChangesDialog() {
10
+ const translate = useTranslations();
11
+ const { pendingAction, save, discard, dismiss } = useAccountUnsavedChanges();
12
+ return /* @__PURE__ */ jsx(Dialog, { open: pendingAction !== null, onOpenChange: (nextOpen) => !nextOpen && dismiss(), children: /* @__PURE__ */ jsxs(DialogContent, { className: "flex flex-col p-0 gap-0 md:w-[386px]! lg:w-[386px]! max-w-[386px]! max-[386px]:w-[calc(100%-32px)]", children: [
13
+ /* @__PURE__ */ jsxs(DialogHeader, { className: "sr-only", children: [
14
+ /* @__PURE__ */ jsx(DialogTitle, { children: translate("common.account.unsavedChanges.title") }),
15
+ /* @__PURE__ */ jsx(DialogDescription, { children: translate("common.account.unsavedChanges.description") })
16
+ ] }),
17
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-3 p-5", children: [
18
+ /* @__PURE__ */ jsx("div", { className: "flex items-center justify-center bg-gray-50 rounded-lg size-10", children: /* @__PURE__ */ jsx(IconAlertTriangle, { size: 24, className: "text-gray-950" }) }),
19
+ /* @__PURE__ */ jsxs("div", { className: "flex flex-col gap-2", children: [
20
+ /* @__PURE__ */ jsx("span", { className: "paragraph-medium-semibold text-gray-950", children: translate("common.account.unsavedChanges.title") }),
21
+ /* @__PURE__ */ jsx("span", { className: "paragraph-small-regular text-gray-600", children: translate("common.account.unsavedChanges.description") })
22
+ ] })
23
+ ] }),
24
+ /* @__PURE__ */ jsx(Separator, { className: "w-full" }),
25
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 p-5", children: [
26
+ /* @__PURE__ */ jsx(Button, { onClick: save, className: "h-10!", children: translate("common.actions.save") }),
27
+ /* @__PURE__ */ jsx(Button, { variant: "secondary", onClick: discard, className: "h-10!", children: translate("common.account.unsavedChanges.discard") })
28
+ ] })
29
+ ] }) });
30
+ }
31
+ export {
32
+ AccountUnsavedChangesDialog as default
33
+ };
34
+ //# sourceMappingURL=AccountUnsavedChangesDialog.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/components/account/AccountUnsavedChangesDialog.tsx"],"sourcesContent":["'use client';\n\nimport { useTranslations } from 'next-intl';\nimport { IconAlertTriangle } from '@tabler/icons-react';\nimport { Button } from '../ui/buttons/Button';\nimport { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '../ui/overlay/Dialog';\nimport { Separator } from '../ui/data-display/Separator';\nimport { useAccountUnsavedChanges } from '../../store/useAccountUnsavedChanges';\n\nexport default function AccountUnsavedChangesDialog() {\n const translate = useTranslations();\n const { pendingAction, save, discard, dismiss } = useAccountUnsavedChanges();\n\n return (\n <Dialog open={pendingAction !== null} onOpenChange={(nextOpen) => !nextOpen && dismiss()}>\n <DialogContent className=\"flex flex-col p-0 gap-0 md:w-[386px]! lg:w-[386px]! max-w-[386px]! max-[386px]:w-[calc(100%-32px)]\">\n <DialogHeader className=\"sr-only\">\n <DialogTitle>{translate('common.account.unsavedChanges.title')}</DialogTitle>\n <DialogDescription>{translate('common.account.unsavedChanges.description')}</DialogDescription>\n </DialogHeader>\n\n <div className=\"flex flex-col gap-3 p-5\">\n <div className=\"flex items-center justify-center bg-gray-50 rounded-lg size-10\">\n <IconAlertTriangle size={24} className=\"text-gray-950\" />\n </div>\n <div className=\"flex flex-col gap-2\">\n <span className=\"paragraph-medium-semibold text-gray-950\">\n {translate('common.account.unsavedChanges.title')}\n </span>\n <span className=\"paragraph-small-regular text-gray-600\">\n {translate('common.account.unsavedChanges.description')}\n </span>\n </div>\n </div>\n\n <Separator className=\"w-full\" />\n\n <div className=\"flex items-center gap-2 p-5\">\n <Button onClick={save} className=\"h-10!\">\n {translate('common.actions.save')}\n </Button>\n <Button variant=\"secondary\" onClick={discard} className=\"h-10!\">\n {translate('common.account.unsavedChanges.discard')}\n </Button>\n </div>\n </DialogContent>\n </Dialog>\n );\n}\n"],"mappings":";AAgBQ,SACE,KADF;AAdR,SAAS,uBAAuB;AAChC,SAAS,yBAAyB;AAClC,SAAS,cAAc;AACvB,SAAS,QAAQ,eAAe,mBAAmB,cAAc,mBAAmB;AACpF,SAAS,iBAAiB;AAC1B,SAAS,gCAAgC;AAE1B,SAAR,8BAA+C;AACpD,QAAM,YAAY,gBAAgB;AAClC,QAAM,EAAE,eAAe,MAAM,SAAS,QAAQ,IAAI,yBAAyB;AAE3E,SACE,oBAAC,UAAO,MAAM,kBAAkB,MAAM,cAAc,CAAC,aAAa,CAAC,YAAY,QAAQ,GACrF,+BAAC,iBAAc,WAAU,sGACvB;AAAA,yBAAC,gBAAa,WAAU,WACtB;AAAA,0BAAC,eAAa,oBAAU,qCAAqC,GAAE;AAAA,MAC/D,oBAAC,qBAAmB,oBAAU,2CAA2C,GAAE;AAAA,OAC7E;AAAA,IAEA,qBAAC,SAAI,WAAU,2BACb;AAAA,0BAAC,SAAI,WAAU,kEACb,8BAAC,qBAAkB,MAAM,IAAI,WAAU,iBAAgB,GACzD;AAAA,MACA,qBAAC,SAAI,WAAU,uBACb;AAAA,4BAAC,UAAK,WAAU,2CACb,oBAAU,qCAAqC,GAClD;AAAA,QACA,oBAAC,UAAK,WAAU,yCACb,oBAAU,2CAA2C,GACxD;AAAA,SACF;AAAA,OACF;AAAA,IAEA,oBAAC,aAAU,WAAU,UAAS;AAAA,IAE9B,qBAAC,SAAI,WAAU,+BACb;AAAA,0BAAC,UAAO,SAAS,MAAM,WAAU,SAC9B,oBAAU,qBAAqB,GAClC;AAAA,MACA,oBAAC,UAAO,SAAQ,aAAY,SAAS,SAAS,WAAU,SACrD,oBAAU,uCAAuC,GACpD;AAAA,OACF;AAAA,KACF,GACF;AAEJ;","names":[]}
@@ -27,9 +27,11 @@ import { ProjectsSection } from "./sections/ProjectsSection";
27
27
  import { TeamSection } from "./sections/TeamSection";
28
28
  import { AffiliatesSection } from "./sections/AffiliatesSection";
29
29
  import useMyAccountModal from "./hooks/useMyAccountModal";
30
+ import AccountUnsavedChangesDialog from "./AccountUnsavedChangesDialog";
30
31
  import { AccountSectionType } from "../../enums/AccountSectionType";
31
32
  import { WorkspaceSectionType } from "../../enums/WorkspaceSectionType";
32
33
  import { useAccountModals } from "../../store/useAccountModals";
34
+ import { useAccountUnsavedChanges } from "../../store/useAccountUnsavedChanges";
33
35
  import { useAuth } from "../../providers/auth.provider";
34
36
  import { useManagementPermissions } from "../../modules/auth/hooks/useManagementPermissions";
35
37
  import { useCurrentSubscription } from "../../modules/subscriptions/hooks/use-current-subscription.hook";
@@ -43,6 +45,7 @@ function MyAccountModal() {
43
45
  const { isExternalContracting } = useExternalContracting();
44
46
  const { whitelabel } = useWhitelabel();
45
47
  const { data: { data: [subscription] = [] } = {} } = useCurrentSubscription();
48
+ const guardUnsavedChanges = useAccountUnsavedChanges((state) => state.guard);
46
49
  const open = activeModal === "account";
47
50
  const isBlockingSubscription = !!subscription && subscription.type !== "free" && subscription.type !== "trial" && subscription.active === true && !subscription.date_cancellation && !hasSubscriptionExpired(subscription.date_due);
48
51
  const handleDeleteAccount = () => {
@@ -59,14 +62,14 @@ function MyAccountModal() {
59
62
  icon: IconUser,
60
63
  label: translate("common.account.profile.title"),
61
64
  visible: true,
62
- content: /* @__PURE__ */ jsx(MyProfileSection, { onClose: close })
65
+ content: /* @__PURE__ */ jsx(MyProfileSection, {})
63
66
  },
64
67
  {
65
68
  value: AccountSectionType.PREFERENCES,
66
69
  icon: IconSettings2,
67
70
  label: translate("common.preferences.title"),
68
71
  visible: true,
69
- content: /* @__PURE__ */ jsx(PreferencesSection, { onClose: close })
72
+ content: /* @__PURE__ */ jsx(PreferencesSection, {})
70
73
  },
71
74
  {
72
75
  value: AccountSectionType.TOKEN,
@@ -140,26 +143,29 @@ function MyAccountModal() {
140
143
  });
141
144
  const sectionTitle = [...visibleSections, ...changePasswordSection].find((section) => section.value === activeSection)?.label ?? translate("common.account.workspace.title");
142
145
  const renderNavItems = (sections) => sections.map((section) => /* @__PURE__ */ jsx(AccountModalNavItem, { value: section.value, icon: section.icon, label: section.label }, section.value));
143
- return /* @__PURE__ */ jsxs(
144
- AccountModalLayout,
145
- {
146
- open,
147
- onClose: close,
148
- title: translate("common.account.workspace.title"),
149
- sectionTitle,
150
- activeSection: activeSection ?? "",
151
- onSectionChange: setActiveSection,
152
- isLoading: isProfileLoading,
153
- navigation: /* @__PURE__ */ jsxs(Fragment, { children: [
154
- visibleWorkspaceSections.length > 0 && /* @__PURE__ */ jsx(AccountModalNavGroup, { label: translate("common.account.workspace.groups.workspace"), children: renderNavItems(visibleWorkspaceSections) }),
155
- /* @__PURE__ */ jsx(AccountModalNavGroup, { label: translate("common.account.workspace.groups.account"), children: renderNavItems(visibleAccountSections) })
156
- ] }),
157
- children: [
158
- visibleSections.map((section) => /* @__PURE__ */ jsx(AccountModalSection, { value: section.value, children: section.content }, section.value)),
159
- changePasswordSection.map((section) => /* @__PURE__ */ jsx(AccountModalSection, { value: section.value, children: section.content }, section.value))
160
- ]
161
- }
162
- );
146
+ return /* @__PURE__ */ jsxs(Fragment, { children: [
147
+ /* @__PURE__ */ jsxs(
148
+ AccountModalLayout,
149
+ {
150
+ open,
151
+ onClose: () => guardUnsavedChanges(close),
152
+ title: translate("common.account.workspace.title"),
153
+ sectionTitle,
154
+ activeSection: activeSection ?? "",
155
+ onSectionChange: (section) => guardUnsavedChanges(() => setActiveSection(section)),
156
+ isLoading: isProfileLoading,
157
+ navigation: /* @__PURE__ */ jsxs(Fragment, { children: [
158
+ visibleWorkspaceSections.length > 0 && /* @__PURE__ */ jsx(AccountModalNavGroup, { label: translate("common.account.workspace.groups.workspace"), children: renderNavItems(visibleWorkspaceSections) }),
159
+ /* @__PURE__ */ jsx(AccountModalNavGroup, { label: translate("common.account.workspace.groups.account"), children: renderNavItems(visibleAccountSections) })
160
+ ] }),
161
+ children: [
162
+ visibleSections.map((section) => /* @__PURE__ */ jsx(AccountModalSection, { value: section.value, children: section.content }, section.value)),
163
+ changePasswordSection.map((section) => /* @__PURE__ */ jsx(AccountModalSection, { value: section.value, children: section.content }, section.value))
164
+ ]
165
+ }
166
+ ),
167
+ /* @__PURE__ */ jsx(AccountUnsavedChangesDialog, {})
168
+ ] });
163
169
  }
164
170
  export {
165
171
  MyAccountModal as default
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/account/MyAccountModal.tsx"],"sourcesContent":["'use client';\n\nimport type { ReactNode } from 'react';\nimport {\n IconCreditCard,\n IconFolder,\n IconKey,\n IconLink,\n IconLock,\n IconSettings2,\n IconUser,\n IconUsers,\n type TablerIcon,\n} from '@tabler/icons-react';\nimport { useTranslations } from 'next-intl';\nimport {\n AccountModalLayout,\n AccountModalNavGroup,\n AccountModalNavItem,\n AccountModalSection,\n} from './AccountModalLayout';\nimport { MyProfileSection } from './sections/MyProfileSection';\nimport { PreferencesSection } from './sections/PreferencesSection';\nimport { TokenSection } from './sections/TokenSection';\nimport { SecuritySection } from './sections/SecuritySection';\nimport { ChangePasswordSection } from './sections/ChangePasswordSection';\nimport { SubscriptionSection } from './sections/SubscriptionSection';\nimport { ProjectsSection } from './sections/ProjectsSection';\nimport { TeamSection } from './sections/TeamSection';\nimport { AffiliatesSection } from './sections/AffiliatesSection';\nimport useMyAccountModal from './hooks/useMyAccountModal';\nimport { AccountSectionType } from '../../enums/AccountSectionType';\nimport { WorkspaceSectionType } from '../../enums/WorkspaceSectionType';\nimport { useAccountModals, type MyAccountSection } from '../../store/useAccountModals';\nimport { useAuth } from '../../providers/auth.provider';\nimport { useManagementPermissions } from '../../modules/auth/hooks/useManagementPermissions';\nimport { useCurrentSubscription } from '../../modules/subscriptions/hooks/use-current-subscription.hook';\nimport { hasSubscriptionExpired } from '../../modules/subscriptions/utils/has-subscription-expired';\nimport { useExternalContracting, useWhitelabel } from '../../providers/whitelabel.provider';\n\ntype MyAccountNavSection = {\n value: MyAccountSection;\n icon: TablerIcon;\n label: string;\n visible: boolean;\n content: ReactNode;\n};\n\n/** Modal Minha conta: assinatura, projetos, equipe e afiliados (workspace), e preferências, token e segurança. */\nexport default function MyAccountModal() {\n const translate = useTranslations();\n const { activeModal, initialSection, openDeleteAccount, openIsntPossibleDelete, close } = useAccountModals();\n const { isOwner, isOwnerOrAdmin } = useManagementPermissions();\n const { isLoading: isProfileLoading } = useAuth();\n const { isExternalContracting } = useExternalContracting();\n const { whitelabel } = useWhitelabel();\n const { data: { data: [subscription] = [] } = {} } = useCurrentSubscription();\n\n const open = activeModal === 'account';\n\n // Assinatura paga vigente impede excluir a conta: o fluxo manda para o aviso em vez da exclusão.\n const isBlockingSubscription =\n !!subscription &&\n subscription.type !== 'free' &&\n subscription.type !== 'trial' &&\n subscription.active === true &&\n !subscription.date_cancellation &&\n !hasSubscriptionExpired(subscription.date_due);\n\n const handleDeleteAccount = () => {\n close();\n if (isBlockingSubscription) {\n openIsntPossibleDelete();\n } else {\n openDeleteAccount();\n }\n };\n\n const accountSections: MyAccountNavSection[] = [\n {\n value: AccountSectionType.MY_PROFILE,\n icon: IconUser,\n label: translate('common.account.profile.title'),\n visible: true,\n content: <MyProfileSection onClose={close} />,\n },\n {\n value: AccountSectionType.PREFERENCES,\n icon: IconSettings2,\n label: translate('common.preferences.title'),\n visible: true,\n content: <PreferencesSection onClose={close} />,\n },\n {\n value: AccountSectionType.TOKEN,\n icon: IconKey,\n label: translate('common.account.token.title'),\n visible: isOwner,\n content: <TokenSection />,\n },\n {\n value: AccountSectionType.SECURITY,\n icon: IconLock,\n label: translate('common.account.security.title'),\n visible: isOwner,\n content: (\n <SecuritySection\n setActiveSection={(section) => setActiveSection(section)}\n onClose={close}\n onDeleteAccount={handleDeleteAccount}\n />\n ),\n },\n ];\n\n const workspaceSections: MyAccountNavSection[] = [\n {\n value: WorkspaceSectionType.SUBSCRIPTION,\n icon: IconCreditCard,\n label: translate('common.account.workspace.nav.subscription'),\n visible: isOwnerOrAdmin && !isExternalContracting,\n content: <SubscriptionSection />,\n },\n {\n value: WorkspaceSectionType.PROJECTS,\n icon: IconFolder,\n label: translate('common.account.workspace.nav.projects'),\n visible: true,\n content: <ProjectsSection />,\n },\n {\n value: WorkspaceSectionType.TEAM,\n icon: IconUsers,\n label: translate('common.account.workspace.nav.team'),\n visible: true,\n content: <TeamSection />,\n },\n {\n value: WorkspaceSectionType.AFFILIATES,\n icon: IconLink,\n label: translate('common.account.workspace.nav.affiliates'),\n visible: whitelabel?.id === 1 && isOwnerOrAdmin,\n content: <AffiliatesSection />,\n },\n ];\n\n const visibleAccountSections = accountSections.filter((section) => section.visible);\n const visibleWorkspaceSections = workspaceSections.filter((section) => section.visible);\n const visibleSections = [...visibleWorkspaceSections, ...visibleAccountSections];\n\n // Troca de senha não tem item no menu: abre a partir de Segurança.\n const changePasswordSection: MyAccountNavSection[] = isOwner\n ? [\n {\n value: AccountSectionType.CHANGE_PASSWORD,\n icon: IconLock,\n label: translate('common.account.changePassword.title'),\n visible: false,\n content: <ChangePasswordSection onBack={() => setActiveSection(AccountSectionType.SECURITY)} />,\n },\n ]\n : [];\n\n const { activeSection, setActiveSection } = useMyAccountModal({\n isOpen: open,\n initialSection,\n availableSections: [...visibleSections, ...changePasswordSection].map((section) => section.value),\n isReady: !isProfileLoading,\n });\n\n const sectionTitle =\n [...visibleSections, ...changePasswordSection].find((section) => section.value === activeSection)?.label ??\n translate('common.account.workspace.title');\n\n const renderNavItems = (sections: MyAccountNavSection[]) =>\n sections.map((section) => (\n <AccountModalNavItem key={section.value} value={section.value} icon={section.icon} label={section.label} />\n ));\n\n return (\n <AccountModalLayout\n open={open}\n onClose={close}\n title={translate('common.account.workspace.title')}\n sectionTitle={sectionTitle}\n activeSection={activeSection ?? ''}\n onSectionChange={setActiveSection}\n isLoading={isProfileLoading}\n navigation={\n <>\n {visibleWorkspaceSections.length > 0 && (\n <AccountModalNavGroup label={translate('common.account.workspace.groups.workspace')}>\n {renderNavItems(visibleWorkspaceSections)}\n </AccountModalNavGroup>\n )}\n <AccountModalNavGroup label={translate('common.account.workspace.groups.account')}>\n {renderNavItems(visibleAccountSections)}\n </AccountModalNavGroup>\n </>\n }\n >\n {visibleSections.map((section) => (\n <AccountModalSection key={section.value} value={section.value}>\n {section.content}\n </AccountModalSection>\n ))}\n\n {changePasswordSection.map((section) => (\n <AccountModalSection key={section.value} value={section.value}>\n {section.content}\n </AccountModalSection>\n ))}\n </AccountModalLayout>\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../src/components/account/MyAccountModal.tsx"],"sourcesContent":["'use client';\n\nimport type { ReactNode } from 'react';\nimport {\n IconCreditCard,\n IconFolder,\n IconKey,\n IconLink,\n IconLock,\n IconSettings2,\n IconUser,\n IconUsers,\n type TablerIcon,\n} from '@tabler/icons-react';\nimport { useTranslations } from 'next-intl';\nimport {\n AccountModalLayout,\n AccountModalNavGroup,\n AccountModalNavItem,\n AccountModalSection,\n} from './AccountModalLayout';\nimport { MyProfileSection } from './sections/MyProfileSection';\nimport { PreferencesSection } from './sections/PreferencesSection';\nimport { TokenSection } from './sections/TokenSection';\nimport { SecuritySection } from './sections/SecuritySection';\nimport { ChangePasswordSection } from './sections/ChangePasswordSection';\nimport { SubscriptionSection } from './sections/SubscriptionSection';\nimport { ProjectsSection } from './sections/ProjectsSection';\nimport { TeamSection } from './sections/TeamSection';\nimport { AffiliatesSection } from './sections/AffiliatesSection';\nimport useMyAccountModal from './hooks/useMyAccountModal';\nimport AccountUnsavedChangesDialog from './AccountUnsavedChangesDialog';\nimport { AccountSectionType } from '../../enums/AccountSectionType';\nimport { WorkspaceSectionType } from '../../enums/WorkspaceSectionType';\nimport { useAccountModals, type MyAccountSection } from '../../store/useAccountModals';\nimport { useAccountUnsavedChanges } from '../../store/useAccountUnsavedChanges';\nimport { useAuth } from '../../providers/auth.provider';\nimport { useManagementPermissions } from '../../modules/auth/hooks/useManagementPermissions';\nimport { useCurrentSubscription } from '../../modules/subscriptions/hooks/use-current-subscription.hook';\nimport { hasSubscriptionExpired } from '../../modules/subscriptions/utils/has-subscription-expired';\nimport { useExternalContracting, useWhitelabel } from '../../providers/whitelabel.provider';\n\ntype MyAccountNavSection = {\n value: MyAccountSection;\n icon: TablerIcon;\n label: string;\n visible: boolean;\n content: ReactNode;\n};\n\n/** Modal Minha conta: assinatura, projetos, equipe e afiliados (workspace), e preferências, token e segurança. */\nexport default function MyAccountModal() {\n const translate = useTranslations();\n const { activeModal, initialSection, openDeleteAccount, openIsntPossibleDelete, close } = useAccountModals();\n const { isOwner, isOwnerOrAdmin } = useManagementPermissions();\n const { isLoading: isProfileLoading } = useAuth();\n const { isExternalContracting } = useExternalContracting();\n const { whitelabel } = useWhitelabel();\n const { data: { data: [subscription] = [] } = {} } = useCurrentSubscription();\n const guardUnsavedChanges = useAccountUnsavedChanges((state) => state.guard);\n\n const open = activeModal === 'account';\n\n // Assinatura paga vigente impede excluir a conta: o fluxo manda para o aviso em vez da exclusão.\n const isBlockingSubscription =\n !!subscription &&\n subscription.type !== 'free' &&\n subscription.type !== 'trial' &&\n subscription.active === true &&\n !subscription.date_cancellation &&\n !hasSubscriptionExpired(subscription.date_due);\n\n const handleDeleteAccount = () => {\n close();\n if (isBlockingSubscription) {\n openIsntPossibleDelete();\n } else {\n openDeleteAccount();\n }\n };\n\n const accountSections: MyAccountNavSection[] = [\n {\n value: AccountSectionType.MY_PROFILE,\n icon: IconUser,\n label: translate('common.account.profile.title'),\n visible: true,\n content: <MyProfileSection />,\n },\n {\n value: AccountSectionType.PREFERENCES,\n icon: IconSettings2,\n label: translate('common.preferences.title'),\n visible: true,\n content: <PreferencesSection />,\n },\n {\n value: AccountSectionType.TOKEN,\n icon: IconKey,\n label: translate('common.account.token.title'),\n visible: isOwner,\n content: <TokenSection />,\n },\n {\n value: AccountSectionType.SECURITY,\n icon: IconLock,\n label: translate('common.account.security.title'),\n visible: isOwner,\n content: (\n <SecuritySection\n setActiveSection={(section) => setActiveSection(section)}\n onClose={close}\n onDeleteAccount={handleDeleteAccount}\n />\n ),\n },\n ];\n\n const workspaceSections: MyAccountNavSection[] = [\n {\n value: WorkspaceSectionType.SUBSCRIPTION,\n icon: IconCreditCard,\n label: translate('common.account.workspace.nav.subscription'),\n visible: isOwnerOrAdmin && !isExternalContracting,\n content: <SubscriptionSection />,\n },\n {\n value: WorkspaceSectionType.PROJECTS,\n icon: IconFolder,\n label: translate('common.account.workspace.nav.projects'),\n visible: true,\n content: <ProjectsSection />,\n },\n {\n value: WorkspaceSectionType.TEAM,\n icon: IconUsers,\n label: translate('common.account.workspace.nav.team'),\n visible: true,\n content: <TeamSection />,\n },\n {\n value: WorkspaceSectionType.AFFILIATES,\n icon: IconLink,\n label: translate('common.account.workspace.nav.affiliates'),\n visible: whitelabel?.id === 1 && isOwnerOrAdmin,\n content: <AffiliatesSection />,\n },\n ];\n\n const visibleAccountSections = accountSections.filter((section) => section.visible);\n const visibleWorkspaceSections = workspaceSections.filter((section) => section.visible);\n const visibleSections = [...visibleWorkspaceSections, ...visibleAccountSections];\n\n // Troca de senha não tem item no menu: abre a partir de Segurança.\n const changePasswordSection: MyAccountNavSection[] = isOwner\n ? [\n {\n value: AccountSectionType.CHANGE_PASSWORD,\n icon: IconLock,\n label: translate('common.account.changePassword.title'),\n visible: false,\n content: <ChangePasswordSection onBack={() => setActiveSection(AccountSectionType.SECURITY)} />,\n },\n ]\n : [];\n\n const { activeSection, setActiveSection } = useMyAccountModal({\n isOpen: open,\n initialSection,\n availableSections: [...visibleSections, ...changePasswordSection].map((section) => section.value),\n isReady: !isProfileLoading,\n });\n\n const sectionTitle =\n [...visibleSections, ...changePasswordSection].find((section) => section.value === activeSection)?.label ??\n translate('common.account.workspace.title');\n\n const renderNavItems = (sections: MyAccountNavSection[]) =>\n sections.map((section) => (\n <AccountModalNavItem key={section.value} value={section.value} icon={section.icon} label={section.label} />\n ));\n\n return (\n <>\n <AccountModalLayout\n open={open}\n onClose={() => guardUnsavedChanges(close)}\n title={translate('common.account.workspace.title')}\n sectionTitle={sectionTitle}\n activeSection={activeSection ?? ''}\n onSectionChange={(section) => guardUnsavedChanges(() => setActiveSection(section))}\n isLoading={isProfileLoading}\n navigation={\n <>\n {visibleWorkspaceSections.length > 0 && (\n <AccountModalNavGroup label={translate('common.account.workspace.groups.workspace')}>\n {renderNavItems(visibleWorkspaceSections)}\n </AccountModalNavGroup>\n )}\n <AccountModalNavGroup label={translate('common.account.workspace.groups.account')}>\n {renderNavItems(visibleAccountSections)}\n </AccountModalNavGroup>\n </>\n }\n >\n {visibleSections.map((section) => (\n <AccountModalSection key={section.value} value={section.value}>\n {section.content}\n </AccountModalSection>\n ))}\n\n {changePasswordSection.map((section) => (\n <AccountModalSection key={section.value} value={section.value}>\n {section.content}\n </AccountModalSection>\n ))}\n </AccountModalLayout>\n\n <AccountUnsavedChangesDialog />\n </>\n );\n}\n"],"mappings":";AAuFe,SA0GL,UA1GK,KA0GL,YA1GK;AApFf;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OAEK;AACP,SAAS,uBAAuB;AAChC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,wBAAwB;AACjC,SAAS,0BAA0B;AACnC,SAAS,oBAAoB;AAC7B,SAAS,uBAAuB;AAChC,SAAS,6BAA6B;AACtC,SAAS,2BAA2B;AACpC,SAAS,uBAAuB;AAChC,SAAS,mBAAmB;AAC5B,SAAS,yBAAyB;AAClC,OAAO,uBAAuB;AAC9B,OAAO,iCAAiC;AACxC,SAAS,0BAA0B;AACnC,SAAS,4BAA4B;AACrC,SAAS,wBAA+C;AACxD,SAAS,gCAAgC;AACzC,SAAS,eAAe;AACxB,SAAS,gCAAgC;AACzC,SAAS,8BAA8B;AACvC,SAAS,8BAA8B;AACvC,SAAS,wBAAwB,qBAAqB;AAWvC,SAAR,iBAAkC;AACvC,QAAM,YAAY,gBAAgB;AAClC,QAAM,EAAE,aAAa,gBAAgB,mBAAmB,wBAAwB,MAAM,IAAI,iBAAiB;AAC3G,QAAM,EAAE,SAAS,eAAe,IAAI,yBAAyB;AAC7D,QAAM,EAAE,WAAW,iBAAiB,IAAI,QAAQ;AAChD,QAAM,EAAE,sBAAsB,IAAI,uBAAuB;AACzD,QAAM,EAAE,WAAW,IAAI,cAAc;AACrC,QAAM,EAAE,MAAM,EAAE,MAAM,CAAC,YAAY,IAAI,CAAC,EAAE,IAAI,CAAC,EAAE,IAAI,uBAAuB;AAC5E,QAAM,sBAAsB,yBAAyB,CAAC,UAAU,MAAM,KAAK;AAE3E,QAAM,OAAO,gBAAgB;AAG7B,QAAM,yBACJ,CAAC,CAAC,gBACF,aAAa,SAAS,UACtB,aAAa,SAAS,WACtB,aAAa,WAAW,QACxB,CAAC,aAAa,qBACd,CAAC,uBAAuB,aAAa,QAAQ;AAE/C,QAAM,sBAAsB,MAAM;AAChC,UAAM;AACN,QAAI,wBAAwB;AAC1B,6BAAuB;AAAA,IACzB,OAAO;AACL,wBAAkB;AAAA,IACpB;AAAA,EACF;AAEA,QAAM,kBAAyC;AAAA,IAC7C;AAAA,MACE,OAAO,mBAAmB;AAAA,MAC1B,MAAM;AAAA,MACN,OAAO,UAAU,8BAA8B;AAAA,MAC/C,SAAS;AAAA,MACT,SAAS,oBAAC,oBAAiB;AAAA,IAC7B;AAAA,IACA;AAAA,MACE,OAAO,mBAAmB;AAAA,MAC1B,MAAM;AAAA,MACN,OAAO,UAAU,0BAA0B;AAAA,MAC3C,SAAS;AAAA,MACT,SAAS,oBAAC,sBAAmB;AAAA,IAC/B;AAAA,IACA;AAAA,MACE,OAAO,mBAAmB;AAAA,MAC1B,MAAM;AAAA,MACN,OAAO,UAAU,4BAA4B;AAAA,MAC7C,SAAS;AAAA,MACT,SAAS,oBAAC,gBAAa;AAAA,IACzB;AAAA,IACA;AAAA,MACE,OAAO,mBAAmB;AAAA,MAC1B,MAAM;AAAA,MACN,OAAO,UAAU,+BAA+B;AAAA,MAChD,SAAS;AAAA,MACT,SACE;AAAA,QAAC;AAAA;AAAA,UACC,kBAAkB,CAAC,YAAY,iBAAiB,OAAO;AAAA,UACvD,SAAS;AAAA,UACT,iBAAiB;AAAA;AAAA,MACnB;AAAA,IAEJ;AAAA,EACF;AAEA,QAAM,oBAA2C;AAAA,IAC/C;AAAA,MACE,OAAO,qBAAqB;AAAA,MAC5B,MAAM;AAAA,MACN,OAAO,UAAU,2CAA2C;AAAA,MAC5D,SAAS,kBAAkB,CAAC;AAAA,MAC5B,SAAS,oBAAC,uBAAoB;AAAA,IAChC;AAAA,IACA;AAAA,MACE,OAAO,qBAAqB;AAAA,MAC5B,MAAM;AAAA,MACN,OAAO,UAAU,uCAAuC;AAAA,MACxD,SAAS;AAAA,MACT,SAAS,oBAAC,mBAAgB;AAAA,IAC5B;AAAA,IACA;AAAA,MACE,OAAO,qBAAqB;AAAA,MAC5B,MAAM;AAAA,MACN,OAAO,UAAU,mCAAmC;AAAA,MACpD,SAAS;AAAA,MACT,SAAS,oBAAC,eAAY;AAAA,IACxB;AAAA,IACA;AAAA,MACE,OAAO,qBAAqB;AAAA,MAC5B,MAAM;AAAA,MACN,OAAO,UAAU,yCAAyC;AAAA,MAC1D,SAAS,YAAY,OAAO,KAAK;AAAA,MACjC,SAAS,oBAAC,qBAAkB;AAAA,IAC9B;AAAA,EACF;AAEA,QAAM,yBAAyB,gBAAgB,OAAO,CAAC,YAAY,QAAQ,OAAO;AAClF,QAAM,2BAA2B,kBAAkB,OAAO,CAAC,YAAY,QAAQ,OAAO;AACtF,QAAM,kBAAkB,CAAC,GAAG,0BAA0B,GAAG,sBAAsB;AAG/E,QAAM,wBAA+C,UACjD;AAAA,IACE;AAAA,MACE,OAAO,mBAAmB;AAAA,MAC1B,MAAM;AAAA,MACN,OAAO,UAAU,qCAAqC;AAAA,MACtD,SAAS;AAAA,MACT,SAAS,oBAAC,yBAAsB,QAAQ,MAAM,iBAAiB,mBAAmB,QAAQ,GAAG;AAAA,IAC/F;AAAA,EACF,IACA,CAAC;AAEL,QAAM,EAAE,eAAe,iBAAiB,IAAI,kBAAkB;AAAA,IAC5D,QAAQ;AAAA,IACR;AAAA,IACA,mBAAmB,CAAC,GAAG,iBAAiB,GAAG,qBAAqB,EAAE,IAAI,CAAC,YAAY,QAAQ,KAAK;AAAA,IAChG,SAAS,CAAC;AAAA,EACZ,CAAC;AAED,QAAM,eACJ,CAAC,GAAG,iBAAiB,GAAG,qBAAqB,EAAE,KAAK,CAAC,YAAY,QAAQ,UAAU,aAAa,GAAG,SACnG,UAAU,gCAAgC;AAE5C,QAAM,iBAAiB,CAAC,aACtB,SAAS,IAAI,CAAC,YACZ,oBAAC,uBAAwC,OAAO,QAAQ,OAAO,MAAM,QAAQ,MAAM,OAAO,QAAQ,SAAxE,QAAQ,KAAuE,CAC1G;AAEH,SACE,iCACE;AAAA;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA,SAAS,MAAM,oBAAoB,KAAK;AAAA,QACxC,OAAO,UAAU,gCAAgC;AAAA,QACjD;AAAA,QACA,eAAe,iBAAiB;AAAA,QAChC,iBAAiB,CAAC,YAAY,oBAAoB,MAAM,iBAAiB,OAAO,CAAC;AAAA,QACjF,WAAW;AAAA,QACX,YACE,iCACG;AAAA,mCAAyB,SAAS,KACjC,oBAAC,wBAAqB,OAAO,UAAU,2CAA2C,GAC/E,yBAAe,wBAAwB,GAC1C;AAAA,UAEF,oBAAC,wBAAqB,OAAO,UAAU,yCAAyC,GAC7E,yBAAe,sBAAsB,GACxC;AAAA,WACF;AAAA,QAGD;AAAA,0BAAgB,IAAI,CAAC,YACpB,oBAAC,uBAAwC,OAAO,QAAQ,OACrD,kBAAQ,WADe,QAAQ,KAElC,CACD;AAAA,UAEA,sBAAsB,IAAI,CAAC,YAC1B,oBAAC,uBAAwC,OAAO,QAAQ,OACrD,kBAAQ,WADe,QAAQ,KAElC,CACD;AAAA;AAAA;AAAA,IACH;AAAA,IAEA,oBAAC,+BAA4B;AAAA,KAC/B;AAEJ;","names":[]}
@@ -1,8 +1,10 @@
1
1
  "use client";
2
2
  import { AccountHubFlow } from "../../../enums/AccountHubFlow";
3
3
  import { WorkspaceSectionType } from "../../../enums/WorkspaceSectionType";
4
+ import { useTranslations } from "next-intl";
4
5
  import { useCouponByCode } from "../../../modules/coupons/hooks/use-coupon-by-code.hook";
5
6
  import { isAccountSectionType } from "../../../modules/accounts/utils/is-account-section";
7
+ import { getBillingPeriodSuffix } from "../../../modules/subscriptions/utils/periodicity";
6
8
  import { useExternalContracting } from "../../../providers/whitelabel.provider";
7
9
  import { useAccountModals } from "../../../store/useAccountModals";
8
10
  import { useProjectFlows } from "../../../store/useProjectFlows";
@@ -16,7 +18,9 @@ function useAccountHubActions() {
16
18
  const requestOverdueReceipt = useSubscriptionFlows((state) => state.requestOverdueReceipt);
17
19
  const openCreateProjectFlow = useProjectFlows((state) => state.openCreateProject);
18
20
  const openAddUserFlow = useTeamFlows((state) => state.openAddUser);
21
+ const openPaymentConfirmationFlow = useSubscriptionFlows((state) => state.openPaymentConfirmation);
19
22
  const { redirectToExternal } = useExternalContracting();
23
+ const translate = useTranslations();
20
24
  const { mutate: findCouponByCode } = useCouponByCode();
21
25
  const openSection = (section) => {
22
26
  if (isAccountSectionType(section)) {
@@ -59,6 +63,15 @@ function useAccountHubActions() {
59
63
  openWorkspace(WorkspaceSectionType.TEAM);
60
64
  openAddUserFlow();
61
65
  };
66
+ const openPaymentConfirmation = (purchase) => {
67
+ openWorkspace(WorkspaceSectionType.SUBSCRIPTION);
68
+ openPaymentConfirmationFlow({
69
+ planName: purchase.planName,
70
+ total: purchase.total,
71
+ periodLabel: getBillingPeriodSuffix(purchase.billingPeriod, translate),
72
+ immediateChargeTotal: purchase.immediateChargeTotal
73
+ });
74
+ };
62
75
  const openLink = (link) => {
63
76
  if (link.flow === AccountHubFlow.PLANS) return openPlansCatalog(link.couponCode);
64
77
  if (link.flow === AccountHubFlow.ADDONS) return openAddons();
@@ -66,6 +79,7 @@ function useAccountHubActions() {
66
79
  if (link.flow === AccountHubFlow.RECEIPT) return openOverdueReceipt();
67
80
  if (link.flow === AccountHubFlow.CREATE_PROJECT) return openCreateProject();
68
81
  if (link.flow === AccountHubFlow.ADD_USER) return openAddUser();
82
+ if (link.flow === AccountHubFlow.CONFIRMATION && link.purchase) return openPaymentConfirmation(link.purchase);
69
83
  openSection(link.section);
70
84
  };
71
85
  return {
@@ -76,6 +90,7 @@ function useAccountHubActions() {
76
90
  openOverdueReceipt,
77
91
  openCreateProject,
78
92
  openAddUser,
93
+ openPaymentConfirmation,
79
94
  openLink
80
95
  };
81
96
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/components/account/hooks/useAccountHubActions.ts"],"sourcesContent":["'use client';\n\nimport { AccountHubFlow } from '../../../enums/AccountHubFlow';\nimport { WorkspaceSectionType } from '../../../enums/WorkspaceSectionType';\nimport { useCouponByCode } from '../../../modules/coupons/hooks/use-coupon-by-code.hook';\nimport { isAccountSectionType } from '../../../modules/accounts/utils/is-account-section';\nimport type { AccountHubLink } from '../../../modules/accounts/types/account-hub-link.type';\nimport { useExternalContracting } from '../../../providers/whitelabel.provider';\nimport { useAccountModals, type MyAccountSection } from '../../../store/useAccountModals';\nimport { useProjectFlows } from '../../../store/useProjectFlows';\nimport { useSubscriptionFlows } from '../../../store/useSubscriptionFlows';\nimport { useTeamFlows } from '../../../store/useTeamFlows';\n\nexport default function useAccountHubActions() {\n const openConfigurations = useAccountModals((state) => state.openConfigurations);\n const openWorkspace = useAccountModals((state) => state.openWorkspace);\n const openChangePlan = useSubscriptionFlows((state) => state.openChangePlan);\n const openPlansCatalogFlow = useSubscriptionFlows((state) => state.openPlansCatalog);\n const requestOverdueReceipt = useSubscriptionFlows((state) => state.requestOverdueReceipt);\n const openCreateProjectFlow = useProjectFlows((state) => state.openCreateProject);\n const openAddUserFlow = useTeamFlows((state) => state.openAddUser);\n const { redirectToExternal } = useExternalContracting();\n const { mutate: findCouponByCode } = useCouponByCode();\n\n const openSection = (section: MyAccountSection) => {\n if (isAccountSectionType(section)) {\n openConfigurations(section);\n return;\n }\n openWorkspace(section);\n };\n\n const openPlansCatalog = (couponCode?: string) => {\n if (redirectToExternal('plans')) return;\n openWorkspace(WorkspaceSectionType.SUBSCRIPTION);\n\n if (!couponCode) {\n openPlansCatalogFlow();\n return;\n }\n\n findCouponByCode(couponCode, {\n onSuccess: (coupon) => openPlansCatalogFlow({ appliedCouponStripeData: coupon?.stripe_data }),\n onError: () => openPlansCatalogFlow(),\n });\n };\n\n const openAddons = () => {\n if (redirectToExternal('contracting')) return;\n openWorkspace(WorkspaceSectionType.SUBSCRIPTION);\n openChangePlan();\n };\n\n const openReactivate = () => {\n if (redirectToExternal('contracting')) return;\n openWorkspace(WorkspaceSectionType.SUBSCRIPTION);\n openChangePlan({ isReactivation: true });\n };\n\n const openOverdueReceipt = () => {\n openWorkspace(WorkspaceSectionType.SUBSCRIPTION);\n requestOverdueReceipt();\n };\n\n const openCreateProject = () => {\n openWorkspace(WorkspaceSectionType.PROJECTS);\n openCreateProjectFlow();\n };\n\n const openAddUser = () => {\n openWorkspace(WorkspaceSectionType.TEAM);\n openAddUserFlow();\n };\n\n const openLink = (link: AccountHubLink) => {\n if (link.flow === AccountHubFlow.PLANS) return openPlansCatalog(link.couponCode);\n if (link.flow === AccountHubFlow.ADDONS) return openAddons();\n if (link.flow === AccountHubFlow.REACTIVATE) return openReactivate();\n if (link.flow === AccountHubFlow.RECEIPT) return openOverdueReceipt();\n if (link.flow === AccountHubFlow.CREATE_PROJECT) return openCreateProject();\n if (link.flow === AccountHubFlow.ADD_USER) return openAddUser();\n openSection(link.section);\n };\n\n return {\n openSection,\n openPlansCatalog,\n openAddons,\n openReactivate,\n openOverdueReceipt,\n openCreateProject,\n openAddUser,\n openLink,\n };\n}\n"],"mappings":";AAEA,SAAS,sBAAsB;AAC/B,SAAS,4BAA4B;AACrC,SAAS,uBAAuB;AAChC,SAAS,4BAA4B;AAErC,SAAS,8BAA8B;AACvC,SAAS,wBAA+C;AACxD,SAAS,uBAAuB;AAChC,SAAS,4BAA4B;AACrC,SAAS,oBAAoB;AAEd,SAAR,uBAAwC;AAC7C,QAAM,qBAAqB,iBAAiB,CAAC,UAAU,MAAM,kBAAkB;AAC/E,QAAM,gBAAgB,iBAAiB,CAAC,UAAU,MAAM,aAAa;AACrE,QAAM,iBAAiB,qBAAqB,CAAC,UAAU,MAAM,cAAc;AAC3E,QAAM,uBAAuB,qBAAqB,CAAC,UAAU,MAAM,gBAAgB;AACnF,QAAM,wBAAwB,qBAAqB,CAAC,UAAU,MAAM,qBAAqB;AACzF,QAAM,wBAAwB,gBAAgB,CAAC,UAAU,MAAM,iBAAiB;AAChF,QAAM,kBAAkB,aAAa,CAAC,UAAU,MAAM,WAAW;AACjE,QAAM,EAAE,mBAAmB,IAAI,uBAAuB;AACtD,QAAM,EAAE,QAAQ,iBAAiB,IAAI,gBAAgB;AAErD,QAAM,cAAc,CAAC,YAA8B;AACjD,QAAI,qBAAqB,OAAO,GAAG;AACjC,yBAAmB,OAAO;AAC1B;AAAA,IACF;AACA,kBAAc,OAAO;AAAA,EACvB;AAEA,QAAM,mBAAmB,CAAC,eAAwB;AAChD,QAAI,mBAAmB,OAAO,EAAG;AACjC,kBAAc,qBAAqB,YAAY;AAE/C,QAAI,CAAC,YAAY;AACf,2BAAqB;AACrB;AAAA,IACF;AAEA,qBAAiB,YAAY;AAAA,MAC3B,WAAW,CAAC,WAAW,qBAAqB,EAAE,yBAAyB,QAAQ,YAAY,CAAC;AAAA,MAC5F,SAAS,MAAM,qBAAqB;AAAA,IACtC,CAAC;AAAA,EACH;AAEA,QAAM,aAAa,MAAM;AACvB,QAAI,mBAAmB,aAAa,EAAG;AACvC,kBAAc,qBAAqB,YAAY;AAC/C,mBAAe;AAAA,EACjB;AAEA,QAAM,iBAAiB,MAAM;AAC3B,QAAI,mBAAmB,aAAa,EAAG;AACvC,kBAAc,qBAAqB,YAAY;AAC/C,mBAAe,EAAE,gBAAgB,KAAK,CAAC;AAAA,EACzC;AAEA,QAAM,qBAAqB,MAAM;AAC/B,kBAAc,qBAAqB,YAAY;AAC/C,0BAAsB;AAAA,EACxB;AAEA,QAAM,oBAAoB,MAAM;AAC9B,kBAAc,qBAAqB,QAAQ;AAC3C,0BAAsB;AAAA,EACxB;AAEA,QAAM,cAAc,MAAM;AACxB,kBAAc,qBAAqB,IAAI;AACvC,oBAAgB;AAAA,EAClB;AAEA,QAAM,WAAW,CAAC,SAAyB;AACzC,QAAI,KAAK,SAAS,eAAe,MAAO,QAAO,iBAAiB,KAAK,UAAU;AAC/E,QAAI,KAAK,SAAS,eAAe,OAAQ,QAAO,WAAW;AAC3D,QAAI,KAAK,SAAS,eAAe,WAAY,QAAO,eAAe;AACnE,QAAI,KAAK,SAAS,eAAe,QAAS,QAAO,mBAAmB;AACpE,QAAI,KAAK,SAAS,eAAe,eAAgB,QAAO,kBAAkB;AAC1E,QAAI,KAAK,SAAS,eAAe,SAAU,QAAO,YAAY;AAC9D,gBAAY,KAAK,OAAO;AAAA,EAC1B;AAEA,SAAO;AAAA,IACL;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AACF;","names":[]}
1
+ {"version":3,"sources":["../../../../src/components/account/hooks/useAccountHubActions.ts"],"sourcesContent":["'use client';\n\nimport { AccountHubFlow } from '../../../enums/AccountHubFlow';\nimport { WorkspaceSectionType } from '../../../enums/WorkspaceSectionType';\nimport { useTranslations } from 'next-intl';\nimport { useCouponByCode } from '../../../modules/coupons/hooks/use-coupon-by-code.hook';\nimport { isAccountSectionType } from '../../../modules/accounts/utils/is-account-section';\nimport type { AccountHubLink, AccountHubPurchase } from '../../../modules/accounts/types/account-hub-link.type';\nimport { getBillingPeriodSuffix } from '../../../modules/subscriptions/utils/periodicity';\nimport { useExternalContracting } from '../../../providers/whitelabel.provider';\nimport { useAccountModals, type MyAccountSection } from '../../../store/useAccountModals';\nimport { useProjectFlows } from '../../../store/useProjectFlows';\nimport { useSubscriptionFlows } from '../../../store/useSubscriptionFlows';\nimport { useTeamFlows } from '../../../store/useTeamFlows';\n\nexport default function useAccountHubActions() {\n const openConfigurations = useAccountModals((state) => state.openConfigurations);\n const openWorkspace = useAccountModals((state) => state.openWorkspace);\n const openChangePlan = useSubscriptionFlows((state) => state.openChangePlan);\n const openPlansCatalogFlow = useSubscriptionFlows((state) => state.openPlansCatalog);\n const requestOverdueReceipt = useSubscriptionFlows((state) => state.requestOverdueReceipt);\n const openCreateProjectFlow = useProjectFlows((state) => state.openCreateProject);\n const openAddUserFlow = useTeamFlows((state) => state.openAddUser);\n const openPaymentConfirmationFlow = useSubscriptionFlows((state) => state.openPaymentConfirmation);\n const { redirectToExternal } = useExternalContracting();\n const translate = useTranslations();\n const { mutate: findCouponByCode } = useCouponByCode();\n\n const openSection = (section: MyAccountSection) => {\n if (isAccountSectionType(section)) {\n openConfigurations(section);\n return;\n }\n openWorkspace(section);\n };\n\n const openPlansCatalog = (couponCode?: string) => {\n if (redirectToExternal('plans')) return;\n openWorkspace(WorkspaceSectionType.SUBSCRIPTION);\n\n if (!couponCode) {\n openPlansCatalogFlow();\n return;\n }\n\n findCouponByCode(couponCode, {\n onSuccess: (coupon) => openPlansCatalogFlow({ appliedCouponStripeData: coupon?.stripe_data }),\n onError: () => openPlansCatalogFlow(),\n });\n };\n\n const openAddons = () => {\n if (redirectToExternal('contracting')) return;\n openWorkspace(WorkspaceSectionType.SUBSCRIPTION);\n openChangePlan();\n };\n\n const openReactivate = () => {\n if (redirectToExternal('contracting')) return;\n openWorkspace(WorkspaceSectionType.SUBSCRIPTION);\n openChangePlan({ isReactivation: true });\n };\n\n const openOverdueReceipt = () => {\n openWorkspace(WorkspaceSectionType.SUBSCRIPTION);\n requestOverdueReceipt();\n };\n\n const openCreateProject = () => {\n openWorkspace(WorkspaceSectionType.PROJECTS);\n openCreateProjectFlow();\n };\n\n const openAddUser = () => {\n openWorkspace(WorkspaceSectionType.TEAM);\n openAddUserFlow();\n };\n\n const openPaymentConfirmation = (purchase: AccountHubPurchase) => {\n openWorkspace(WorkspaceSectionType.SUBSCRIPTION);\n openPaymentConfirmationFlow({\n planName: purchase.planName,\n total: purchase.total,\n periodLabel: getBillingPeriodSuffix(purchase.billingPeriod, translate),\n immediateChargeTotal: purchase.immediateChargeTotal,\n });\n };\n\n const openLink = (link: AccountHubLink) => {\n if (link.flow === AccountHubFlow.PLANS) return openPlansCatalog(link.couponCode);\n if (link.flow === AccountHubFlow.ADDONS) return openAddons();\n if (link.flow === AccountHubFlow.REACTIVATE) return openReactivate();\n if (link.flow === AccountHubFlow.RECEIPT) return openOverdueReceipt();\n if (link.flow === AccountHubFlow.CREATE_PROJECT) return openCreateProject();\n if (link.flow === AccountHubFlow.ADD_USER) return openAddUser();\n if (link.flow === AccountHubFlow.CONFIRMATION && link.purchase) return openPaymentConfirmation(link.purchase);\n openSection(link.section);\n };\n\n return {\n openSection,\n openPlansCatalog,\n openAddons,\n openReactivate,\n openOverdueReceipt,\n openCreateProject,\n openAddUser,\n openPaymentConfirmation,\n openLink,\n };\n}\n"],"mappings":"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","names":[]}
@@ -25,6 +25,7 @@ import { ChangeEmailModal } from "./ChangeEmailModal";
25
25
  import { NotificationsStep } from "./team/steps/NotificationsStep";
26
26
  import { ProjectSelectionStep } from "./team/steps/ProjectSelectionStep";
27
27
  import { FormSection } from "./shared/FormSection";
28
+ import { useAccountUnsavedChanges } from "../../../store/useAccountUnsavedChanges";
28
29
  import { buildUserFormDefaults, buildUserFormSchema } from "./team/user-form.schema";
29
30
  const TAB_PADDING = "px-4 py-5 lg:px-8 lg:py-6";
30
31
  const tabContentClasses = cn(
@@ -33,7 +34,7 @@ const tabContentClasses = cn(
33
34
  "[&::-webkit-scrollbar-thumb]:rounded-full [&::-webkit-scrollbar-thumb]:bg-zinc-300"
34
35
  );
35
36
  const tabTriggerClasses = "cursor-pointer gap-2 px-3 py-4";
36
- function MyProfileSection({ onClose, storageUrl }) {
37
+ function MyProfileSection({ storageUrl }) {
37
38
  const translate = useTranslations();
38
39
  const genderOptions = GENDER_OPTIONS.map((option) => ({
39
40
  ...option,
@@ -49,6 +50,9 @@ function MyProfileSection({ onClose, storageUrl }) {
49
50
  const [photoRemoved, setPhotoRemoved] = useState(false);
50
51
  const [changePhoneOpen, setChangePhoneOpen] = useState(false);
51
52
  const [changeEmailOpen, setChangeEmailOpen] = useState(false);
53
+ const setDirtyForm = useAccountUnsavedChanges((state) => state.setDirtyForm);
54
+ const completeSave = useAccountUnsavedChanges((state) => state.completeSave);
55
+ const dismissUnsavedChanges = useAccountUnsavedChanges((state) => state.dismiss);
52
56
  const profileFormSchema = useMemo(
53
57
  () => buildUserFormSchema({
54
58
  required: translate("common.validations.required"),
@@ -66,7 +70,7 @@ function MyProfileSection({ onClose, storageUrl }) {
66
70
  handleSubmit,
67
71
  watch,
68
72
  setValue,
69
- formState: { errors, isSubmitting }
73
+ formState: { errors, isSubmitting, isDirty }
70
74
  } = form;
71
75
  useEffect(() => {
72
76
  if (user) form.reset(buildUserFormDefaults(user));
@@ -106,7 +110,7 @@ function MyProfileSection({ onClose, storageUrl }) {
106
110
  toastId
107
111
  }
108
112
  ));
109
- onClose();
113
+ completeSave();
110
114
  } catch (error) {
111
115
  toast.custom((toastId) => /* @__PURE__ */ jsx(
112
116
  Toast,
@@ -116,8 +120,20 @@ function MyProfileSection({ onClose, storageUrl }) {
116
120
  toastId
117
121
  }
118
122
  ));
123
+ dismissUnsavedChanges();
119
124
  }
120
125
  };
126
+ const hasChanges = isDirty || selectedPhoto !== null || photoRemoved;
127
+ const discardChanges = () => {
128
+ form.reset();
129
+ setSelectedPhoto(null);
130
+ setPhotoRemoved(false);
131
+ };
132
+ useEffect(() => {
133
+ if (!hasChanges) return;
134
+ setDirtyForm({ submit: handleSubmit(onSubmit), discard: discardChanges });
135
+ return () => setDirtyForm(null);
136
+ });
121
137
  return /* @__PURE__ */ jsxs(Fragment, { children: [
122
138
  /* @__PURE__ */ jsx(FormProvider, { ...form, children: /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit(onSubmit), className: "flex flex-col h-full", children: [
123
139
  /* @__PURE__ */ jsx(
@@ -295,25 +311,7 @@ function MyProfileSection({ onClose, storageUrl }) {
295
311
  /* @__PURE__ */ jsx(TabsContent, { value: "projects", className: tabContentClasses, children: /* @__PURE__ */ jsx(ProjectSelectionStep, { mode: "edit", readOnly: !isOwnerOrAdmin, className: TAB_PADDING }) }),
296
312
  /* @__PURE__ */ jsx(TabsContent, { value: "notifications", className: tabContentClasses, children: /* @__PURE__ */ jsx(NotificationsStep, { className: TAB_PADDING }) })
297
313
  ] }),
298
- /* @__PURE__ */ jsxs("div", { className: "fixed bottom-0 left-0 right-0 lg:absolute flex items-center h-20 px-4 lg:px-8 border-t border-gray-200 justify-between bg-white z-10", children: [
299
- /* @__PURE__ */ jsx(
300
- Button,
301
- {
302
- variant: "secondary",
303
- className: "h-10!",
304
- type: "button",
305
- disabled: isSubmitting,
306
- onClick: () => {
307
- form.reset();
308
- setSelectedPhoto(null);
309
- setPhotoRemoved(false);
310
- onClose();
311
- },
312
- children: translate("common.actions.cancel")
313
- }
314
- ),
315
- /* @__PURE__ */ jsx(Button, { className: "h-10! cursor-pointer", type: "submit", disabled: isSubmitting, children: isSubmitting ? translate("common.actions.saving") : translate("common.actions.saveChanges") })
316
- ] })
314
+ /* @__PURE__ */ jsx("div", { className: "fixed bottom-0 left-0 right-0 lg:absolute flex items-center h-20 px-4 lg:px-8 border-t border-gray-200 justify-end bg-white z-10", children: /* @__PURE__ */ jsx(Button, { className: "h-10! cursor-pointer", type: "submit", disabled: !hasChanges || isSubmitting, children: isSubmitting ? translate("common.actions.saving") : translate("common.actions.saveChanges") }) })
317
315
  ] }) }),
318
316
  /* @__PURE__ */ jsx(ChangePhoneModal, { open: changePhoneOpen, onClose: () => setChangePhoneOpen(false) }),
319
317
  /* @__PURE__ */ jsx(ChangeEmailModal, { open: changeEmailOpen, onClose: () => setChangeEmailOpen(false) })
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/components/account/sections/MyProfileSection.tsx"],"sourcesContent":["'use client';\n\nimport { useEffect, useMemo, useState, useRef } from 'react';\nimport Image from 'next/image';\nimport { useTranslations } from 'next-intl';\nimport { FormProvider, useForm } from 'react-hook-form';\nimport { zodResolver } from '@hookform/resolvers/zod';\nimport { toast } from 'sonner';\nimport { IconBell, IconStack2, IconTrash, IconUpload, IconUser } from '@tabler/icons-react';\nimport { ACCEPTED_IMAGE_FORMATS_STRING } from '../../../modules/images/constants/image.constants';\nimport { Button } from '../../ui/buttons/Button';\nimport { Separator } from '../../ui/data-display/Separator';\nimport { Tabs, TabsContent, TabsList, TabsTrigger } from '../../ui/data-display/Tabs';\nimport { Toast } from '../../ui/feedback/Toast';\nimport { FormField } from '../../ui/form/FormField';\nimport { SelectField } from '../../ui/form/SelectField';\nimport PhoneInput from '../../ui/form/PhoneInput';\nimport { useAuth } from '../../../providers/auth.provider';\nimport { useManagementPermissions } from '../../../modules/auth/hooks/useManagementPermissions';\nimport { useUpdateAccountUser } from '../../../modules/accounts/hooks/useAccountManagement';\nimport { cn } from '../../../infra/utils/clsx';\nimport { GENDER_OPTIONS } from '../constants';\nimport { ChangePhoneModal } from './ChangePhoneModal';\nimport { ChangeEmailModal } from './ChangeEmailModal';\nimport { NotificationsStep } from './team/steps/NotificationsStep';\nimport { ProjectSelectionStep } from './team/steps/ProjectSelectionStep';\nimport { FormSection } from './shared/FormSection';\nimport { buildUserFormDefaults, buildUserFormSchema, type UserFormData } from './team/user-form.schema';\n\n/** Espaçamento das seções do modal, aplicado dentro de cada aba. */\nconst TAB_PADDING = 'px-4 py-5 lg:px-8 lg:py-6';\n\n/** Cada aba rola sozinha: a barra de abas fica fixa no topo e o rodapé de ações no fim. */\nconst tabContentClasses = cn(\n 'flex-1 min-h-0 overflow-y-auto overscroll-contain pb-24 lg:pb-28',\n '[scrollbar-width:auto] [&::-webkit-scrollbar]:w-1.5 [&::-webkit-scrollbar-track]:bg-transparent',\n '[&::-webkit-scrollbar-thumb]:rounded-full [&::-webkit-scrollbar-thumb]:bg-zinc-300'\n);\n\nconst tabTriggerClasses = 'cursor-pointer gap-2 px-3 py-4';\n\ninterface MyProfileSectionProps {\n onClose: () => void;\n storageUrl?: string;\n}\n\nexport function MyProfileSection({ onClose, storageUrl }: MyProfileSectionProps) {\n const translate = useTranslations();\n const genderOptions = GENDER_OPTIONS.map((option) => ({\n ...option,\n label: translate(\n `common.account.profile.genderOptions.${option.value === 'masculino' ? 'male' : 'female'}`,\n ),\n }));\n const { user } = useAuth();\n const { isOwnerOrAdmin } = useManagementPermissions();\n const updateAccountUser = useUpdateAccountUser();\n const fileInputRef = useRef<HTMLInputElement>(null);\n const [selectedPhoto, setSelectedPhoto] = useState<File | null>(null);\n const [photoRemoved, setPhotoRemoved] = useState(false);\n const [changePhoneOpen, setChangePhoneOpen] = useState(false);\n const [changeEmailOpen, setChangeEmailOpen] = useState(false);\n\n // Mesmo formulário do sheet da equipe: as abas Projetos e Notificações são os passos de lá.\n const profileFormSchema = useMemo(\n () =>\n buildUserFormSchema({\n required: translate('common.validations.required'),\n invalidEmail: translate('common.validations.invalidEmail'),\n }),\n [translate]\n );\n\n const form = useForm<UserFormData>({\n resolver: zodResolver(profileFormSchema),\n mode: 'onChange',\n defaultValues: buildUserFormDefaults(user ?? undefined),\n });\n\n const {\n register,\n handleSubmit,\n watch,\n setValue,\n formState: { errors, isSubmitting },\n } = form;\n\n // O usuário autenticado chega por query: repõe os valores quando ele carrega depois da montagem.\n useEffect(() => {\n if (user) form.reset(buildUserFormDefaults(user));\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [user?.id]);\n\n const baseStorageUrl = storageUrl ?? process.env.NEXT_PUBLIC_STORAGE_URL ?? '';\n\n const avatarPreview = selectedPhoto\n ? URL.createObjectURL(selectedPhoto)\n : !photoRemoved && user?.photo\n ? `${baseStorageUrl}/${user.photo}`\n : null;\n\n const handlePhotoChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const file = e.target.files?.[0] ?? null;\n if (file) {\n setSelectedPhoto(file);\n setPhotoRemoved(false);\n }\n };\n\n const handleRemovePhoto = () => {\n setSelectedPhoto(null);\n setPhotoRemoved(true);\n };\n\n const onSubmit = async (data: UserFormData) => {\n try {\n await updateAccountUser.mutateAsync({\n name: data.name,\n last_name: data.lastName,\n gender: data.gender,\n receive_sms: data.receiveSms,\n receive_email: data.receiveEmail,\n ...(selectedPhoto ? { photo: selectedPhoto } : {}),\n ...(photoRemoved ? { photo: '' } : {}),\n });\n\n form.reset(data);\n setSelectedPhoto(null);\n setPhotoRemoved(false);\n toast.custom((toastId) => (\n <Toast\n variant=\"success\"\n message={translate('common.account.profile.savedSuccess')}\n toastId={toastId}\n />\n ));\n onClose();\n } catch (error) {\n toast.custom((toastId) => (\n <Toast\n variant=\"error\"\n message={error instanceof Error ? error.message : translate('common.account.profile.saveError')}\n toastId={toastId}\n />\n ));\n }\n };\n\n return (\n <>\n <FormProvider {...form}>\n <form onSubmit={handleSubmit(onSubmit)} className=\"flex flex-col h-full\">\n <input\n ref={fileInputRef}\n type=\"file\"\n accept={ACCEPTED_IMAGE_FORMATS_STRING}\n className=\"hidden\"\n onChange={handlePhotoChange}\n />\n\n <Tabs defaultValue=\"information\" className=\"flex flex-col flex-1 min-h-0 gap-0\">\n {/* `px-1 lg:px-5` + `px-3` do gatilho: o primeiro rótulo alinha com o conteúdo das abas. */}\n <TabsList className=\"shrink-0 mt-1.5 px-1 lg:px-5 overflow-x-auto scrollbar-hide\">\n <TabsTrigger className={tabTriggerClasses} value=\"information\">\n <IconUser size={16} />\n {translate('common.account.workspace.team.sheet.tabInformation')}\n </TabsTrigger>\n <TabsTrigger className={tabTriggerClasses} value=\"projects\">\n <IconStack2 size={16} />\n {translate('common.account.workspace.team.sheet.tabProjects')}\n </TabsTrigger>\n <TabsTrigger className={tabTriggerClasses} value=\"notifications\">\n <IconBell size={16} />\n {translate('common.account.workspace.team.sheet.tabNotifications')}\n </TabsTrigger>\n </TabsList>\n\n <TabsContent\n value=\"information\"\n className={cn(tabContentClasses, TAB_PADDING, 'flex flex-col gap-6 lg:gap-8')}\n >\n <FormSection\n title={translate('common.account.profile.yourData')}\n description={translate('common.account.profile.yourDataDescription')}\n >\n <div className=\"flex items-center gap-4\">\n {avatarPreview ? (\n <Image\n src={avatarPreview}\n alt={translate('common.account.profile.avatarAlt')}\n width={48}\n height={48}\n className=\"size-12 rounded-full object-cover cursor-pointer\"\n onClick={() => fileInputRef.current?.click()}\n />\n ) : (\n <div\n className=\"size-12 rounded-full bg-gray-50 border border-dashed border-gray-200 flex items-center justify-center cursor-pointer\"\n onClick={() => fileInputRef.current?.click()}\n >\n <IconUpload size={20} className=\"text-gray-400\" />\n </div>\n )}\n {avatarPreview ? (\n <div className=\"flex items-center gap-1\">\n <Button\n variant=\"secondary\"\n className=\"w-fit h-8!\"\n type=\"button\"\n onClick={() => fileInputRef.current?.click()}\n >\n {translate('common.account.profile.changePhoto')}\n </Button>\n <Button type=\"button\" variant=\"ghost\" className=\"size-8! p-0\" onClick={handleRemovePhoto}>\n <IconTrash size={16} className=\"text-gray-500\" />\n </Button>\n </div>\n ) : (\n <Button\n variant=\"secondary\"\n className=\"w-fit h-8!\"\n type=\"button\"\n onClick={() => fileInputRef.current?.click()}\n >\n {translate('common.account.profile.changePhoto')}\n </Button>\n )}\n </div>\n <div className=\"flex flex-col gap-4\">\n <div className=\"flex gap-4\">\n <FormField\n label={translate('common.account.profile.name')}\n placeholder={translate('common.account.profile.namePlaceholder')}\n error={!!errors.name}\n errorMessage={errors.name?.message}\n {...register('name')}\n />\n <FormField\n label={translate('common.account.profile.lastName')}\n placeholder={translate('common.account.profile.lastNamePlaceholder')}\n error={!!errors.lastName}\n errorMessage={errors.lastName?.message}\n {...register('lastName')}\n />\n </div>\n <SelectField\n label={translate('common.account.profile.gender')}\n placeholder={translate('common.account.profile.genderPlaceholder')}\n options={genderOptions}\n value={GENDER_OPTIONS.find((option) => option.apiValue === watch('gender'))?.value ?? ''}\n className=\"h-10!\"\n onChange={(value) =>\n setValue(\n 'gender',\n GENDER_OPTIONS.find((option) => option.value === value)?.apiValue ?? 1,\n { shouldDirty: true }\n )\n }\n />\n <div className=\"relative\">\n <PhoneInput\n label={translate('common.account.profile.phone')}\n value={user?.phone ?? ''}\n disabled\n classnameContainer=\"bg-gray-50 border-0!\"\n />\n <button\n type=\"button\"\n className=\"absolute right-3 top-5.5 h-10 flex items-center paragraph-xsmall-semibold text-gray-950 underline cursor-pointer\"\n onClick={() => setChangePhoneOpen(true)}\n >\n {translate('common.account.profile.change')}\n </button>\n </div>\n </div>\n </FormSection>\n\n <Separator />\n\n <FormSection\n title={translate('common.account.profile.accessData')}\n description={translate('common.account.profile.accessDataDescription')}\n >\n <div className=\"relative\">\n <div className=\"cursor-not-allowed\">\n <FormField\n label={translate('common.account.profile.email')}\n value={user?.email ?? ''}\n disabled\n classnameContainer=\"bg-gray-50 border-0!\"\n />\n </div>\n <button\n type=\"button\"\n className=\"absolute right-3 top-5.5 h-10 flex items-center paragraph-xsmall-semibold text-gray-950 underline cursor-pointer\"\n onClick={() => setChangeEmailOpen(true)}\n >\n {translate('common.account.profile.change')}\n </button>\n </div>\n </FormSection>\n </TabsContent>\n\n {/* Só proprietário e administrador mudam vínculos de projeto — e ambos já veem todos. */}\n <TabsContent value=\"projects\" className={tabContentClasses}>\n <ProjectSelectionStep mode=\"edit\" readOnly={!isOwnerOrAdmin} className={TAB_PADDING} />\n </TabsContent>\n\n <TabsContent value=\"notifications\" className={tabContentClasses}>\n <NotificationsStep className={TAB_PADDING} />\n </TabsContent>\n </Tabs>\n\n <div className=\"fixed bottom-0 left-0 right-0 lg:absolute flex items-center h-20 px-4 lg:px-8 border-t border-gray-200 justify-between bg-white z-10\">\n <Button\n variant=\"secondary\"\n className=\"h-10!\"\n type=\"button\"\n disabled={isSubmitting}\n onClick={() => {\n form.reset();\n setSelectedPhoto(null);\n setPhotoRemoved(false);\n onClose();\n }}\n >\n {translate('common.actions.cancel')}\n </Button>\n <Button className=\"h-10! cursor-pointer\" type=\"submit\" disabled={isSubmitting}>\n {isSubmitting ? translate('common.actions.saving') : translate('common.actions.saveChanges')}\n </Button>\n </div>\n </form>\n </FormProvider>\n\n <ChangePhoneModal open={changePhoneOpen} onClose={() => setChangePhoneOpen(false)} />\n <ChangeEmailModal open={changeEmailOpen} onClose={() => setChangeEmailOpen(false)} />\n </>\n );\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../../../src/components/account/sections/MyProfileSection.tsx"],"sourcesContent":["'use client';\n\nimport { useEffect, useMemo, useState, useRef } from 'react';\nimport Image from 'next/image';\nimport { useTranslations } from 'next-intl';\nimport { FormProvider, useForm } from 'react-hook-form';\nimport { zodResolver } from '@hookform/resolvers/zod';\nimport { toast } from 'sonner';\nimport { IconBell, IconStack2, IconTrash, IconUpload, IconUser } from '@tabler/icons-react';\nimport { ACCEPTED_IMAGE_FORMATS_STRING } from '../../../modules/images/constants/image.constants';\nimport { Button } from '../../ui/buttons/Button';\nimport { Separator } from '../../ui/data-display/Separator';\nimport { Tabs, TabsContent, TabsList, TabsTrigger } from '../../ui/data-display/Tabs';\nimport { Toast } from '../../ui/feedback/Toast';\nimport { FormField } from '../../ui/form/FormField';\nimport { SelectField } from '../../ui/form/SelectField';\nimport PhoneInput from '../../ui/form/PhoneInput';\nimport { useAuth } from '../../../providers/auth.provider';\nimport { useManagementPermissions } from '../../../modules/auth/hooks/useManagementPermissions';\nimport { useUpdateAccountUser } from '../../../modules/accounts/hooks/useAccountManagement';\nimport { cn } from '../../../infra/utils/clsx';\nimport { GENDER_OPTIONS } from '../constants';\nimport { ChangePhoneModal } from './ChangePhoneModal';\nimport { ChangeEmailModal } from './ChangeEmailModal';\nimport { NotificationsStep } from './team/steps/NotificationsStep';\nimport { ProjectSelectionStep } from './team/steps/ProjectSelectionStep';\nimport { FormSection } from './shared/FormSection';\nimport { useAccountUnsavedChanges } from '../../../store/useAccountUnsavedChanges';\nimport { buildUserFormDefaults, buildUserFormSchema, type UserFormData } from './team/user-form.schema';\n\n/** Espaçamento das seções do modal, aplicado dentro de cada aba. */\nconst TAB_PADDING = 'px-4 py-5 lg:px-8 lg:py-6';\n\n/** Cada aba rola sozinha: a barra de abas fica fixa no topo e o rodapé de ações no fim. */\nconst tabContentClasses = cn(\n 'flex-1 min-h-0 overflow-y-auto overscroll-contain pb-24 lg:pb-28',\n '[scrollbar-width:auto] [&::-webkit-scrollbar]:w-1.5 [&::-webkit-scrollbar-track]:bg-transparent',\n '[&::-webkit-scrollbar-thumb]:rounded-full [&::-webkit-scrollbar-thumb]:bg-zinc-300'\n);\n\nconst tabTriggerClasses = 'cursor-pointer gap-2 px-3 py-4';\n\ninterface MyProfileSectionProps {\n storageUrl?: string;\n}\n\nexport function MyProfileSection({ storageUrl }: MyProfileSectionProps) {\n const translate = useTranslations();\n const genderOptions = GENDER_OPTIONS.map((option) => ({\n ...option,\n label: translate(\n `common.account.profile.genderOptions.${option.value === 'masculino' ? 'male' : 'female'}`,\n ),\n }));\n const { user } = useAuth();\n const { isOwnerOrAdmin } = useManagementPermissions();\n const updateAccountUser = useUpdateAccountUser();\n const fileInputRef = useRef<HTMLInputElement>(null);\n const [selectedPhoto, setSelectedPhoto] = useState<File | null>(null);\n const [photoRemoved, setPhotoRemoved] = useState(false);\n const [changePhoneOpen, setChangePhoneOpen] = useState(false);\n const [changeEmailOpen, setChangeEmailOpen] = useState(false);\n const setDirtyForm = useAccountUnsavedChanges((state) => state.setDirtyForm);\n const completeSave = useAccountUnsavedChanges((state) => state.completeSave);\n const dismissUnsavedChanges = useAccountUnsavedChanges((state) => state.dismiss);\n\n // Mesmo formulário do sheet da equipe: as abas Projetos e Notificações são os passos de lá.\n const profileFormSchema = useMemo(\n () =>\n buildUserFormSchema({\n required: translate('common.validations.required'),\n invalidEmail: translate('common.validations.invalidEmail'),\n }),\n [translate]\n );\n\n const form = useForm<UserFormData>({\n resolver: zodResolver(profileFormSchema),\n mode: 'onChange',\n defaultValues: buildUserFormDefaults(user ?? undefined),\n });\n\n const {\n register,\n handleSubmit,\n watch,\n setValue,\n formState: { errors, isSubmitting, isDirty },\n } = form;\n\n // O usuário autenticado chega por query: repõe os valores quando ele carrega depois da montagem.\n useEffect(() => {\n if (user) form.reset(buildUserFormDefaults(user));\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [user?.id]);\n\n const baseStorageUrl = storageUrl ?? process.env.NEXT_PUBLIC_STORAGE_URL ?? '';\n\n const avatarPreview = selectedPhoto\n ? URL.createObjectURL(selectedPhoto)\n : !photoRemoved && user?.photo\n ? `${baseStorageUrl}/${user.photo}`\n : null;\n\n const handlePhotoChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const file = e.target.files?.[0] ?? null;\n if (file) {\n setSelectedPhoto(file);\n setPhotoRemoved(false);\n }\n };\n\n const handleRemovePhoto = () => {\n setSelectedPhoto(null);\n setPhotoRemoved(true);\n };\n\n const onSubmit = async (data: UserFormData) => {\n try {\n await updateAccountUser.mutateAsync({\n name: data.name,\n last_name: data.lastName,\n gender: data.gender,\n receive_sms: data.receiveSms,\n receive_email: data.receiveEmail,\n ...(selectedPhoto ? { photo: selectedPhoto } : {}),\n ...(photoRemoved ? { photo: '' } : {}),\n });\n\n form.reset(data);\n setSelectedPhoto(null);\n setPhotoRemoved(false);\n toast.custom((toastId) => (\n <Toast\n variant=\"success\"\n message={translate('common.account.profile.savedSuccess')}\n toastId={toastId}\n />\n ));\n completeSave();\n } catch (error) {\n toast.custom((toastId) => (\n <Toast\n variant=\"error\"\n message={error instanceof Error ? error.message : translate('common.account.profile.saveError')}\n toastId={toastId}\n />\n ));\n dismissUnsavedChanges();\n }\n };\n\n const hasChanges = isDirty || selectedPhoto !== null || photoRemoved;\n\n const discardChanges = () => {\n form.reset();\n setSelectedPhoto(null);\n setPhotoRemoved(false);\n };\n\n useEffect(() => {\n if (!hasChanges) return;\n setDirtyForm({ submit: handleSubmit(onSubmit), discard: discardChanges });\n return () => setDirtyForm(null);\n });\n\n return (\n <>\n <FormProvider {...form}>\n <form onSubmit={handleSubmit(onSubmit)} className=\"flex flex-col h-full\">\n <input\n ref={fileInputRef}\n type=\"file\"\n accept={ACCEPTED_IMAGE_FORMATS_STRING}\n className=\"hidden\"\n onChange={handlePhotoChange}\n />\n\n <Tabs defaultValue=\"information\" className=\"flex flex-col flex-1 min-h-0 gap-0\">\n {/* `px-1 lg:px-5` + `px-3` do gatilho: o primeiro rótulo alinha com o conteúdo das abas. */}\n <TabsList className=\"shrink-0 mt-1.5 px-1 lg:px-5 overflow-x-auto scrollbar-hide\">\n <TabsTrigger className={tabTriggerClasses} value=\"information\">\n <IconUser size={16} />\n {translate('common.account.workspace.team.sheet.tabInformation')}\n </TabsTrigger>\n <TabsTrigger className={tabTriggerClasses} value=\"projects\">\n <IconStack2 size={16} />\n {translate('common.account.workspace.team.sheet.tabProjects')}\n </TabsTrigger>\n <TabsTrigger className={tabTriggerClasses} value=\"notifications\">\n <IconBell size={16} />\n {translate('common.account.workspace.team.sheet.tabNotifications')}\n </TabsTrigger>\n </TabsList>\n\n <TabsContent\n value=\"information\"\n className={cn(tabContentClasses, TAB_PADDING, 'flex flex-col gap-6 lg:gap-8')}\n >\n <FormSection\n title={translate('common.account.profile.yourData')}\n description={translate('common.account.profile.yourDataDescription')}\n >\n <div className=\"flex items-center gap-4\">\n {avatarPreview ? (\n <Image\n src={avatarPreview}\n alt={translate('common.account.profile.avatarAlt')}\n width={48}\n height={48}\n className=\"size-12 rounded-full object-cover cursor-pointer\"\n onClick={() => fileInputRef.current?.click()}\n />\n ) : (\n <div\n className=\"size-12 rounded-full bg-gray-50 border border-dashed border-gray-200 flex items-center justify-center cursor-pointer\"\n onClick={() => fileInputRef.current?.click()}\n >\n <IconUpload size={20} className=\"text-gray-400\" />\n </div>\n )}\n {avatarPreview ? (\n <div className=\"flex items-center gap-1\">\n <Button\n variant=\"secondary\"\n className=\"w-fit h-8!\"\n type=\"button\"\n onClick={() => fileInputRef.current?.click()}\n >\n {translate('common.account.profile.changePhoto')}\n </Button>\n <Button type=\"button\" variant=\"ghost\" className=\"size-8! p-0\" onClick={handleRemovePhoto}>\n <IconTrash size={16} className=\"text-gray-500\" />\n </Button>\n </div>\n ) : (\n <Button\n variant=\"secondary\"\n className=\"w-fit h-8!\"\n type=\"button\"\n onClick={() => fileInputRef.current?.click()}\n >\n {translate('common.account.profile.changePhoto')}\n </Button>\n )}\n </div>\n <div className=\"flex flex-col gap-4\">\n <div className=\"flex gap-4\">\n <FormField\n label={translate('common.account.profile.name')}\n placeholder={translate('common.account.profile.namePlaceholder')}\n error={!!errors.name}\n errorMessage={errors.name?.message}\n {...register('name')}\n />\n <FormField\n label={translate('common.account.profile.lastName')}\n placeholder={translate('common.account.profile.lastNamePlaceholder')}\n error={!!errors.lastName}\n errorMessage={errors.lastName?.message}\n {...register('lastName')}\n />\n </div>\n <SelectField\n label={translate('common.account.profile.gender')}\n placeholder={translate('common.account.profile.genderPlaceholder')}\n options={genderOptions}\n value={GENDER_OPTIONS.find((option) => option.apiValue === watch('gender'))?.value ?? ''}\n className=\"h-10!\"\n onChange={(value) =>\n setValue(\n 'gender',\n GENDER_OPTIONS.find((option) => option.value === value)?.apiValue ?? 1,\n { shouldDirty: true }\n )\n }\n />\n <div className=\"relative\">\n <PhoneInput\n label={translate('common.account.profile.phone')}\n value={user?.phone ?? ''}\n disabled\n classnameContainer=\"bg-gray-50 border-0!\"\n />\n <button\n type=\"button\"\n className=\"absolute right-3 top-5.5 h-10 flex items-center paragraph-xsmall-semibold text-gray-950 underline cursor-pointer\"\n onClick={() => setChangePhoneOpen(true)}\n >\n {translate('common.account.profile.change')}\n </button>\n </div>\n </div>\n </FormSection>\n\n <Separator />\n\n <FormSection\n title={translate('common.account.profile.accessData')}\n description={translate('common.account.profile.accessDataDescription')}\n >\n <div className=\"relative\">\n <div className=\"cursor-not-allowed\">\n <FormField\n label={translate('common.account.profile.email')}\n value={user?.email ?? ''}\n disabled\n classnameContainer=\"bg-gray-50 border-0!\"\n />\n </div>\n <button\n type=\"button\"\n className=\"absolute right-3 top-5.5 h-10 flex items-center paragraph-xsmall-semibold text-gray-950 underline cursor-pointer\"\n onClick={() => setChangeEmailOpen(true)}\n >\n {translate('common.account.profile.change')}\n </button>\n </div>\n </FormSection>\n </TabsContent>\n\n {/* Só proprietário e administrador mudam vínculos de projeto — e ambos já veem todos. */}\n <TabsContent value=\"projects\" className={tabContentClasses}>\n <ProjectSelectionStep mode=\"edit\" readOnly={!isOwnerOrAdmin} className={TAB_PADDING} />\n </TabsContent>\n\n <TabsContent value=\"notifications\" className={tabContentClasses}>\n <NotificationsStep className={TAB_PADDING} />\n </TabsContent>\n </Tabs>\n\n <div className=\"fixed bottom-0 left-0 right-0 lg:absolute flex items-center h-20 px-4 lg:px-8 border-t border-gray-200 justify-end bg-white z-10\">\n <Button className=\"h-10! cursor-pointer\" type=\"submit\" disabled={!hasChanges || isSubmitting}>\n {isSubmitting ? translate('common.actions.saving') : translate('common.actions.saveChanges')}\n </Button>\n </div>\n </form>\n </FormProvider>\n\n <ChangePhoneModal open={changePhoneOpen} onClose={() => setChangePhoneOpen(false)} />\n <ChangeEmailModal open={changeEmailOpen} onClose={() => setChangeEmailOpen(false)} />\n </>\n );\n}\n"],"mappings":"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","names":[]}
@@ -31,8 +31,9 @@ import {
31
31
  } from "../constants";
32
32
  import { useTimezones } from "../../../modules/accounts/hooks/useTimezones";
33
33
  import { useCurrencies } from "../../../modules/accounts/hooks/useCurrencies";
34
+ import { useAccountUnsavedChanges } from "../../../store/useAccountUnsavedChanges";
34
35
  import { getCurrencyForGateway } from "../../../utils/format/currency";
35
- function PreferencesSection({ onClose }) {
36
+ function PreferencesSection() {
36
37
  const translate = useTranslations();
37
38
  const translateLanguage = useTranslations("common.languages");
38
39
  const currentLocale = useLocale();
@@ -59,7 +60,10 @@ function PreferencesSection({ onClose }) {
59
60
  const [confirmGlobalOpen, setConfirmGlobalOpen] = useState(false);
60
61
  const [pendingSubmit, setPendingSubmit] = useState(null);
61
62
  const [isSaving, setIsSaving] = useState(false);
62
- const { watch, setValue, handleSubmit, reset, formState: { dirtyFields } } = useForm({
63
+ const setDirtyForm = useAccountUnsavedChanges((state) => state.setDirtyForm);
64
+ const completeSave = useAccountUnsavedChanges((state) => state.completeSave);
65
+ const dismissUnsavedChanges = useAccountUnsavedChanges((state) => state.dismiss);
66
+ const { watch, setValue, handleSubmit, reset, formState: { dirtyFields, isDirty } } = useForm({
63
67
  defaultValues: {
64
68
  companyName: "",
65
69
  language: "",
@@ -100,7 +104,7 @@ function PreferencesSection({ onClose }) {
100
104
  }
101
105
  reset(data);
102
106
  toast.custom((toastId) => /* @__PURE__ */ jsx(Toast, { variant: "success", message: translate("common.preferences.savedSuccess"), toastId }));
103
- onClose();
107
+ completeSave();
104
108
  if (data.language !== currentLocale) {
105
109
  setLocaleCookie(data.language, whitelabel?.domain);
106
110
  router.refresh();
@@ -114,6 +118,7 @@ function PreferencesSection({ onClose }) {
114
118
  toastId
115
119
  }
116
120
  ));
121
+ dismissUnsavedChanges();
117
122
  } finally {
118
123
  setIsSaving(false);
119
124
  }
@@ -142,7 +147,13 @@ function PreferencesSection({ onClose }) {
142
147
  const handleCancelGlobal = () => {
143
148
  setConfirmGlobalOpen(false);
144
149
  setPendingSubmit(null);
150
+ dismissUnsavedChanges();
145
151
  };
152
+ useEffect(() => {
153
+ if (!isDirty) return;
154
+ setDirtyForm({ submit: handleSubmit(onSubmit), discard: () => reset() });
155
+ return () => setDirtyForm(null);
156
+ });
146
157
  return /* @__PURE__ */ jsxs(Fragment, { children: [
147
158
  /* @__PURE__ */ jsxs("form", { onSubmit: handleSubmit(onSubmit), className: "flex flex-col h-full", children: [
148
159
  /* @__PURE__ */ jsxs("div", { className: "overflow-y-auto [scrollbar-width:auto] [&::-webkit-scrollbar]:w-1.5 [&::-webkit-scrollbar-track]:bg-transparent [&::-webkit-scrollbar-thumb]:rounded-full [&::-webkit-scrollbar-thumb]:bg-zinc-300 overscroll-contain px-4 py-5 pb-24 lg:px-8 lg:py-6 lg:pb-28 flex flex-col gap-6 lg:gap-8 flex-1", children: [
@@ -233,23 +244,7 @@ function PreferencesSection({ onClose }) {
233
244
  }
234
245
  )
235
246
  ] }),
236
- /* @__PURE__ */ jsxs("div", { className: "fixed bottom-0 left-0 right-0 lg:absolute flex items-center h-20 px-4 lg:px-8 border-t border-gray-200 justify-between bg-white z-10", children: [
237
- /* @__PURE__ */ jsx(
238
- Button,
239
- {
240
- variant: "secondary",
241
- className: "h-10!",
242
- type: "button",
243
- disabled: isSaving,
244
- onClick: () => {
245
- reset();
246
- onClose();
247
- },
248
- children: translate("common.actions.cancel")
249
- }
250
- ),
251
- /* @__PURE__ */ jsx(Button, { className: "h-10!", type: "submit", disabled: isSaving, children: isSaving ? translate("common.actions.saving") : translate("common.actions.saveChanges") })
252
- ] })
247
+ /* @__PURE__ */ jsx("div", { className: "fixed bottom-0 left-0 right-0 lg:absolute flex items-center h-20 px-4 lg:px-8 border-t border-gray-200 justify-end bg-white z-10", children: /* @__PURE__ */ jsx(Button, { className: "h-10!", type: "submit", disabled: !isDirty || isSaving, children: isSaving ? translate("common.actions.saving") : translate("common.actions.saveChanges") }) })
253
248
  ] }),
254
249
  /* @__PURE__ */ jsx(
255
250
  ConfirmGlobalPreferencesModal,