@greatapps/common 1.1.542 → 1.1.546

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 (82) hide show
  1. package/dist/components/account/ConfigurationsMyAccountModal.mjs +1 -1
  2. package/dist/components/account/ConfigurationsMyAccountModal.mjs.map +1 -1
  3. package/dist/components/account/ConfirmDeleteAccountModal.mjs +12 -10
  4. package/dist/components/account/ConfirmDeleteAccountModal.mjs.map +1 -1
  5. package/dist/components/account/DeleteAccountModal.mjs +21 -5
  6. package/dist/components/account/DeleteAccountModal.mjs.map +1 -1
  7. package/dist/components/account/sections/MyProfileSection.mjs +3 -61
  8. package/dist/components/account/sections/MyProfileSection.mjs.map +1 -1
  9. package/dist/components/navigation/CancelledSubscriptionBanner.mjs +5 -4
  10. package/dist/components/navigation/CancelledSubscriptionBanner.mjs.map +1 -1
  11. package/dist/modules/accounts/actions/account-management.action.mjs +16 -15
  12. package/dist/modules/accounts/actions/account-management.action.mjs.map +1 -1
  13. package/dist/modules/accounts/hooks/useAccountManagement.mjs.map +1 -1
  14. package/dist/modules/accounts/services/account.service.mjs +5 -2
  15. package/dist/modules/accounts/services/account.service.mjs.map +1 -1
  16. package/dist/modules/auth/hooks/useUserQuery.mjs +1 -1
  17. package/dist/modules/auth/hooks/useUserQuery.mjs.map +1 -1
  18. package/dist/modules/auth/utils/require-valid-session.mjs +17 -0
  19. package/dist/modules/auth/utils/require-valid-session.mjs.map +1 -0
  20. package/dist/modules/cards/actions/create-card.action.mjs +2 -2
  21. package/dist/modules/cards/actions/create-card.action.mjs.map +1 -1
  22. package/dist/modules/cards/actions/delete-card.action.mjs +2 -2
  23. package/dist/modules/cards/actions/delete-card.action.mjs.map +1 -1
  24. package/dist/modules/cards/actions/set-default-card.action.mjs +2 -2
  25. package/dist/modules/cards/actions/set-default-card.action.mjs.map +1 -1
  26. package/dist/modules/cards/types.mjs +13 -12
  27. package/dist/modules/cards/types.mjs.map +1 -1
  28. package/dist/modules/plans/utils/map-api-plan-to-ui.mjs +1 -1
  29. package/dist/modules/plans/utils/map-api-plan-to-ui.mjs.map +1 -1
  30. package/dist/modules/projects/actions/add-project-user.action.mjs +2 -2
  31. package/dist/modules/projects/actions/add-project-user.action.mjs.map +1 -1
  32. package/dist/modules/projects/actions/create-project.action.mjs +2 -2
  33. package/dist/modules/projects/actions/create-project.action.mjs.map +1 -1
  34. package/dist/modules/projects/actions/delete-project.action.mjs +2 -2
  35. package/dist/modules/projects/actions/delete-project.action.mjs.map +1 -1
  36. package/dist/modules/projects/actions/remove-project-user.action.mjs +2 -2
  37. package/dist/modules/projects/actions/remove-project-user.action.mjs.map +1 -1
  38. package/dist/modules/projects/actions/update-project.action.mjs +2 -2
  39. package/dist/modules/projects/actions/update-project.action.mjs.map +1 -1
  40. package/dist/modules/subscriptions/actions/update-subscription-plan.action.mjs +2 -2
  41. package/dist/modules/subscriptions/actions/update-subscription-plan.action.mjs.map +1 -1
  42. package/dist/modules/subscriptions/types/subscription.type.mjs +2 -1
  43. package/dist/modules/subscriptions/types/subscription.type.mjs.map +1 -1
  44. package/dist/modules/users/action/mark-all-messages-as-read.action.mjs +2 -2
  45. package/dist/modules/users/action/mark-all-messages-as-read.action.mjs.map +1 -1
  46. package/dist/modules/users/action/mark-message-as-read.action.mjs +2 -2
  47. package/dist/modules/users/action/mark-message-as-read.action.mjs.map +1 -1
  48. package/dist/server.mjs +4 -0
  49. package/dist/server.mjs.map +1 -1
  50. package/dist/store/useAccountModals.mjs +25 -4
  51. package/dist/store/useAccountModals.mjs.map +1 -1
  52. package/dist/utils/safeMutationAction.mjs +13 -0
  53. package/dist/utils/safeMutationAction.mjs.map +1 -0
  54. package/package.json +1 -1
  55. package/src/components/account/ConfigurationsMyAccountModal.tsx +1 -0
  56. package/src/components/account/ConfirmDeleteAccountModal.tsx +16 -7
  57. package/src/components/account/DeleteAccountModal.tsx +24 -4
  58. package/src/components/account/sections/MyProfileSection.tsx +2 -68
  59. package/src/components/navigation/CancelledSubscriptionBanner.tsx +7 -4
  60. package/src/modules/accounts/actions/account-management.action.ts +17 -15
  61. package/src/modules/accounts/hooks/useAccountManagement.ts +2 -1
  62. package/src/modules/accounts/services/account.service.ts +6 -2
  63. package/src/modules/accounts/types.ts +4 -0
  64. package/src/modules/auth/hooks/useUserQuery.ts +1 -1
  65. package/src/modules/auth/utils/require-valid-session.ts +35 -0
  66. package/src/modules/cards/actions/create-card.action.ts +2 -2
  67. package/src/modules/cards/actions/delete-card.action.ts +2 -2
  68. package/src/modules/cards/actions/set-default-card.action.ts +2 -2
  69. package/src/modules/cards/types.ts +14 -12
  70. package/src/modules/plans/utils/map-api-plan-to-ui.ts +1 -1
  71. package/src/modules/projects/actions/add-project-user.action.ts +2 -2
  72. package/src/modules/projects/actions/create-project.action.ts +2 -2
  73. package/src/modules/projects/actions/delete-project.action.ts +2 -2
  74. package/src/modules/projects/actions/remove-project-user.action.ts +2 -2
  75. package/src/modules/projects/actions/update-project.action.ts +2 -2
  76. package/src/modules/subscriptions/actions/update-subscription-plan.action.ts +2 -2
  77. package/src/modules/subscriptions/types/subscription.type.ts +2 -1
  78. package/src/modules/users/action/mark-all-messages-as-read.action.ts +2 -2
  79. package/src/modules/users/action/mark-message-as-read.action.ts +2 -2
  80. package/src/server.ts +2 -0
  81. package/src/store/useAccountModals.ts +41 -8
  82. package/src/utils/safeMutationAction.ts +30 -0
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/server.ts"],"sourcesContent":["import 'server-only';\r\n\r\n// API Client\r\nexport { ApiClient, api, apiClient } from './infra/api/client';\r\n\r\n// Services\r\nexport { authService } from './modules/auth/services/auth.service';\r\nexport { whitelabelService } from './modules/whitelabel/services/whitelabel.service';\r\nexport { revalidateWhitelabelAction } from './modules/whitelabel/actions/revalidate-whitelabel.action';\r\nexport { assertManagementPermission } from './modules/auth/utils/assert-management-permission';\r\nexport { assertManagementSubscription } from './modules/auth/utils/assert-management-subscription';\r\nexport { assertSubscriptionAddon } from './modules/auth/utils/assert-subscription-addon';\r\nexport { assertPaidSubscription } from './modules/auth/utils/assert-paid-subscription';\r\nexport { subscriptionsService } from './modules/subscriptions/services/subscriptions.service';\r\nexport { plansService } from './modules/plans/services/plans.service';\r\nexport { cardsService } from './modules/cards/services/cards.service';\r\nexport { iaCreditsService } from './modules/ia-credits/services/ia-credits.service';\r\n\r\n// Actions\r\nexport { findWhitelabel } from './modules/whitelabel/actions/find-whitelabel.action';\r\nexport { validateSessionAction } from './modules/auth/actions/validate-session.action';\r\nexport { findUserById } from './modules/users/action/find-user-by-id.action';\r\nexport { findCurrentAccount } from './modules/accounts/actions/find-current-account.action';\r\nexport { getAccountTokenAction } from './modules/accounts/actions/get-account-token.action';\r\nexport { listSubscriptionsAction } from './modules/subscriptions/actions/list-subscriptions.action';\r\nexport { findPlanByIdAction } from './modules/plans/actions/find-plan-by-id.action';\r\nexport { listPlansAction } from './modules/plans/actions/list-plans.action';\r\nexport { calculateSubscriptionAction } from './modules/subscriptions/actions/calculate-subscription.action';\r\nexport { updateSubscriptionPlanAction } from './modules/subscriptions/actions/update-subscription-plan.action';\r\nexport { listCardsAction } from './modules/cards/actions/list-cards.action';\r\nexport { createCardAction } from './modules/cards/actions/create-card.action';\r\nexport { resolvePlanExtrasPrices } from './modules/subscriptions/utils/resolve-plan-extras-prices';\r\nexport { hasSubscriptionExpired } from './modules/subscriptions/utils/has-subscription-expired';\r\nexport { listIaCreditsAction } from './modules/ia-credits/actions/list-ia-credits.action';\r\nexport { countPagesAction } from './modules/pages/actions/count-pages.action';\r\n\r\n// Project Services & Actions (server-only)\r\nexport { projectsService } from './modules/projects/services/projects.service';\r\nexport { listProjectsAction } from './modules/projects/actions/list-projects.action';\r\nexport { findProjectAction } from './modules/projects/actions/find-project.action';\r\nexport { createProjectAction } from './modules/projects/actions/create-project.action';\r\nexport { updateProjectAction } from './modules/projects/actions/update-project.action';\r\nexport { deleteProjectAction } from './modules/projects/actions/delete-project.action';\r\n\r\n// Project Users Services & Actions (server-only)\r\nexport { projectUsersService } from './modules/projects/services/project-users.service';\r\nexport { listProjectUsersAction } from './modules/projects/actions/list-project-users.action';\r\nexport { listAvailableUsersAction } from './modules/projects/actions/list-available-users.action';\r\nexport { addProjectUserAction } from './modules/projects/actions/add-project-user.action';\r\nexport { removeProjectUserAction } from './modules/projects/actions/remove-project-user.action';\r\n\r\n// Account Services & Actions (server-only)\r\nexport { accountService } from './modules/accounts/services/account.service';\r\nexport { twoFactorService } from './modules/accounts/services/two-factor.service';\r\nexport {\r\n listAccountUsersAction,\r\n updateUserAction,\r\n updateAccountAction,\r\n updateAccountUserByIdAction,\r\n deleteAccountUserAction,\r\n deleteAccountAction,\r\n createAccountUserAction,\r\n changePasswordAction,\r\n requestEmailChangeAction,\r\n confirmEmailChangeAction,\r\n requestPhoneChangeAction,\r\n confirmPhoneChangeAction,\r\n generateTwoFactorAction,\r\n confirmTwoFactorAction,\r\n disableTwoFactorAction,\r\n} from './modules/accounts/actions/account-management.action';\r\n\r\n// Server Utils\r\nexport { getClientInfoFromRequest } from './infra/utils/client-info';\r\nexport { getUserContext } from './modules/auth/utils/get-user-context';\r\nexport { buildWlOverride, buildWlOverrideFromJwt } from './modules/auth/utils/build-wl-override';\r\nexport { safeServerAction } from './utils/safeServerAction';\r\n\r\n// Image Processing (server-side)\r\nexport { processImageAction } from './modules/images/actions/process-image.action';\r\nexport { processImage } from './modules/images/services/image-processing.service';\r\nexport type {\r\n ProcessImageInput,\r\n ProcessImageOutput,\r\n ProcessImageActionInput,\r\n ProcessImageActionOutput,\r\n} from './modules/images/types/image.type';\r\n"],"mappings":"AAAA,OAAO;AAGP,SAAS,WAAW,KAAK,iBAAiB;AAG1C,SAAS,mBAAmB;AAC5B,SAAS,yBAAyB;AAClC,SAAS,kCAAkC;AAC3C,SAAS,kCAAkC;AAC3C,SAAS,oCAAoC;AAC7C,SAAS,+BAA+B;AACxC,SAAS,8BAA8B;AACvC,SAAS,4BAA4B;AACrC,SAAS,oBAAoB;AAC7B,SAAS,oBAAoB;AAC7B,SAAS,wBAAwB;AAGjC,SAAS,sBAAsB;AAC/B,SAAS,6BAA6B;AACtC,SAAS,oBAAoB;AAC7B,SAAS,0BAA0B;AACnC,SAAS,6BAA6B;AACtC,SAAS,+BAA+B;AACxC,SAAS,0BAA0B;AACnC,SAAS,uBAAuB;AAChC,SAAS,mCAAmC;AAC5C,SAAS,oCAAoC;AAC7C,SAAS,uBAAuB;AAChC,SAAS,wBAAwB;AACjC,SAAS,+BAA+B;AACxC,SAAS,8BAA8B;AACvC,SAAS,2BAA2B;AACpC,SAAS,wBAAwB;AAGjC,SAAS,uBAAuB;AAChC,SAAS,0BAA0B;AACnC,SAAS,yBAAyB;AAClC,SAAS,2BAA2B;AACpC,SAAS,2BAA2B;AACpC,SAAS,2BAA2B;AAGpC,SAAS,2BAA2B;AACpC,SAAS,8BAA8B;AACvC,SAAS,gCAAgC;AACzC,SAAS,4BAA4B;AACrC,SAAS,+BAA+B;AAGxC,SAAS,sBAAsB;AAC/B,SAAS,wBAAwB;AACjC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAGP,SAAS,gCAAgC;AACzC,SAAS,sBAAsB;AAC/B,SAAS,iBAAiB,8BAA8B;AACxD,SAAS,wBAAwB;AAGjC,SAAS,0BAA0B;AACnC,SAAS,oBAAoB;","names":[]}
1
+ {"version":3,"sources":["../src/server.ts"],"sourcesContent":["import 'server-only';\r\n\r\n// API Client\r\nexport { ApiClient, api, apiClient } from './infra/api/client';\r\n\r\n// Services\r\nexport { authService } from './modules/auth/services/auth.service';\r\nexport { whitelabelService } from './modules/whitelabel/services/whitelabel.service';\r\nexport { revalidateWhitelabelAction } from './modules/whitelabel/actions/revalidate-whitelabel.action';\r\nexport { assertManagementPermission } from './modules/auth/utils/assert-management-permission';\r\nexport { assertManagementSubscription } from './modules/auth/utils/assert-management-subscription';\r\nexport { assertSubscriptionAddon } from './modules/auth/utils/assert-subscription-addon';\r\nexport { assertPaidSubscription } from './modules/auth/utils/assert-paid-subscription';\r\nexport { subscriptionsService } from './modules/subscriptions/services/subscriptions.service';\r\nexport { plansService } from './modules/plans/services/plans.service';\r\nexport { cardsService } from './modules/cards/services/cards.service';\r\nexport { iaCreditsService } from './modules/ia-credits/services/ia-credits.service';\r\n\r\n// Actions\r\nexport { findWhitelabel } from './modules/whitelabel/actions/find-whitelabel.action';\r\nexport { validateSessionAction } from './modules/auth/actions/validate-session.action';\r\nexport { findUserById } from './modules/users/action/find-user-by-id.action';\r\nexport { findCurrentAccount } from './modules/accounts/actions/find-current-account.action';\r\nexport { getAccountTokenAction } from './modules/accounts/actions/get-account-token.action';\r\nexport { listSubscriptionsAction } from './modules/subscriptions/actions/list-subscriptions.action';\r\nexport { findPlanByIdAction } from './modules/plans/actions/find-plan-by-id.action';\r\nexport { listPlansAction } from './modules/plans/actions/list-plans.action';\r\nexport { calculateSubscriptionAction } from './modules/subscriptions/actions/calculate-subscription.action';\r\nexport { updateSubscriptionPlanAction } from './modules/subscriptions/actions/update-subscription-plan.action';\r\nexport { listCardsAction } from './modules/cards/actions/list-cards.action';\r\nexport { createCardAction } from './modules/cards/actions/create-card.action';\r\nexport { resolvePlanExtrasPrices } from './modules/subscriptions/utils/resolve-plan-extras-prices';\r\nexport { hasSubscriptionExpired } from './modules/subscriptions/utils/has-subscription-expired';\r\nexport { listIaCreditsAction } from './modules/ia-credits/actions/list-ia-credits.action';\r\nexport { countPagesAction } from './modules/pages/actions/count-pages.action';\r\n\r\n// Project Services & Actions (server-only)\r\nexport { projectsService } from './modules/projects/services/projects.service';\r\nexport { listProjectsAction } from './modules/projects/actions/list-projects.action';\r\nexport { findProjectAction } from './modules/projects/actions/find-project.action';\r\nexport { createProjectAction } from './modules/projects/actions/create-project.action';\r\nexport { updateProjectAction } from './modules/projects/actions/update-project.action';\r\nexport { deleteProjectAction } from './modules/projects/actions/delete-project.action';\r\n\r\n// Project Users Services & Actions (server-only)\r\nexport { projectUsersService } from './modules/projects/services/project-users.service';\r\nexport { listProjectUsersAction } from './modules/projects/actions/list-project-users.action';\r\nexport { listAvailableUsersAction } from './modules/projects/actions/list-available-users.action';\r\nexport { addProjectUserAction } from './modules/projects/actions/add-project-user.action';\r\nexport { removeProjectUserAction } from './modules/projects/actions/remove-project-user.action';\r\n\r\n// Account Services & Actions (server-only)\r\nexport { accountService } from './modules/accounts/services/account.service';\r\nexport { twoFactorService } from './modules/accounts/services/two-factor.service';\r\nexport {\r\n listAccountUsersAction,\r\n updateUserAction,\r\n updateAccountAction,\r\n updateAccountUserByIdAction,\r\n deleteAccountUserAction,\r\n deleteAccountAction,\r\n createAccountUserAction,\r\n changePasswordAction,\r\n requestEmailChangeAction,\r\n confirmEmailChangeAction,\r\n requestPhoneChangeAction,\r\n confirmPhoneChangeAction,\r\n generateTwoFactorAction,\r\n confirmTwoFactorAction,\r\n disableTwoFactorAction,\r\n} from './modules/accounts/actions/account-management.action';\r\n\r\n// Server Utils\r\nexport { getClientInfoFromRequest } from './infra/utils/client-info';\r\nexport { getUserContext } from './modules/auth/utils/get-user-context';\r\nexport { requireValidSession } from './modules/auth/utils/require-valid-session';\r\nexport { buildWlOverride, buildWlOverrideFromJwt } from './modules/auth/utils/build-wl-override';\r\nexport { safeServerAction } from './utils/safeServerAction';\r\nexport { safeMutationAction } from './utils/safeMutationAction';\r\n\r\n// Image Processing (server-side)\r\nexport { processImageAction } from './modules/images/actions/process-image.action';\r\nexport { processImage } from './modules/images/services/image-processing.service';\r\nexport type {\r\n ProcessImageInput,\r\n ProcessImageOutput,\r\n ProcessImageActionInput,\r\n ProcessImageActionOutput,\r\n} from './modules/images/types/image.type';\r\n"],"mappings":"AAAA,OAAO;AAGP,SAAS,WAAW,KAAK,iBAAiB;AAG1C,SAAS,mBAAmB;AAC5B,SAAS,yBAAyB;AAClC,SAAS,kCAAkC;AAC3C,SAAS,kCAAkC;AAC3C,SAAS,oCAAoC;AAC7C,SAAS,+BAA+B;AACxC,SAAS,8BAA8B;AACvC,SAAS,4BAA4B;AACrC,SAAS,oBAAoB;AAC7B,SAAS,oBAAoB;AAC7B,SAAS,wBAAwB;AAGjC,SAAS,sBAAsB;AAC/B,SAAS,6BAA6B;AACtC,SAAS,oBAAoB;AAC7B,SAAS,0BAA0B;AACnC,SAAS,6BAA6B;AACtC,SAAS,+BAA+B;AACxC,SAAS,0BAA0B;AACnC,SAAS,uBAAuB;AAChC,SAAS,mCAAmC;AAC5C,SAAS,oCAAoC;AAC7C,SAAS,uBAAuB;AAChC,SAAS,wBAAwB;AACjC,SAAS,+BAA+B;AACxC,SAAS,8BAA8B;AACvC,SAAS,2BAA2B;AACpC,SAAS,wBAAwB;AAGjC,SAAS,uBAAuB;AAChC,SAAS,0BAA0B;AACnC,SAAS,yBAAyB;AAClC,SAAS,2BAA2B;AACpC,SAAS,2BAA2B;AACpC,SAAS,2BAA2B;AAGpC,SAAS,2BAA2B;AACpC,SAAS,8BAA8B;AACvC,SAAS,gCAAgC;AACzC,SAAS,4BAA4B;AACrC,SAAS,+BAA+B;AAGxC,SAAS,sBAAsB;AAC/B,SAAS,wBAAwB;AACjC;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAGP,SAAS,gCAAgC;AACzC,SAAS,sBAAsB;AAC/B,SAAS,2BAA2B;AACpC,SAAS,iBAAiB,8BAA8B;AACxD,SAAS,wBAAwB;AACjC,SAAS,0BAA0B;AAGnC,SAAS,0BAA0B;AACnC,SAAS,oBAAoB;","names":[]}
@@ -4,12 +4,33 @@ const useAccountModals = create((set) => ({
4
4
  activeModal: null,
5
5
  config: {},
6
6
  initialSection: void 0,
7
+ deleteAccountPayload: null,
8
+ deleteAccountError: null,
7
9
  updateConfig: (config) => set({ config }),
8
- openConfigurations: (initialSection) => set({ activeModal: "configurations", initialSection }),
9
- openDeleteAccount: () => set({ activeModal: "deleteAccount" }),
10
+ openConfigurations: (initialSection) => set({
11
+ activeModal: "configurations",
12
+ initialSection,
13
+ deleteAccountPayload: null,
14
+ deleteAccountError: null
15
+ }),
16
+ openDeleteAccount: (error) => set({
17
+ activeModal: "deleteAccount",
18
+ deleteAccountPayload: null,
19
+ deleteAccountError: error ?? null
20
+ }),
10
21
  openIsntPossibleDelete: () => set({ activeModal: "isntPossibleDelete" }),
11
- openConfirmDelete: () => set({ activeModal: "confirmDelete" }),
12
- close: () => set({ activeModal: null, initialSection: void 0 })
22
+ openConfirmDelete: (payload) => set({
23
+ activeModal: "confirmDelete",
24
+ deleteAccountPayload: payload,
25
+ deleteAccountError: null
26
+ }),
27
+ clearDeleteAccountError: () => set({ deleteAccountError: null }),
28
+ close: () => set({
29
+ activeModal: null,
30
+ initialSection: void 0,
31
+ deleteAccountPayload: null,
32
+ deleteAccountError: null
33
+ })
13
34
  }));
14
35
  export {
15
36
  useAccountModals
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/store/useAccountModals.ts"],"sourcesContent":["'use client';\n\nimport { create } from 'zustand';\nimport type { AccountSectionType } from '../enums/AccountSectionType';\n\ntype AccountModalType =\n | 'configurations'\n | 'deleteAccount'\n | 'isntPossibleDelete'\n | 'confirmDelete';\n\nexport interface AccountModalsConfig {\n onGoToSubscription?: () => void;\n}\n\ninterface AccountModalsState {\n activeModal: AccountModalType | null;\n config: AccountModalsConfig;\n initialSection?: AccountSectionType;\n\n /** Called by <AccountModals> to keep app-level config in sync */\n updateConfig: (config: AccountModalsConfig) => void;\n\n openConfigurations: (initialSection?: AccountSectionType) => void;\n openDeleteAccount: () => void;\n openIsntPossibleDelete: () => void;\n openConfirmDelete: () => void;\n close: () => void;\n}\n\nexport const useAccountModals = create<AccountModalsState>((set) => ({\n activeModal: null,\n config: {},\n initialSection: undefined,\n\n updateConfig: (config) => set({ config }),\n\n openConfigurations: (initialSection) =>\n set({ activeModal: 'configurations', initialSection }),\n\n openDeleteAccount: () =>\n set({ activeModal: 'deleteAccount' }),\n\n openIsntPossibleDelete: () =>\n set({ activeModal: 'isntPossibleDelete' }),\n\n openConfirmDelete: () =>\n set({ activeModal: 'confirmDelete' }),\n\n close: () =>\n set({ activeModal: null, initialSection: undefined }),\n}));\n"],"mappings":";AAEA,SAAS,cAAc;AA4BhB,MAAM,mBAAmB,OAA2B,CAAC,SAAS;AAAA,EACnE,aAAa;AAAA,EACb,QAAQ,CAAC;AAAA,EACT,gBAAgB;AAAA,EAEhB,cAAc,CAAC,WAAW,IAAI,EAAE,OAAO,CAAC;AAAA,EAExC,oBAAoB,CAAC,mBACnB,IAAI,EAAE,aAAa,kBAAkB,eAAe,CAAC;AAAA,EAEvD,mBAAmB,MACjB,IAAI,EAAE,aAAa,gBAAgB,CAAC;AAAA,EAEtC,wBAAwB,MACtB,IAAI,EAAE,aAAa,qBAAqB,CAAC;AAAA,EAE3C,mBAAmB,MACjB,IAAI,EAAE,aAAa,gBAAgB,CAAC;AAAA,EAEtC,OAAO,MACL,IAAI,EAAE,aAAa,MAAM,gBAAgB,OAAU,CAAC;AACxD,EAAE;","names":[]}
1
+ {"version":3,"sources":["../../src/store/useAccountModals.ts"],"sourcesContent":["'use client';\n\nimport { create } from 'zustand';\nimport type { AccountSectionType } from '../enums/AccountSectionType';\n\ntype AccountModalType =\n | 'configurations'\n | 'deleteAccount'\n | 'isntPossibleDelete'\n | 'confirmDelete';\n\nexport interface AccountModalsConfig {\n onGoToSubscription?: () => void;\n}\n\ninterface DeleteAccountPayload {\n password: string;\n}\n\ninterface AccountModalsState {\n activeModal: AccountModalType | null;\n config: AccountModalsConfig;\n initialSection?: AccountSectionType;\n\n /** Payload coletado no DeleteAccountModal, consumido pelo ConfirmDeleteAccountModal. */\n deleteAccountPayload: DeleteAccountPayload | null;\n /** Mensagem de erro do backend quando o delete falha (ex.: senha inválida).\n * Mostrada no DeleteAccountModal quando o usuário é mandado de volta. */\n deleteAccountError: string | null;\n\n /** Called by <AccountModals> to keep app-level config in sync */\n updateConfig: (config: AccountModalsConfig) => void;\n\n openConfigurations: (initialSection?: AccountSectionType) => void;\n openDeleteAccount: (error?: string) => void;\n openIsntPossibleDelete: () => void;\n openConfirmDelete: (payload: DeleteAccountPayload) => void;\n clearDeleteAccountError: () => void;\n close: () => void;\n}\n\nexport const useAccountModals = create<AccountModalsState>((set) => ({\n activeModal: null,\n config: {},\n initialSection: undefined,\n deleteAccountPayload: null,\n deleteAccountError: null,\n\n updateConfig: (config) => set({ config }),\n\n openConfigurations: (initialSection) =>\n set({\n activeModal: 'configurations',\n initialSection,\n deleteAccountPayload: null,\n deleteAccountError: null,\n }),\n\n openDeleteAccount: (error) =>\n set({\n activeModal: 'deleteAccount',\n deleteAccountPayload: null,\n deleteAccountError: error ?? null,\n }),\n\n openIsntPossibleDelete: () =>\n set({ activeModal: 'isntPossibleDelete' }),\n\n openConfirmDelete: (payload) =>\n set({\n activeModal: 'confirmDelete',\n deleteAccountPayload: payload,\n deleteAccountError: null,\n }),\n\n clearDeleteAccountError: () => set({ deleteAccountError: null }),\n\n close: () =>\n set({\n activeModal: null,\n initialSection: undefined,\n deleteAccountPayload: null,\n deleteAccountError: null,\n }),\n}));\n"],"mappings":";AAEA,SAAS,cAAc;AAuChB,MAAM,mBAAmB,OAA2B,CAAC,SAAS;AAAA,EACnE,aAAa;AAAA,EACb,QAAQ,CAAC;AAAA,EACT,gBAAgB;AAAA,EAChB,sBAAsB;AAAA,EACtB,oBAAoB;AAAA,EAEpB,cAAc,CAAC,WAAW,IAAI,EAAE,OAAO,CAAC;AAAA,EAExC,oBAAoB,CAAC,mBACnB,IAAI;AAAA,IACF,aAAa;AAAA,IACb;AAAA,IACA,sBAAsB;AAAA,IACtB,oBAAoB;AAAA,EACtB,CAAC;AAAA,EAEH,mBAAmB,CAAC,UAClB,IAAI;AAAA,IACF,aAAa;AAAA,IACb,sBAAsB;AAAA,IACtB,oBAAoB,SAAS;AAAA,EAC/B,CAAC;AAAA,EAEH,wBAAwB,MACtB,IAAI,EAAE,aAAa,qBAAqB,CAAC;AAAA,EAE3C,mBAAmB,CAAC,YAClB,IAAI;AAAA,IACF,aAAa;AAAA,IACb,sBAAsB;AAAA,IACtB,oBAAoB;AAAA,EACtB,CAAC;AAAA,EAEH,yBAAyB,MAAM,IAAI,EAAE,oBAAoB,KAAK,CAAC;AAAA,EAE/D,OAAO,MACL,IAAI;AAAA,IACF,aAAa;AAAA,IACb,gBAAgB;AAAA,IAChB,sBAAsB;AAAA,IACtB,oBAAoB;AAAA,EACtB,CAAC;AACL,EAAE;","names":[]}
@@ -0,0 +1,13 @@
1
+ import "server-only";
2
+ import { requireValidSession } from "../modules/auth/utils/require-valid-session";
3
+ import { safeServerAction } from "./safeServerAction";
4
+ async function safeMutationAction(fn) {
5
+ return safeServerAction(async () => {
6
+ await requireValidSession();
7
+ return fn();
8
+ });
9
+ }
10
+ export {
11
+ safeMutationAction
12
+ };
13
+ //# sourceMappingURL=safeMutationAction.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/utils/safeMutationAction.ts"],"sourcesContent":["import 'server-only';\n\nimport type { ActionResult } from '../infra/api/types';\nimport { requireValidSession } from '../modules/auth/utils/require-valid-session';\nimport { safeServerAction } from './safeServerAction';\n\n/**\n * Wrapper para server actions que mutam estado (delete/update/create/publish/\n * billing/etc.). Faz validação de sessão contra o backend (`POST /auth/keep`)\n * antes de executar a ação e roteia erros via `safeServerAction`.\n *\n * A validação é deduplicada por request — múltiplas actions sensíveis que\n * rodem dentro de uma mesma request pagam 1 round-trip ao backend, não N.\n *\n * Motivação (resumo): o backend confia no par `(whitelabel-token, id_account\n * no path)` nos endpoints de CRUD; o Next.js deriva `id_account` de\n * `atob(cookie)` sem verificar assinatura. Chamar `requireValidSession`\n * aqui garante que o cookie corresponde a uma sessão real no banco antes\n * de qualquer mutação.\n *\n * Para reads não-sensíveis, continue usando `safeServerAction` direto.\n */\nexport async function safeMutationAction<T>(\n fn: () => Promise<T>\n): Promise<ActionResult<T>> {\n return safeServerAction(async () => {\n await requireValidSession();\n return fn();\n });\n}\n"],"mappings":"AAAA,OAAO;AAGP,SAAS,2BAA2B;AACpC,SAAS,wBAAwB;AAkBjC,eAAsB,mBACpB,IAC0B;AAC1B,SAAO,iBAAiB,YAAY;AAClC,UAAM,oBAAoB;AAC1B,WAAO,GAAG;AAAA,EACZ,CAAC;AACH;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@greatapps/common",
3
- "version": "1.1.542",
3
+ "version": "1.1.546",
4
4
  "description": "Shared library for GreatApps frontend applications",
5
5
  "main": "./dist/index.mjs",
6
6
  "types": "./src/index.ts",
@@ -36,6 +36,7 @@ export default function ConfigurationsMyAccountModal() {
36
36
  const isBlockingSubscription =
37
37
  isPaidSubscriptionType &&
38
38
  subscription.active === true &&
39
+ !subscription.date_cancellation &&
39
40
  !hasSubscriptionExpired(subscription.date_due);
40
41
 
41
42
  const { user } = useAuth();
@@ -15,7 +15,7 @@ import { useAuth } from '../../providers/auth.provider';
15
15
  import { UserProfile } from '../../modules/users/schema';
16
16
 
17
17
  export default function ConfirmDeleteAccountModal() {
18
- const { activeModal, close } = useAccountModals();
18
+ const { activeModal, deleteAccountPayload, openDeleteAccount, close } = useAccountModals();
19
19
  const open = activeModal === 'confirmDelete';
20
20
 
21
21
  const { data: pagesCount = 0 } = useCountPages();
@@ -30,7 +30,13 @@ export default function ConfirmDeleteAccountModal() {
30
30
  const handleConfirm = async () => {
31
31
  try {
32
32
  if (isOwner) {
33
- await deleteAccount.mutateAsync();
33
+ if (!deleteAccountPayload) {
34
+ // Fluxo quebrado (usuário pulou o primeiro modal ou refresh) — manda
35
+ // de volta pro modal inicial pra re-coletar a senha.
36
+ openDeleteAccount('Sessão expirada. Preencha os campos novamente.');
37
+ return;
38
+ }
39
+ await deleteAccount.mutateAsync({ password: deleteAccountPayload.password });
34
40
  } else {
35
41
  await deleteAccountUser.mutateAsync(user!.id);
36
42
  }
@@ -40,12 +46,15 @@ export default function ConfirmDeleteAccountModal() {
40
46
  ));
41
47
  await logout();
42
48
  } catch (error) {
49
+ const message = error instanceof Error ? error.message : 'Erro ao excluir. Tente novamente.';
50
+ if (isOwner) {
51
+ // Falha no delete do owner (ex.: senha inválida) — volta pro primeiro
52
+ // modal com a mensagem do backend pra o usuário corrigir e tentar de novo.
53
+ openDeleteAccount(message);
54
+ return;
55
+ }
43
56
  toast.custom((t) => (
44
- <Toast
45
- variant="error"
46
- message={error instanceof Error ? error.message : 'Erro ao excluir. Tente novamente.'}
47
- toastId={t}
48
- />
57
+ <Toast variant="error" message={message} toastId={t} />
49
58
  ));
50
59
  }
51
60
  };
@@ -1,6 +1,6 @@
1
1
  'use client';
2
2
 
3
- import { useState, useCallback } from 'react';
3
+ import { useState, useCallback, useEffect } from 'react';
4
4
  import { IconLock, IconAlertTriangle } from '@tabler/icons-react';
5
5
  import { toast } from 'sonner';
6
6
  import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from '../ui/overlay/Dialog';
@@ -23,7 +23,13 @@ const REASON_OPTIONS = [
23
23
  ];
24
24
 
25
25
  export default function DeleteAccountModal() {
26
- const { activeModal, openConfigurations, openConfirmDelete, close } = useAccountModals();
26
+ const {
27
+ activeModal,
28
+ deleteAccountError,
29
+ openConfigurations,
30
+ openConfirmDelete,
31
+ clearDeleteAccountError,
32
+ } = useAccountModals();
27
33
  const open = activeModal === 'deleteAccount';
28
34
 
29
35
  const [reason, setReason] = useState('');
@@ -39,6 +45,14 @@ export default function DeleteAccountModal() {
39
45
  setPassword('');
40
46
  }, []);
41
47
 
48
+ // Quando o modal deixa de estar aberto, limpa o erro residual do store
49
+ // pra não aparecer numa próxima abertura vinda de fluxo limpo.
50
+ useEffect(() => {
51
+ if (!open) {
52
+ clearDeleteAccountError();
53
+ }
54
+ }, [open, clearDeleteAccountError]);
55
+
42
56
  const handleClose = () => {
43
57
  resetForm();
44
58
  openConfigurations(AccountSectionType.SECURITY);
@@ -52,8 +66,9 @@ export default function DeleteAccountModal() {
52
66
  ));
53
67
  return;
54
68
  }
69
+ openConfirmDelete({ password });
70
+ // Campos locais podem ser limpos — o payload já está no store.
55
71
  resetForm();
56
- openConfirmDelete();
57
72
  };
58
73
 
59
74
  return (
@@ -104,10 +119,15 @@ export default function DeleteAccountModal() {
104
119
  placeholder="Informe sua senha"
105
120
  leftIcon={IconLock}
106
121
  value={password}
107
- onChange={(e) => setPassword(e.target.value)}
122
+ onChange={(e) => {
123
+ setPassword(e.target.value);
124
+ if (deleteAccountError) clearDeleteAccountError();
125
+ }}
108
126
  classnameContainer="h-10!"
109
127
  showPassword={showPassword}
110
128
  onTogglePassword={togglePassword}
129
+ error={!!deleteAccountError}
130
+ errorMessage={deleteAccountError ?? undefined}
111
131
  />
112
132
  </div>
113
133
 
@@ -10,18 +10,12 @@ import { ACCEPTED_IMAGE_FORMATS_STRING } from '../../../modules/images/constants
10
10
  import { Button } from '../../ui/buttons/Button';
11
11
  import { Separator } from '../../ui/data-display/Separator';
12
12
  import { Toast } from '../../ui/feedback/Toast';
13
- import { Dialog, DialogContent, DialogHeader, DialogTitle } from '../../ui/overlay/Dialog';
14
13
  import { FormField } from '../../ui/form/FormField';
15
14
  import { SelectField } from '../../ui/form/SelectField';
16
15
  import PhoneInput from '../../ui/form/PhoneInput';
17
16
  import { useAuth } from '../../../providers/auth.provider';
18
- import { UserProfile } from '../../../modules/users/schema';
19
17
  import { myProfileSchema, type MyProfileFormData } from '../../../utils/validators/account';
20
- import {
21
- useUpdateAccountUser,
22
- useDeleteAccount,
23
- useDeleteAccountUser,
24
- } from '../../../modules/accounts/hooks/useAccountManagement';
18
+ import { useUpdateAccountUser } from '../../../modules/accounts/hooks/useAccountManagement';
25
19
  import { GENDER_OPTIONS } from '../constants';
26
20
  import { ChangePhoneModal } from './ChangePhoneModal';
27
21
  import { ChangeEmailModal } from './ChangeEmailModal';
@@ -32,19 +26,13 @@ interface MyProfileSectionProps {
32
26
  }
33
27
 
34
28
  export function MyProfileSection({ onClose, storageUrl }: MyProfileSectionProps) {
35
- const { user, logout } = useAuth();
29
+ const { user } = useAuth();
36
30
  const updateAccountUser = useUpdateAccountUser();
37
- const deleteAccount = useDeleteAccount();
38
- const deleteAccountUser = useDeleteAccountUser();
39
31
  const fileInputRef = useRef<HTMLInputElement>(null);
40
32
  const [selectedPhoto, setSelectedPhoto] = useState<File | null>(null);
41
33
  const [photoRemoved, setPhotoRemoved] = useState(false);
42
34
  const [changePhoneOpen, setChangePhoneOpen] = useState(false);
43
35
  const [changeEmailOpen, setChangeEmailOpen] = useState(false);
44
- const [deleteConfirmOpen, setDeleteConfirmOpen] = useState(false);
45
-
46
- const isOwner = user?.profile === UserProfile.owner;
47
- const isDeleting = deleteAccount.isPending || deleteAccountUser.isPending;
48
36
 
49
37
  const form = useForm<MyProfileFormData>({
50
38
  resolver: zodResolver(myProfileSchema),
@@ -85,26 +73,6 @@ export function MyProfileSection({ onClose, storageUrl }: MyProfileSectionProps)
85
73
  setPhotoRemoved(true);
86
74
  };
87
75
 
88
- const handleDelete = async () => {
89
- try {
90
- if (isOwner) {
91
- await deleteAccount.mutateAsync();
92
- } else {
93
- await deleteAccountUser.mutateAsync(user!.id);
94
- }
95
- await logout();
96
- } catch (error) {
97
- toast.custom((t) => (
98
- <Toast
99
- variant="error"
100
- message={error instanceof Error ? error.message : 'Erro ao excluir. Tente novamente.'}
101
- toastId={t}
102
- />
103
- ));
104
- setDeleteConfirmOpen(false);
105
- }
106
- };
107
-
108
76
  const onSubmit = async (data: MyProfileFormData) => {
109
77
  try {
110
78
  await updateAccountUser.mutateAsync({
@@ -289,40 +257,6 @@ export function MyProfileSection({ onClose, storageUrl }: MyProfileSectionProps)
289
257
 
290
258
  <ChangePhoneModal open={changePhoneOpen} onClose={() => setChangePhoneOpen(false)} />
291
259
  <ChangeEmailModal open={changeEmailOpen} onClose={() => setChangeEmailOpen(false)} />
292
-
293
- <Dialog open={deleteConfirmOpen} onOpenChange={setDeleteConfirmOpen}>
294
- <DialogContent className="max-w-sm! gap-5">
295
- <DialogHeader>
296
- <DialogTitle className="paragraph-large-semibold text-gray-950">
297
- {isOwner ? 'Excluir conta' : 'Excluir usuário'}
298
- </DialogTitle>
299
- </DialogHeader>
300
- <p className="paragraph-small-regular text-gray-600">
301
- {isOwner
302
- ? 'Tem certeza que deseja excluir sua conta? Todos os dados serão apagados permanentemente e essa ação não pode ser desfeita.'
303
- : 'Tem certeza que deseja excluir seu usuário? Você perderá acesso a esta conta permanentemente.'}
304
- </p>
305
- <div className="flex items-center gap-3 justify-end">
306
- <Button
307
- type="button"
308
- variant="secondary"
309
- className="h-10!"
310
- disabled={isDeleting}
311
- onClick={() => setDeleteConfirmOpen(false)}
312
- >
313
- Cancelar
314
- </Button>
315
- <Button
316
- type="button"
317
- className="h-10! bg-red-600 hover:bg-red-700 text-white border-0!"
318
- disabled={isDeleting}
319
- onClick={handleDelete}
320
- >
321
- {isDeleting ? 'Excluindo...' : 'Confirmar exclusão'}
322
- </Button>
323
- </div>
324
- </DialogContent>
325
- </Dialog>
326
260
  </>
327
261
  );
328
262
  }
@@ -20,16 +20,19 @@ export function CancelledSubscriptionBanner({ onReactivate }: CancelledSubscript
20
20
  }, []);
21
21
 
22
22
  const dismiss = useCallback(() => setVisible(false), []);
23
- const dueDate = new Date(subscription?.date_due);
23
+
24
+ if (!visible) return null;
25
+ if (!subscription?.date_due) return null;
26
+
27
+ const dueDate = new Date(subscription.date_due);
24
28
  const today = new Date();
25
29
  const isPastDue = today >= dueDate;
26
30
  const formattedDate = formatShortDate(dueDate);
27
31
 
28
- const isTrial = subscription?.type === 'trial';
32
+ const isTrial = subscription.type === 'trial';
29
33
 
30
- if (!visible) return null;
31
34
  if (isTrial && !isPastDue) return null;
32
- if (!isTrial && (!subscription?.date_cancellation || isPastDue)) return null;
35
+ if (!isTrial && (!subscription.date_cancellation || isPastDue)) return null;
33
36
 
34
37
  return (
35
38
  <div className="fixed top-[80px] md:top-0 left-0 right-0 z-[60] flex justify-center pointer-events-none px-4 lg:px-0">
@@ -1,11 +1,13 @@
1
1
  'use server';
2
2
 
3
3
  import { safeServerAction } from '../../../utils/safeServerAction';
4
+ import { safeMutationAction } from '../../../utils/safeMutationAction';
4
5
  import { accountService } from '../services/account.service';
5
6
  import type {
6
7
  AccountUsersPaginationParams,
7
8
  ChangePasswordRequest,
8
9
  CreateAccountUserRequest,
10
+ DeleteAccountRequest,
9
11
  UpdateAccountRequest,
10
12
  UpdateAccountUserRequest,
11
13
  } from '../types';
@@ -22,18 +24,18 @@ export async function listAccountUsersAction(params?: AccountUsersPaginationPara
22
24
  }
23
25
 
24
26
  export async function updateUserAction(user: UpdateAccountUserRequest) {
25
- return safeServerAction(() => accountService.updateAccountUser(user));
27
+ return safeMutationAction(() => accountService.updateAccountUser(user));
26
28
  }
27
29
 
28
30
  export async function updateAccountUserByIdAction(
29
31
  userId: number,
30
32
  user: UpdateAccountUserRequest
31
33
  ) {
32
- return safeServerAction(() => accountService.updateAccountUserById(userId, user));
34
+ return safeMutationAction(() => accountService.updateAccountUserById(userId, user));
33
35
  }
34
36
 
35
37
  export async function updateAccountAction(data: UpdateAccountRequest) {
36
- return safeServerAction(async () => {
38
+ return safeMutationAction(async () => {
37
39
  if (data.name !== undefined) {
38
40
  const userResult = await findUserById();
39
41
  if (!userResult.success || userResult.data?.profile !== UserProfile.owner) {
@@ -50,39 +52,39 @@ export async function updateAccountAction(data: UpdateAccountRequest) {
50
52
  }
51
53
 
52
54
  export async function deleteAccountUserAction(userId: number) {
53
- return safeServerAction(() => accountService.deleteAccountUser(userId));
55
+ return safeMutationAction(() => accountService.deleteAccountUser(userId));
54
56
  }
55
57
 
56
- export async function deleteAccountAction() {
57
- return safeServerAction(() => accountService.deleteAccount());
58
+ export async function deleteAccountAction(data: DeleteAccountRequest) {
59
+ return safeMutationAction(() => accountService.deleteAccount(data));
58
60
  }
59
61
 
60
62
  export async function createAccountUserAction(user: CreateAccountUserRequest) {
61
- return safeServerAction(() => accountService.createAccountUser(user));
63
+ return safeMutationAction(() => accountService.createAccountUser(user));
62
64
  }
63
65
 
64
66
  export async function changePasswordAction(data: ChangePasswordRequest) {
65
- return safeServerAction(() => accountService.changePassword(data));
67
+ return safeMutationAction(() => accountService.changePassword(data));
66
68
  }
67
69
 
68
70
  export async function requestEmailChangeAction(newEmail: string) {
69
- return safeServerAction(() => accountService.requestContactReset('email', newEmail));
71
+ return safeMutationAction(() => accountService.requestContactReset('email', newEmail));
70
72
  }
71
73
 
72
74
  export async function confirmEmailChangeAction(token: string, newEmail: string) {
73
- return safeServerAction(() => accountService.confirmContactReset('email', token, newEmail));
75
+ return safeMutationAction(() => accountService.confirmContactReset('email', token, newEmail));
74
76
  }
75
77
 
76
78
  export async function requestPhoneChangeAction(newPhone: string, ddi?: string) {
77
- return safeServerAction(() => accountService.requestContactReset('phone', newPhone, ddi));
79
+ return safeMutationAction(() => accountService.requestContactReset('phone', newPhone, ddi));
78
80
  }
79
81
 
80
82
  export async function confirmPhoneChangeAction(token: string, newPhone: string, ddi?: string) {
81
- return safeServerAction(() => accountService.confirmContactReset('phone', token, newPhone, ddi));
83
+ return safeMutationAction(() => accountService.confirmContactReset('phone', token, newPhone, ddi));
82
84
  }
83
85
 
84
86
  export async function generateTwoFactorAction() {
85
- return safeServerAction(async () => {
87
+ return safeMutationAction(async () => {
86
88
  const result = await twoFactorService.generate();
87
89
  return {
88
90
  qrcode: result.data.qrcode,
@@ -92,14 +94,14 @@ export async function generateTwoFactorAction() {
92
94
  }
93
95
 
94
96
  export async function confirmTwoFactorAction(code: string) {
95
- return safeServerAction(async () => {
97
+ return safeMutationAction(async () => {
96
98
  await twoFactorService.confirm(code);
97
99
  await accountService.updateAccountUser({ two_factor_authentication: true });
98
100
  });
99
101
  }
100
102
 
101
103
  export async function disableTwoFactorAction(code: string) {
102
- return safeServerAction(async () => {
104
+ return safeMutationAction(async () => {
103
105
  await twoFactorService.verify(code);
104
106
  await twoFactorService.disable();
105
107
  await accountService.updateAccountUser({ two_factor_authentication: false });
@@ -7,6 +7,7 @@ import type {
7
7
  UpdateAccountRequest,
8
8
  UpdateAccountUserRequest,
9
9
  DeleteAccountUserResponse,
10
+ DeleteAccountRequest,
10
11
  DeleteAccountResponse,
11
12
  } from '../types';
12
13
  import {
@@ -85,7 +86,7 @@ export function useDeleteAccountUser(
85
86
  }
86
87
 
87
88
  export function useDeleteAccount(
88
- options?: UseMutationOptions<DeleteAccountResponse, Error, void>
89
+ options?: UseMutationOptions<DeleteAccountResponse, Error, DeleteAccountRequest>
89
90
  ) {
90
91
  return useMutation({
91
92
  mutationFn: withAction(deleteAccountAction),
@@ -15,6 +15,7 @@ import type {
15
15
  ChangeUserPhotoResponse,
16
16
  CreateAccountUserRequest,
17
17
  CreateAccountUserResponse,
18
+ DeleteAccountRequest,
18
19
  DeleteAccountResponse,
19
20
  DeleteAccountUserResponse,
20
21
  GetAccountDataResponse,
@@ -94,9 +95,12 @@ class AccountService {
94
95
  return response;
95
96
  }
96
97
 
97
- async deleteAccount(): Promise<DeleteAccountResponse> {
98
+ async deleteAccount(data: DeleteAccountRequest): Promise<DeleteAccountResponse> {
98
99
  const { id_account } = await getUserContext();
99
- const response = await api.apps.delete<DeleteAccountResponse>(`/accounts/${id_account}`);
100
+ const response = await api.apps.delete<DeleteAccountResponse>(
101
+ `/accounts/${id_account}`,
102
+ { body: JSON.stringify(data) }
103
+ );
100
104
 
101
105
  if (response.status === 0) {
102
106
  throw new ApiError(response.message || 'Erro ao deletar conta', 'DELETE_ACCOUNT_FAILED', 400);
@@ -127,6 +127,10 @@ export interface DeleteAccountUserResponse {
127
127
  message?: string;
128
128
  }
129
129
 
130
+ export interface DeleteAccountRequest {
131
+ password: string;
132
+ }
133
+
130
134
  export interface DeleteAccountResponse {
131
135
  status: 0 | 1;
132
136
  message?: string;
@@ -25,7 +25,7 @@ export function useUserQuery() {
25
25
  return useQuery({
26
26
  queryKey,
27
27
  queryFn: fetchUser,
28
- staleTime: 5 * 60 * 1000,
28
+ staleTime: 60 * 1000,
29
29
  gcTime: 10 * 60 * 1000,
30
30
  retry: false,
31
31
  });
@@ -0,0 +1,35 @@
1
+ import 'server-only';
2
+
3
+ import { cache } from 'react';
4
+
5
+ import { ApiError } from '../../../infra/api/types';
6
+ import { getClientInfoFromRequest } from '../../../infra/utils/client-info';
7
+ import { authService } from '../services/auth.service';
8
+
9
+ /**
10
+ * Valida a sessão do usuário chamando POST /auth/keep no backend.
11
+ *
12
+ * Garante que o cookie `greatapps`:
13
+ * 1. Existe.
14
+ * 2. Corresponde a uma sessão registrada no banco (não foi forjado).
15
+ * 3. Ainda é considerada válida pelo backend (não foi revogada/expirada).
16
+ *
17
+ * Deduplicada por request via React.cache — múltiplas chamadas dentro do
18
+ * mesmo server action resultam em 1 round-trip ao backend, não N.
19
+ *
20
+ * Use em server actions sensíveis (mutações, billing, destrutivas).
21
+ * Prefira o wrapper `safeMutationAction` que já envelopa isto junto com
22
+ * o `safeServerAction` padrão.
23
+ *
24
+ * Em caso de erro de rede/backend, falha fechada (SESSION_INVALID 401).
25
+ */
26
+ export const requireValidSession = cache(async (): Promise<void> => {
27
+ if (process.env.DUMMY_AUTH_TOKEN) return;
28
+
29
+ const clientInfo = await getClientInfoFromRequest();
30
+ const isValid = await authService.validateSession(clientInfo);
31
+
32
+ if (!isValid) {
33
+ throw new ApiError('Sessão inválida ou expirada', 'SESSION_INVALID', 401);
34
+ }
35
+ });
@@ -1,9 +1,9 @@
1
1
  'use server';
2
2
 
3
- import { safeServerAction } from '../../../utils/safeServerAction';
3
+ import { safeMutationAction } from '../../../utils/safeMutationAction';
4
4
  import { cardsService } from '../services/cards.service';
5
5
  import { CreateCardRequest } from '../types';
6
6
 
7
7
  export async function createCardAction(data: CreateCardRequest) {
8
- return safeServerAction(() => cardsService.createCard(data));
8
+ return safeMutationAction(() => cardsService.createCard(data));
9
9
  }
@@ -1,8 +1,8 @@
1
1
  'use server';
2
2
 
3
- import { safeServerAction } from '../../../utils/safeServerAction';
3
+ import { safeMutationAction } from '../../../utils/safeMutationAction';
4
4
  import { cardsService } from '../services/cards.service';
5
5
 
6
6
  export async function deleteCardAction(id: string) {
7
- return safeServerAction(() => cardsService.deleteCard(id));
7
+ return safeMutationAction(() => cardsService.deleteCard(id));
8
8
  }
@@ -1,8 +1,8 @@
1
1
  'use server';
2
2
 
3
- import { safeServerAction } from '../../../utils/safeServerAction';
3
+ import { safeMutationAction } from '../../../utils/safeMutationAction';
4
4
  import { cardsService } from '../services/cards.service';
5
5
 
6
6
  export async function setDefaultCardAction(cardId: string) {
7
- return safeServerAction(() => cardsService.setDefaultCard(cardId));
7
+ return safeMutationAction(() => cardsService.setDefaultCard(cardId));
8
8
  }