@greatapps/common 1.1.782 → 1.1.784

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 (79) hide show
  1. package/dist/components/layouts/NavBar.mjs +2 -0
  2. package/dist/components/layouts/NavBar.mjs.map +1 -1
  3. package/dist/components/ui/form/PhoneInput.mjs +1 -1
  4. package/dist/components/ui/form/PhoneInput.mjs.map +1 -1
  5. package/dist/handlers.mjs +2 -0
  6. package/dist/handlers.mjs.map +1 -1
  7. package/dist/index.mjs +9 -1
  8. package/dist/index.mjs.map +1 -1
  9. package/dist/infra/api/client.mjs +5 -1
  10. package/dist/infra/api/client.mjs.map +1 -1
  11. package/dist/infra/api/types.mjs +1 -1
  12. package/dist/infra/api/types.mjs.map +1 -1
  13. package/dist/infra/route/safe-route-handler.mjs +4 -1
  14. package/dist/infra/route/safe-route-handler.mjs.map +1 -1
  15. package/dist/modules/auth/utils/assert-account-not-frozen.mjs +6 -5
  16. package/dist/modules/auth/utils/assert-account-not-frozen.mjs.map +1 -1
  17. package/dist/modules/auth/utils/assert-management-subscription.mjs +7 -14
  18. package/dist/modules/auth/utils/assert-management-subscription.mjs.map +1 -1
  19. package/dist/modules/auth/utils/assert-paid-subscription.mjs +7 -12
  20. package/dist/modules/auth/utils/assert-paid-subscription.mjs.map +1 -1
  21. package/dist/modules/auth/utils/assert-subscription-addon.mjs +2 -10
  22. package/dist/modules/auth/utils/assert-subscription-addon.mjs.map +1 -1
  23. package/dist/modules/plans/hooks/use-plan-by-id.hook.mjs.map +1 -1
  24. package/dist/modules/subscriptions/actions/get-current-subscription.action.mjs +10 -0
  25. package/dist/modules/subscriptions/actions/get-current-subscription.action.mjs.map +1 -0
  26. package/dist/modules/subscriptions/constants/subscription.constants.mjs +2 -0
  27. package/dist/modules/subscriptions/constants/subscription.constants.mjs.map +1 -1
  28. package/dist/modules/subscriptions/handlers/current-subscription.handler.mjs +17 -0
  29. package/dist/modules/subscriptions/handlers/current-subscription.handler.mjs.map +1 -0
  30. package/dist/modules/subscriptions/hooks/find-active-subscription.hook.mjs +2 -13
  31. package/dist/modules/subscriptions/hooks/find-active-subscription.hook.mjs.map +1 -1
  32. package/dist/modules/subscriptions/hooks/use-current-subscription.hook.mjs +28 -0
  33. package/dist/modules/subscriptions/hooks/use-current-subscription.hook.mjs.map +1 -0
  34. package/dist/modules/subscriptions/services/subscriptions.service.mjs +26 -0
  35. package/dist/modules/subscriptions/services/subscriptions.service.mjs.map +1 -1
  36. package/dist/modules/subscriptions/types/subscription.type.mjs.map +1 -1
  37. package/dist/modules/subscriptions/utils/group-subscriptions-by-product.mjs +16 -0
  38. package/dist/modules/subscriptions/utils/group-subscriptions-by-product.mjs.map +1 -0
  39. package/dist/modules/subscriptions/utils/has-active-paid-subscription.mjs +1 -1
  40. package/dist/modules/subscriptions/utils/has-active-paid-subscription.mjs.map +1 -1
  41. package/dist/modules/subscriptions/utils/has-paid-subscription.mjs +1 -1
  42. package/dist/modules/subscriptions/utils/has-paid-subscription.mjs.map +1 -1
  43. package/dist/modules/subscriptions/utils/read-current-subscription.mjs +21 -0
  44. package/dist/modules/subscriptions/utils/read-current-subscription.mjs.map +1 -0
  45. package/dist/server.mjs +4 -0
  46. package/dist/server.mjs.map +1 -1
  47. package/dist/store/useMdSidebarStore.mjs +17 -4
  48. package/dist/store/useMdSidebarStore.mjs.map +1 -1
  49. package/dist/utils/safeServerAction.mjs +4 -1
  50. package/dist/utils/safeServerAction.mjs.map +1 -1
  51. package/dist/utils/withAction.mjs +2 -1
  52. package/dist/utils/withAction.mjs.map +1 -1
  53. package/package.json +1 -1
  54. package/src/components/layouts/NavBar.tsx +2 -0
  55. package/src/components/ui/form/PhoneInput.tsx +1 -1
  56. package/src/handlers.ts +1 -0
  57. package/src/index.ts +7 -1
  58. package/src/infra/api/client.ts +11 -3
  59. package/src/infra/api/types.ts +7 -5
  60. package/src/infra/route/safe-route-handler.ts +4 -1
  61. package/src/modules/auth/utils/assert-account-not-frozen.ts +7 -6
  62. package/src/modules/auth/utils/assert-management-subscription.ts +15 -23
  63. package/src/modules/auth/utils/assert-paid-subscription.ts +7 -15
  64. package/src/modules/auth/utils/assert-subscription-addon.ts +2 -12
  65. package/src/modules/subscriptions/actions/get-current-subscription.action.ts +8 -0
  66. package/src/modules/subscriptions/constants/subscription.constants.ts +3 -0
  67. package/src/modules/subscriptions/handlers/current-subscription.handler.ts +16 -0
  68. package/src/modules/subscriptions/hooks/find-active-subscription.hook.ts +7 -17
  69. package/src/modules/subscriptions/hooks/use-current-subscription.hook.ts +38 -0
  70. package/src/modules/subscriptions/services/subscriptions.service.ts +34 -0
  71. package/src/modules/subscriptions/types/subscription.type.ts +2 -0
  72. package/src/modules/subscriptions/utils/group-subscriptions-by-product.ts +23 -0
  73. package/src/modules/subscriptions/utils/has-active-paid-subscription.ts +1 -2
  74. package/src/modules/subscriptions/utils/has-paid-subscription.ts +1 -2
  75. package/src/modules/subscriptions/utils/read-current-subscription.ts +34 -0
  76. package/src/server.ts +2 -0
  77. package/src/store/useMdSidebarStore.ts +20 -5
  78. package/src/utils/safeServerAction.ts +4 -6
  79. package/src/utils/withAction.ts +2 -1
package/dist/server.mjs CHANGED
@@ -22,6 +22,8 @@ import { findUserById } from "./modules/users/action/find-user-by-id.action";
22
22
  import { findCurrentAccount } from "./modules/accounts/actions/find-current-account.action";
23
23
  import { getAccountTokenAction } from "./modules/accounts/actions/get-account-token.action";
24
24
  import { listSubscriptionsAction } from "./modules/subscriptions/actions/list-subscriptions.action";
25
+ import { getCurrentSubscriptionAction } from "./modules/subscriptions/actions/get-current-subscription.action";
26
+ import { readCurrentSubscription } from "./modules/subscriptions/utils/read-current-subscription";
25
27
  import { ackFreezeNoticeAction } from "./modules/subscriptions/actions/ack-freeze-notice.action";
26
28
  import { calculateSubscriptionAction } from "./modules/subscriptions/actions/calculate-subscription.action";
27
29
  import { updateSubscriptionPlanAction } from "./modules/subscriptions/actions/update-subscription-plan.action";
@@ -110,6 +112,7 @@ export {
110
112
  generateTwoFactorAction,
111
113
  getAccountTokenAction,
112
114
  getClientInfoFromRequest,
115
+ getCurrentSubscriptionAction,
113
116
  getUserContext,
114
117
  hasActivePaidSubscription,
115
118
  hasSubscriptionExpired,
@@ -123,6 +126,7 @@ export {
123
126
  processImageAction,
124
127
  projectUsersService,
125
128
  projectsService,
129
+ readCurrentSubscription,
126
130
  removeProjectUserAction,
127
131
  requestEmailChangeAction,
128
132
  requestPhoneChangeAction,
@@ -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 { assertAccountNotFrozen } from './modules/auth/utils/assert-account-not-frozen';\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 { chargesService } from './modules/charges/services/charges.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 { socialGoogleLoginAction } from './modules/auth/actions/social-google-login.action';\r\nexport { socialGoogleOnboardingAction } from './modules/auth/actions/social-google-onboarding.action';\r\nexport { unlinkGoogleAction } from './modules/auth/actions/unlink-google.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 { ackFreezeNoticeAction } from './modules/subscriptions/actions/ack-freeze-notice.action';\r\nexport { calculateSubscriptionAction } from './modules/subscriptions/actions/calculate-subscription.action';\r\nexport { updateSubscriptionPlanAction } from './modules/subscriptions/actions/update-subscription-plan.action';\r\nexport { updateSubscriptionPaymentAction } from './modules/subscriptions/actions/update-subscription-payment.action';\r\nexport { createCardAction } from './modules/cards/actions/create-card.action';\r\nexport { createSetupIntentAction } from './modules/cards/actions/create-setup-intent.action';\r\nexport { chargeActionAction } from './modules/charges/actions/charge-action.action';\r\nexport { resolvePlanExtrasPrices } from './modules/subscriptions/utils/resolve-plan-extras-prices';\r\nexport { hasSubscriptionExpired } from './modules/subscriptions/utils/has-subscription-expired';\r\nexport { hasActivePaidSubscription } from './modules/subscriptions/utils/has-active-paid-subscription';\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 { 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 updateBillingDataAction,\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 { resolveLocale } from './i18n/resolve-locale';\r\nexport type { ResolveLocaleOptions } from './i18n/resolve-locale';\r\nexport { requireValidSession } from './modules/auth/utils/require-valid-session';\r\nexport { requireTokenNotExpired } from './modules/auth/utils/require-token-not-expired';\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,8BAA8B;AACvC,SAAS,4BAA4B;AACrC,SAAS,oBAAoB;AAC7B,SAAS,oBAAoB;AAC7B,SAAS,sBAAsB;AAC/B,SAAS,wBAAwB;AAGjC,SAAS,sBAAsB;AAC/B,SAAS,6BAA6B;AACtC,SAAS,+BAA+B;AACxC,SAAS,oCAAoC;AAC7C,SAAS,0BAA0B;AACnC,SAAS,oBAAoB;AAC7B,SAAS,0BAA0B;AACnC,SAAS,6BAA6B;AACtC,SAAS,+BAA+B;AACxC,SAAS,6BAA6B;AACtC,SAAS,mCAAmC;AAC5C,SAAS,oCAAoC;AAC7C,SAAS,uCAAuC;AAChD,SAAS,wBAAwB;AACjC,SAAS,+BAA+B;AACxC,SAAS,0BAA0B;AACnC,SAAS,+BAA+B;AACxC,SAAS,8BAA8B;AACvC,SAAS,iCAAiC;AAG1C,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,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,EACA;AAAA,OACK;AAGP,SAAS,gCAAgC;AACzC,SAAS,sBAAsB;AAC/B,SAAS,qBAAqB;AAE9B,SAAS,2BAA2B;AACpC,SAAS,8BAA8B;AACvC,SAAS,iBAAiB,8BAA8B;AACxD,SAAS,wBAAwB;AACjC,SAAS,0BAA0B;AAGnC,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 { assertAccountNotFrozen } from './modules/auth/utils/assert-account-not-frozen';\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 { chargesService } from './modules/charges/services/charges.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 { socialGoogleLoginAction } from './modules/auth/actions/social-google-login.action';\r\nexport { socialGoogleOnboardingAction } from './modules/auth/actions/social-google-onboarding.action';\r\nexport { unlinkGoogleAction } from './modules/auth/actions/unlink-google.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 { getCurrentSubscriptionAction } from './modules/subscriptions/actions/get-current-subscription.action';\r\nexport { readCurrentSubscription } from './modules/subscriptions/utils/read-current-subscription';\r\nexport { ackFreezeNoticeAction } from './modules/subscriptions/actions/ack-freeze-notice.action';\r\nexport { calculateSubscriptionAction } from './modules/subscriptions/actions/calculate-subscription.action';\r\nexport { updateSubscriptionPlanAction } from './modules/subscriptions/actions/update-subscription-plan.action';\r\nexport { updateSubscriptionPaymentAction } from './modules/subscriptions/actions/update-subscription-payment.action';\r\nexport { createCardAction } from './modules/cards/actions/create-card.action';\r\nexport { createSetupIntentAction } from './modules/cards/actions/create-setup-intent.action';\r\nexport { chargeActionAction } from './modules/charges/actions/charge-action.action';\r\nexport { resolvePlanExtrasPrices } from './modules/subscriptions/utils/resolve-plan-extras-prices';\r\nexport { hasSubscriptionExpired } from './modules/subscriptions/utils/has-subscription-expired';\r\nexport { hasActivePaidSubscription } from './modules/subscriptions/utils/has-active-paid-subscription';\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 { 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 updateBillingDataAction,\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 { resolveLocale } from './i18n/resolve-locale';\r\nexport type { ResolveLocaleOptions } from './i18n/resolve-locale';\r\nexport { requireValidSession } from './modules/auth/utils/require-valid-session';\r\nexport { requireTokenNotExpired } from './modules/auth/utils/require-token-not-expired';\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,8BAA8B;AACvC,SAAS,4BAA4B;AACrC,SAAS,oBAAoB;AAC7B,SAAS,oBAAoB;AAC7B,SAAS,sBAAsB;AAC/B,SAAS,wBAAwB;AAGjC,SAAS,sBAAsB;AAC/B,SAAS,6BAA6B;AACtC,SAAS,+BAA+B;AACxC,SAAS,oCAAoC;AAC7C,SAAS,0BAA0B;AACnC,SAAS,oBAAoB;AAC7B,SAAS,0BAA0B;AACnC,SAAS,6BAA6B;AACtC,SAAS,+BAA+B;AACxC,SAAS,oCAAoC;AAC7C,SAAS,+BAA+B;AACxC,SAAS,6BAA6B;AACtC,SAAS,mCAAmC;AAC5C,SAAS,oCAAoC;AAC7C,SAAS,uCAAuC;AAChD,SAAS,wBAAwB;AACjC,SAAS,+BAA+B;AACxC,SAAS,0BAA0B;AACnC,SAAS,+BAA+B;AACxC,SAAS,8BAA8B;AACvC,SAAS,iCAAiC;AAG1C,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,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,EACA;AAAA,OACK;AAGP,SAAS,gCAAgC;AACzC,SAAS,sBAAsB;AAC/B,SAAS,qBAAqB;AAE9B,SAAS,2BAA2B;AACpC,SAAS,8BAA8B;AACvC,SAAS,iBAAiB,8BAA8B;AACxD,SAAS,wBAAwB;AACjC,SAAS,0BAA0B;AAGnC,SAAS,0BAA0B;AACnC,SAAS,oBAAoB;","names":[]}
@@ -1,11 +1,10 @@
1
1
  import { create } from "zustand";
2
+ import { useEffect } from "react";
2
3
  import { readStorage, writeStorage } from "../utils/browser/safe-storage";
3
4
  const STORAGE_KEY = "md-sidebar-expanded";
4
- function getInitialState() {
5
- return readStorage(STORAGE_KEY) === "true";
6
- }
7
5
  const useMdSidebarStore = create((set) => ({
8
- isExpanded: getInitialState(),
6
+ isExpanded: false,
7
+ _hydrated: false,
9
8
  toggle: () => set((state) => {
10
9
  const next = !state.isExpanded;
11
10
  writeStorage(STORAGE_KEY, String(next));
@@ -18,9 +17,23 @@ const useMdSidebarStore = create((set) => ({
18
17
  collapse: () => {
19
18
  writeStorage(STORAGE_KEY, "false");
20
19
  return set({ isExpanded: false });
20
+ },
21
+ _hydrate: () => {
22
+ const value = readStorage(STORAGE_KEY) === "true";
23
+ set({ isExpanded: value, _hydrated: true });
21
24
  }
22
25
  }));
26
+ function useMdSidebarHydration() {
27
+ const hydrate = useMdSidebarStore((s) => s._hydrate);
28
+ const hydrated = useMdSidebarStore((s) => s._hydrated);
29
+ useEffect(() => {
30
+ if (!hydrated) {
31
+ hydrate();
32
+ }
33
+ }, [hydrate, hydrated]);
34
+ }
23
35
  export {
36
+ useMdSidebarHydration,
24
37
  useMdSidebarStore
25
38
  };
26
39
  //# sourceMappingURL=useMdSidebarStore.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/store/useMdSidebarStore.ts"],"sourcesContent":["import { create } from \"zustand\";\nimport { readStorage, writeStorage } from \"../utils/browser/safe-storage\";\n\nconst STORAGE_KEY = \"md-sidebar-expanded\";\n\nfunction getInitialState(): boolean {\n return readStorage(STORAGE_KEY) === \"true\";\n}\n\ntype MdSidebarStore = {\n isExpanded: boolean;\n toggle: () => void;\n expand: () => void;\n collapse: () => void;\n};\n\nexport const useMdSidebarStore = create<MdSidebarStore>((set) => ({\n isExpanded: getInitialState(),\n toggle: () => set((state) => {\n const next = !state.isExpanded;\n writeStorage(STORAGE_KEY, String(next));\n return { isExpanded: next };\n }),\n expand: () => {\n writeStorage(STORAGE_KEY, \"true\");\n return set({ isExpanded: true });\n },\n collapse: () => {\n writeStorage(STORAGE_KEY, \"false\");\n return set({ isExpanded: false });\n },\n}));\n"],"mappings":"AAAA,SAAS,cAAc;AACvB,SAAS,aAAa,oBAAoB;AAE1C,MAAM,cAAc;AAEpB,SAAS,kBAA2B;AAChC,SAAO,YAAY,WAAW,MAAM;AACxC;AASO,MAAM,oBAAoB,OAAuB,CAAC,SAAS;AAAA,EAC9D,YAAY,gBAAgB;AAAA,EAC5B,QAAQ,MAAM,IAAI,CAAC,UAAU;AACzB,UAAM,OAAO,CAAC,MAAM;AACpB,iBAAa,aAAa,OAAO,IAAI,CAAC;AACtC,WAAO,EAAE,YAAY,KAAK;AAAA,EAC9B,CAAC;AAAA,EACD,QAAQ,MAAM;AACV,iBAAa,aAAa,MAAM;AAChC,WAAO,IAAI,EAAE,YAAY,KAAK,CAAC;AAAA,EACnC;AAAA,EACA,UAAU,MAAM;AACZ,iBAAa,aAAa,OAAO;AACjC,WAAO,IAAI,EAAE,YAAY,MAAM,CAAC;AAAA,EACpC;AACJ,EAAE;","names":[]}
1
+ {"version":3,"sources":["../../src/store/useMdSidebarStore.ts"],"sourcesContent":["import { create } from \"zustand\";\nimport { useEffect } from \"react\";\nimport { readStorage, writeStorage } from \"../utils/browser/safe-storage\";\n\nconst STORAGE_KEY = \"md-sidebar-expanded\";\n\ntype MdSidebarStore = {\n isExpanded: boolean;\n _hydrated: boolean;\n toggle: () => void;\n expand: () => void;\n collapse: () => void;\n _hydrate: () => void;\n};\n\nexport const useMdSidebarStore = create<MdSidebarStore>((set) => ({\n isExpanded: false,\n _hydrated: false,\n toggle: () => set((state) => {\n const next = !state.isExpanded;\n writeStorage(STORAGE_KEY, String(next));\n return { isExpanded: next };\n }),\n expand: () => {\n writeStorage(STORAGE_KEY, \"true\");\n return set({ isExpanded: true });\n },\n collapse: () => {\n writeStorage(STORAGE_KEY, \"false\");\n return set({ isExpanded: false });\n },\n _hydrate: () => {\n const value = readStorage(STORAGE_KEY) === \"true\";\n set({ isExpanded: value, _hydrated: true });\n },\n}));\n\nexport function useMdSidebarHydration() {\n const hydrate = useMdSidebarStore((s) => s._hydrate);\n const hydrated = useMdSidebarStore((s) => s._hydrated);\n\n useEffect(() => {\n if (!hydrated) {\n hydrate();\n }\n }, [hydrate, hydrated]);\n}\n"],"mappings":"AAAA,SAAS,cAAc;AACvB,SAAS,iBAAiB;AAC1B,SAAS,aAAa,oBAAoB;AAE1C,MAAM,cAAc;AAWb,MAAM,oBAAoB,OAAuB,CAAC,SAAS;AAAA,EAC9D,YAAY;AAAA,EACZ,WAAW;AAAA,EACX,QAAQ,MAAM,IAAI,CAAC,UAAU;AACzB,UAAM,OAAO,CAAC,MAAM;AACpB,iBAAa,aAAa,OAAO,IAAI,CAAC;AACtC,WAAO,EAAE,YAAY,KAAK;AAAA,EAC9B,CAAC;AAAA,EACD,QAAQ,MAAM;AACV,iBAAa,aAAa,MAAM;AAChC,WAAO,IAAI,EAAE,YAAY,KAAK,CAAC;AAAA,EACnC;AAAA,EACA,UAAU,MAAM;AACZ,iBAAa,aAAa,OAAO;AACjC,WAAO,IAAI,EAAE,YAAY,MAAM,CAAC;AAAA,EACpC;AAAA,EACA,UAAU,MAAM;AACZ,UAAM,QAAQ,YAAY,WAAW,MAAM;AAC3C,QAAI,EAAE,YAAY,OAAO,WAAW,KAAK,CAAC;AAAA,EAC9C;AACJ,EAAE;AAEK,SAAS,wBAAwB;AACpC,QAAM,UAAU,kBAAkB,CAAC,MAAM,EAAE,QAAQ;AACnD,QAAM,WAAW,kBAAkB,CAAC,MAAM,EAAE,SAAS;AAErD,YAAU,MAAM;AACZ,QAAI,CAAC,UAAU;AACX,cAAQ;AAAA,IACZ;AAAA,EACJ,GAAG,CAAC,SAAS,QAAQ,CAAC;AAC1B;","names":[]}
@@ -6,7 +6,10 @@ async function safeServerAction(fn) {
6
6
  return { success: true, data };
7
7
  } catch (error) {
8
8
  if (isRedirectError(error)) throw error;
9
- const message = error instanceof ApiError ? error.message : error instanceof Error ? error.message : "Erro desconhecido";
9
+ if (error instanceof ApiError) {
10
+ return { success: false, error: error.message, code: error.code, statusCode: error.statusCode };
11
+ }
12
+ const message = error instanceof Error ? error.message : "Erro desconhecido";
10
13
  return { success: false, error: message };
11
14
  }
12
15
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/utils/safeServerAction.ts"],"sourcesContent":["import { isRedirectError } from 'next/dist/client/components/redirect-error';\nimport { ApiError } from '../infra/api/types';\nimport type { ActionResult } from '../infra/api/types';\n\nexport async function safeServerAction<T>(\n fn: () => Promise<T>\n): Promise<ActionResult<T>> {\n try {\n const data = await fn();\n return { success: true, data };\n } catch (error) {\n if (isRedirectError(error)) throw error;\n const message =\n error instanceof ApiError\n ? error.message\n : error instanceof Error\n ? error.message\n : 'Erro desconhecido';\n return { success: false, error: message };\n }\n}\n"],"mappings":"AAAA,SAAS,uBAAuB;AAChC,SAAS,gBAAgB;AAGzB,eAAsB,iBACpB,IAC0B;AAC1B,MAAI;AACF,UAAM,OAAO,MAAM,GAAG;AACtB,WAAO,EAAE,SAAS,MAAM,KAAK;AAAA,EAC/B,SAAS,OAAO;AACd,QAAI,gBAAgB,KAAK,EAAG,OAAM;AAClC,UAAM,UACJ,iBAAiB,WACb,MAAM,UACN,iBAAiB,QACf,MAAM,UACN;AACR,WAAO,EAAE,SAAS,OAAO,OAAO,QAAQ;AAAA,EAC1C;AACF;","names":[]}
1
+ {"version":3,"sources":["../../src/utils/safeServerAction.ts"],"sourcesContent":["import { isRedirectError } from 'next/dist/client/components/redirect-error';\nimport { ApiError } from '../infra/api/types';\nimport type { ActionResult } from '../infra/api/types';\n\nexport async function safeServerAction<T>(\n fn: () => Promise<T>\n): Promise<ActionResult<T>> {\n try {\n const data = await fn();\n return { success: true, data };\n } catch (error) {\n if (isRedirectError(error)) throw error;\n if (error instanceof ApiError) {\n return { success: false, error: error.message, code: error.code, statusCode: error.statusCode };\n }\n const message = error instanceof Error ? error.message : 'Erro desconhecido';\n return { success: false, error: message };\n }\n}\n"],"mappings":"AAAA,SAAS,uBAAuB;AAChC,SAAS,gBAAgB;AAGzB,eAAsB,iBACpB,IAC0B;AAC1B,MAAI;AACF,UAAM,OAAO,MAAM,GAAG;AACtB,WAAO,EAAE,SAAS,MAAM,KAAK;AAAA,EAC/B,SAAS,OAAO;AACd,QAAI,gBAAgB,KAAK,EAAG,OAAM;AAClC,QAAI,iBAAiB,UAAU;AAC7B,aAAO,EAAE,SAAS,OAAO,OAAO,MAAM,SAAS,MAAM,MAAM,MAAM,YAAY,MAAM,WAAW;AAAA,IAChG;AACA,UAAM,UAAU,iBAAiB,QAAQ,MAAM,UAAU;AACzD,WAAO,EAAE,SAAS,OAAO,OAAO,QAAQ;AAAA,EAC1C;AACF;","names":[]}
@@ -1,8 +1,9 @@
1
+ import { ApiError } from "../infra/api/types";
1
2
  function withAction(action) {
2
3
  return async (...args) => {
3
4
  const result = await action(...args);
4
5
  if (!result.success) {
5
- throw new Error(result.error ?? "Erro desconhecido");
6
+ throw new ApiError(result.error, result.code, result.statusCode);
6
7
  }
7
8
  return result.data;
8
9
  };
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/utils/withAction.ts"],"sourcesContent":["import type { ActionResult } from '../infra/api/types';\n\nexport function withAction<TArgs extends any[], TData>(\n action: (...args: TArgs) => Promise<ActionResult<TData>>\n): (...args: TArgs) => Promise<TData> {\n return async (...args) => {\n const result = await action(...args);\n if (!result.success) {\n throw new Error(result.error ?? 'Erro desconhecido');\n }\n return result.data as TData;\n };\n}\n"],"mappings":"AAEO,SAAS,WACd,QACoC;AACpC,SAAO,UAAU,SAAS;AACxB,UAAM,SAAS,MAAM,OAAO,GAAG,IAAI;AACnC,QAAI,CAAC,OAAO,SAAS;AACnB,YAAM,IAAI,MAAM,OAAO,SAAS,mBAAmB;AAAA,IACrD;AACA,WAAO,OAAO;AAAA,EAChB;AACF;","names":[]}
1
+ {"version":3,"sources":["../../src/utils/withAction.ts"],"sourcesContent":["import { ApiError } from '../infra/api/types';\nimport type { ActionResult } from '../infra/api/types';\n\nexport function withAction<TArgs extends any[], TData>(\n action: (...args: TArgs) => Promise<ActionResult<TData>>\n): (...args: TArgs) => Promise<TData> {\n return async (...args) => {\n const result = await action(...args);\n if (!result.success) {\n throw new ApiError(result.error, result.code, result.statusCode);\n }\n return result.data as TData;\n };\n}\n"],"mappings":"AAAA,SAAS,gBAAgB;AAGlB,SAAS,WACd,QACoC;AACpC,SAAO,UAAU,SAAS;AACxB,UAAM,SAAS,MAAM,OAAO,GAAG,IAAI;AACnC,QAAI,CAAC,OAAO,SAAS;AACnB,YAAM,IAAI,SAAS,OAAO,OAAO,OAAO,MAAM,OAAO,UAAU;AAAA,IACjE;AACA,WAAO,OAAO;AAAA,EAChB;AACF;","names":[]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@greatapps/common",
3
- "version": "1.1.782",
3
+ "version": "1.1.784",
4
4
  "description": "Shared library for GreatApps frontend applications",
5
5
  "main": "./dist/index.mjs",
6
6
  "types": "./src/index.ts",
@@ -12,6 +12,7 @@ import {
12
12
  useDesktopSidebarStore,
13
13
  useDesktopSidebarHydration,
14
14
  } from "../../store/useDesktopSidebarStore";
15
+ import { useMdSidebarHydration } from "../../store/useMdSidebarStore";
15
16
  import {
16
17
  useWhitelabel,
17
18
  useIsDefaultWhitelabel,
@@ -44,6 +45,7 @@ function NavBar({
44
45
  const { isExpanded: isDesktopExpanded, toggle: toggleDesktop } =
45
46
  useDesktopSidebarStore();
46
47
  useDesktopSidebarHydration();
48
+ useMdSidebarHydration();
47
49
  const { whitelabel } = useWhitelabel();
48
50
  const isDefaultWl = useIsDefaultWhitelabel();
49
51
  const primaryColor = whitelabel?.general?.primary_color;
@@ -45,7 +45,7 @@ export default function PhoneInput({
45
45
  const translate = useTranslations();
46
46
  const parsed = parsePhoneValue(value ?? '');
47
47
  const [selectedCountry, setSelectedCountry] = useState<Country>(parsed.country);
48
- const [phoneNumber, setPhoneNumber] = useState(parsed.number);
48
+ const [phoneNumber, setPhoneNumber] = useState(formatNational(parsed.number, parsed.country.iso));
49
49
  const [search, setSearch] = useState('');
50
50
  const [open, setOpen] = useState(false);
51
51
  const prevValueRef = useRef(value);
package/src/handlers.ts CHANGED
@@ -20,6 +20,7 @@ export { createListAvailableUsersHandler } from './modules/projects/handlers/lis
20
20
  export { createListAllAccountUsersHandler } from './modules/projects/handlers/list-all-account-users.handler';
21
21
 
22
22
  export { createListSubscriptionsHandler } from './modules/subscriptions/handlers/list-subscriptions.handler';
23
+ export { createCurrentSubscriptionHandler } from './modules/subscriptions/handlers/current-subscription.handler';
23
24
  export { createFreezeNoticeHandler } from './modules/subscriptions/handlers/freeze-notice.handler';
24
25
 
25
26
  export { createListMessagesHandler } from './modules/users/handlers/list-messages.handler';
package/src/index.ts CHANGED
@@ -86,6 +86,8 @@ export {
86
86
  CHARGES_QUERY_KEY,
87
87
  FREEZE_NOTICE_QUERY_KEY,
88
88
  } from "./modules/subscriptions/constants/query-keys.constants";
89
+ export { useCurrentSubscription } from "./modules/subscriptions/hooks/use-current-subscription.hook";
90
+ /** @deprecated Use `useCurrentSubscription`. Sai na 1.1.784. */
89
91
  export { useActiveSubscription } from "./modules/subscriptions/hooks/find-active-subscription.hook";
90
92
  export { useCancelledSubscriptionGuard } from "./modules/subscriptions/hooks/use-cancelled-subscription-guard";
91
93
  export { useAccountFreeze } from "./modules/subscriptions/hooks/use-account-freeze.hook";
@@ -221,7 +223,11 @@ export {
221
223
  confirmPaymentIfRequired,
222
224
  PaymentAuthenticationError,
223
225
  } from "./modules/subscriptions/utils/confirm-payment-if-required";
224
- export { SUBSCRIPTION_GRACE_PERIOD_DAYS } from "./modules/subscriptions/constants/subscription.constants";
226
+ export {
227
+ SUBSCRIPTION_GRACE_PERIOD_DAYS,
228
+ FREE_PLAN_ID,
229
+ } from "./modules/subscriptions/constants/subscription.constants";
230
+ export { groupSubscriptionsByProduct } from "./modules/subscriptions/utils/group-subscriptions-by-product";
225
231
  export { getSubscriptionCancellationState } from "./modules/subscriptions/utils/get-subscription-cancellation-state";
226
232
  export type { SubscriptionCancellationState } from "./modules/subscriptions/utils/get-subscription-cancellation-state";
227
233
  export {
@@ -31,6 +31,11 @@ export interface ApiClientOptions {
31
31
  emitLogs?: boolean;
32
32
  }
33
33
 
34
+ interface ApiErrorResponseBody {
35
+ message?: string | { message?: string };
36
+ code?: string;
37
+ }
38
+
34
39
  export interface RequestConfig extends RequestInit {
35
40
  timeout?: number;
36
41
  /** Padrão `json`. Use `text` quando a API devolve corpo não-JSON. */
@@ -169,9 +174,12 @@ class ApiClient {
169
174
  let message = `HTTP error ${response.status} ${response.statusText} ${errBody}`;
170
175
  let code = "HTTP_ERROR";
171
176
  try {
172
- const errorData = JSON.parse(errBody) as Record<string, unknown>;
173
- // @ts-ignore -- msg pode ser string ou objeto com .message
174
- message = errorData.message?.message ?? errorData.message ?? message;
177
+ const errorData: ApiErrorResponseBody = JSON.parse(errBody);
178
+ if (typeof errorData.message === "string") {
179
+ message = errorData.message;
180
+ } else if (errorData.message?.message) {
181
+ message = errorData.message.message;
182
+ }
175
183
  if (typeof errorData.code === "string") code = errorData.code;
176
184
  } catch {
177
185
  if (errBody.trim()) message = errBody.trim();
@@ -1,9 +1,9 @@
1
1
  import z from 'zod';
2
2
 
3
- export const PaginationParamsSchema = z.object({
4
- page: z.number().min(1).optional().default(1),
5
- limit: z.number().optional().default(20),
6
- });
3
+ export const PaginationParamsSchema = z.object({
4
+ page: z.number().min(1).optional().default(1),
5
+ limit: z.number().optional().default(20),
6
+ });
7
7
 
8
8
  export interface PaginationParams extends z.infer<typeof PaginationParamsSchema> {}
9
9
 
@@ -51,6 +51,8 @@ export interface PaginatedSuccessResult<T> extends Omit<SuccessResult<T>, 'data'
51
51
  export interface ErrorResult {
52
52
  success: false;
53
53
  error?: string;
54
+ code?: string;
55
+ statusCode?: number;
54
56
  }
55
57
 
56
58
  export type ActionResult<T> = SuccessResult<T> | ErrorResult;
@@ -60,7 +62,7 @@ export class ApiError extends Error {
60
62
  public readonly code: string;
61
63
  public readonly statusCode: number;
62
64
 
63
- constructor(message: string, code: string = 'UNKNOWN_ERROR', statusCode: number = 500) {
65
+ constructor(message: string = 'Erro desconhecido', code: string = 'UNKNOWN_ERROR', statusCode: number = 500) {
64
66
  super(message);
65
67
  this.name = 'ApiError';
66
68
  this.code = code;
@@ -8,7 +8,10 @@ export async function safeRouteHandler<T>(fn: () => Promise<T>): Promise<Respons
8
8
  return Response.json({ success: true, data });
9
9
  } catch (error) {
10
10
  if (error instanceof ApiError) {
11
- return Response.json({ success: false, error: error.message }, { status: error.statusCode });
11
+ return Response.json(
12
+ { success: false, error: error.message, code: error.code },
13
+ { status: error.statusCode },
14
+ );
12
15
  }
13
16
  console.error('[safeRouteHandler] erro inesperado', error);
14
17
  return Response.json({ success: false, error: 'Erro interno do servidor' }, { status: 500 });
@@ -1,22 +1,23 @@
1
1
  import "server-only";
2
2
 
3
3
  import { ApiError } from "../../../infra/api/types";
4
- import { listSubscriptionsAction } from "../../subscriptions/actions/list-subscriptions.action";
4
+ import { readCurrentSubscription } from "../../subscriptions/utils/read-current-subscription";
5
5
  import { getAccountFreezeState } from "../../subscriptions/utils/get-account-freeze-state";
6
+ import type { Subscription } from "../../subscriptions/types/subscription.type";
6
7
 
7
8
  export async function assertAccountNotFrozen(): Promise<void> {
8
- const result = await listSubscriptionsAction({ active: true });
9
+ let subscription: Subscription | null;
9
10
 
10
- if (!result.success) {
11
+ try {
12
+ subscription = await readCurrentSubscription();
13
+ } catch (error) {
11
14
  console.error(
12
15
  "[assertAccountNotFrozen] failed to read subscription context:",
13
- result.error,
16
+ error,
14
17
  );
15
18
  return;
16
19
  }
17
20
 
18
- const subscription = result.data?.data?.[0];
19
-
20
21
  if (!subscription) return;
21
22
 
22
23
  if (getAccountFreezeState(subscription) !== "frozen") return;
@@ -1,32 +1,23 @@
1
1
  import "server-only";
2
2
 
3
- import { cache } from "react";
4
-
5
3
  import { ApiError } from "../../../infra/api/types";
6
- import { listSubscriptionsAction } from "../../subscriptions/actions/list-subscriptions.action";
4
+ import { readCurrentSubscription } from "../../subscriptions/utils/read-current-subscription";
5
+ import { getSubscriptionCancellationState } from "../../subscriptions/utils/get-subscription-cancellation-state";
7
6
  import { getSubscriptionExpiryDate } from "../../subscriptions/utils/get-subscription-expiry-date";
8
7
 
9
8
  /**
10
- * Deduplicado por request via React.cache, como `getCurrentUserProfile` e `requireValidSession`.
9
+ * A leitura por trás (`readCurrentSubscription`) já é deduplicada por request via
10
+ * React.cache, então não precisa de outra camada aqui: em produção (26/08) o mesmo
11
+ * carregamento disparava até quatro cópias da mesma pergunta em paralelo, com 727 ms,
12
+ * 4,2 s, 5,0 s e 7,0 s, contra um p50 de 528 ms; agora é uma leitura só,
13
+ * compartilhada pelos quatro gates.
11
14
  *
12
- * A leitura por trás é `/accounts/{id}/subscriptions?limit=1`, e ela é MAIS cara que o gate de
13
- * perfil: para conta Stripe a listagem consulta a API do gateway ao vivo. Em produção (26/08) o
14
- * mesmo carregamento disparou quatro dessas em paralelo — 727 ms, 4,2 s, 5,0 s e 7,0 s, contra um
15
- * p50 de 528 ms para o endpoint. Elas se atrasam entre si: são cópias da mesma pergunta disputando
16
- * a mesma fila.
15
+ * A checagem de cancelamento é obrigatória além da de vencimento: a corrente pode
16
+ * vir com `active: false` (baixa por falha de pagamento) com `date_due` ainda no
17
+ * futuro, caso que só `getSubscriptionExpiryDate` não cobre.
17
18
  */
18
- export const assertManagementSubscription = cache(async () => {
19
- const result = await listSubscriptionsAction({ active: true });
20
-
21
- if (!result.success) {
22
- throw new ApiError(
23
- result.error || "Não foi possível validar a assinatura atual",
24
- "SUBSCRIPTION_CONTEXT_FAILED",
25
- 403,
26
- );
27
- }
28
-
29
- const subscription = result.data?.data?.[0];
19
+ export async function assertManagementSubscription(): Promise<void> {
20
+ const subscription = await readCurrentSubscription();
30
21
 
31
22
  if (!subscription) {
32
23
  throw new ApiError(
@@ -36,14 +27,15 @@ export const assertManagementSubscription = cache(async () => {
36
27
  );
37
28
  }
38
29
 
30
+ const isCancelled = getSubscriptionCancellationState(subscription) === "cancelled";
39
31
  const expiryDate = getSubscriptionExpiryDate(subscription);
40
32
  const isExpired = !!expiryDate && Date.now() >= expiryDate.getTime();
41
33
 
42
- if (isExpired) {
34
+ if (isCancelled || isExpired) {
43
35
  throw new ApiError(
44
36
  "Sua assinatura está vencida. Assine um novo plano para continuar.",
45
37
  "SUBSCRIPTION_EXPIRED",
46
38
  403,
47
39
  );
48
40
  }
49
- });
41
+ }
@@ -1,21 +1,13 @@
1
1
  import "server-only";
2
2
 
3
3
  import { ApiError } from "../../../infra/api/types";
4
- import { listSubscriptionsAction } from "../../subscriptions/actions/list-subscriptions.action";
4
+ import { readCurrentSubscription } from "../../subscriptions/utils/read-current-subscription";
5
+ import { getSubscriptionCancellationState } from "../../subscriptions/utils/get-subscription-cancellation-state";
5
6
  import { getSubscriptionExpiryDate } from "../../subscriptions/utils/get-subscription-expiry-date";
7
+ import { FREE_PLAN_ID } from "../../subscriptions/constants/subscription.constants";
6
8
 
7
9
  export async function assertPaidSubscription() {
8
- const result = await listSubscriptionsAction({ active: true });
9
-
10
- if (!result.success) {
11
- throw new ApiError(
12
- result.error || "Não foi possível validar a assinatura atual",
13
- "SUBSCRIPTION_CONTEXT_FAILED",
14
- 403,
15
- );
16
- }
17
-
18
- const subscription = result.data?.data?.[0];
10
+ const subscription = await readCurrentSubscription();
19
11
 
20
12
  if (!subscription) {
21
13
  throw new ApiError(
@@ -25,10 +17,11 @@ export async function assertPaidSubscription() {
25
17
  );
26
18
  }
27
19
 
20
+ const isCancelled = getSubscriptionCancellationState(subscription) === "cancelled";
28
21
  const expiryDate = getSubscriptionExpiryDate(subscription);
29
22
  const isExpired = !!expiryDate && Date.now() >= expiryDate.getTime();
30
23
 
31
- if (isExpired) {
24
+ if (isCancelled || isExpired) {
32
25
  throw new ApiError(
33
26
  "Sua assinatura está vencida. Assine um novo plano para continuar.",
34
27
  "SUBSCRIPTION_EXPIRED",
@@ -36,8 +29,7 @@ export async function assertPaidSubscription() {
36
29
  );
37
30
  }
38
31
 
39
-
40
- if (subscription.type === 'free' || subscription.id_plan === 5) {
32
+ if (subscription.type === 'free' || subscription.id_plan === FREE_PLAN_ID) {
41
33
  throw new ApiError(
42
34
  "Este recurso está disponível apenas para planos pagos. Faça upgrade do seu plano para continuar.",
43
35
  "PAID_SUBSCRIPTION_REQUIRED",
@@ -1,24 +1,14 @@
1
1
  import "server-only";
2
2
 
3
3
  import { ApiError } from "../../../infra/api/types";
4
- import { listSubscriptionsAction } from "../../subscriptions/actions/list-subscriptions.action";
4
+ import { readCurrentSubscription } from "../../subscriptions/utils/read-current-subscription";
5
5
  import type { AddonKindEnum } from "../../subscriptions/constants/addons.constants";
6
6
 
7
7
  export async function assertSubscriptionAddon(
8
8
  addonId: AddonKindEnum,
9
9
  addonLabel: string,
10
10
  ) {
11
- const result = await listSubscriptionsAction({ active: true });
12
-
13
- if (!result.success) {
14
- throw new ApiError(
15
- result.error || "Não foi possível validar a assinatura atual",
16
- "SUBSCRIPTION_CONTEXT_FAILED",
17
- 403,
18
- );
19
- }
20
-
21
- const subscription = result.data?.data?.[0];
11
+ const subscription = await readCurrentSubscription();
22
12
 
23
13
  if (!subscription) {
24
14
  throw new ApiError(
@@ -0,0 +1,8 @@
1
+ 'use server';
2
+
3
+ import { safeServerAction } from '../../../utils/safeServerAction';
4
+ import { subscriptionsService } from '../services/subscriptions.service';
5
+
6
+ export async function getCurrentSubscriptionAction(params?: { id_product?: number }) {
7
+ return safeServerAction(() => subscriptionsService.getCurrentSubscription(params));
8
+ }
@@ -6,3 +6,6 @@ export const FREEZE_WARNING_DAYS_BEFORE = 1;
6
6
  export const FREEZE_ELIGIBLE_WHITELABEL_ID = 1;
7
7
 
8
8
  export const MAX_PAYMENT_ATTEMPTS = 8;
9
+
10
+ /** Plano legado "FREE" identificado por `id_plan`, fora do `type: 'free'` moderno. */
11
+ export const FREE_PLAN_ID = 5;
@@ -0,0 +1,16 @@
1
+ import 'server-only';
2
+
3
+ import type { NextRequest } from 'next/server';
4
+ import { safeRouteHandler } from '../../../infra/route/safe-route-handler';
5
+ import { searchParamsToParams } from '../../../infra/route/search-params';
6
+ import { subscriptionsService } from '../services/subscriptions.service';
7
+
8
+ export function createCurrentSubscriptionHandler() {
9
+ return async function GET(req: NextRequest) {
10
+ return safeRouteHandler(() =>
11
+ subscriptionsService.getCurrentSubscription(
12
+ searchParamsToParams<{ id_product?: number }>(req.nextUrl.searchParams),
13
+ ),
14
+ );
15
+ };
16
+ }
@@ -1,22 +1,12 @@
1
1
  "use client";
2
2
 
3
- import { useQuery } from "@tanstack/react-query";
4
- import { apiFetch } from "../../../infra/http/api-fetch";
5
- import type { PaginatedSuccessResult } from "../../../infra/api/types";
6
- import type { Subscription } from "../types/subscription.type";
7
- import { SUBSCRIPTIONS_QUERY_KEY } from "../constants/query-keys.constants";
8
- import { useAuthQueryKey } from "../../../hooks/useAuthQueryKey";
3
+ import { useCurrentSubscription } from "./use-current-subscription.hook";
9
4
 
5
+ /**
6
+ * @deprecated Use `useCurrentSubscription`, mesma leitura (mesma chave de
7
+ * cache, uma única requisição); só o nome muda para o vocabulário certo
8
+ * ("corrente", não "ativa"). Sai na 1.1.784.
9
+ */
10
10
  export function useActiveSubscription() {
11
- const queryKey = useAuthQueryKey([...SUBSCRIPTIONS_QUERY_KEY, "active"]);
12
-
13
- return useQuery({
14
- queryKey,
15
- queryFn: ({ signal }) =>
16
- apiFetch<PaginatedSuccessResult<Subscription>>('/api/subscriptions', {
17
- params: { limit: 1 },
18
- signal,
19
- }),
20
- staleTime: 10_000,
21
- });
11
+ return useCurrentSubscription();
22
12
  }
@@ -0,0 +1,38 @@
1
+ 'use client';
2
+
3
+ import { useQuery } from '@tanstack/react-query';
4
+ import { apiFetch } from '../../../infra/http/api-fetch';
5
+ import { useAuthQueryKey } from '../../../hooks/useAuthQueryKey';
6
+ import type { PaginatedSuccessResult, SuccessResult } from '../../../infra/api/types';
7
+ import type { Subscription } from '../types/subscription.type';
8
+ import { SUBSCRIPTIONS_QUERY_KEY } from '../constants/query-keys.constants';
9
+
10
+ /**
11
+ * A assinatura corrente do plano da conta, pela regra única do backend
12
+ * (contrato de domínio nunca é corrente; encerrada é decidida por `active`).
13
+ *
14
+ * Devolvida na mesma forma paginada de `useSubscriptions`/`useActiveSubscription`
15
+ * (`{ data: [sub] }`) para não exigir mudança nos call sites que já leem
16
+ * `data?.[0]`: só a URL e a regra por trás mudam.
17
+ */
18
+ export function useCurrentSubscription() {
19
+ const queryKey = useAuthQueryKey([...SUBSCRIPTIONS_QUERY_KEY, 'current']);
20
+
21
+ return useQuery({
22
+ queryKey,
23
+ queryFn: async ({ signal }): Promise<PaginatedSuccessResult<Subscription>> => {
24
+ const result = await apiFetch<SuccessResult<Subscription | null>>(
25
+ '/api/subscriptions/current',
26
+ { signal },
27
+ );
28
+ const subscription = result.data ?? null;
29
+
30
+ return {
31
+ success: true,
32
+ data: subscription ? [subscription] : [],
33
+ total: subscription ? 1 : 0,
34
+ };
35
+ },
36
+ staleTime: 10_000,
37
+ });
38
+ }
@@ -85,6 +85,40 @@ class SubscriptionsService {
85
85
  } satisfies PaginatedSuccessResult<Subscription>;
86
86
  }
87
87
 
88
+ /**
89
+ * A assinatura corrente do plano da conta, pela regra única do backend
90
+ * (`GET .../subscriptions/action/current`): contrato de domínio nunca é
91
+ * corrente, encerrada é decidida por `active`. Sem `id_product`, paridade
92
+ * com hoje (melhor rank entre todos os produtos, nunca item/domínio).
93
+ */
94
+ async getCurrentSubscription(
95
+ params?: { id_product?: number },
96
+ ): Promise<SuccessResult<Subscription | null>> {
97
+ const { id_account } = await getUserContext();
98
+
99
+ const query = buildQueryParams(
100
+ params as Record<string, string | number | undefined>,
101
+ );
102
+ const url = `/accounts/${id_account}/subscriptions/action/current${query ? `?${query}` : ""}`;
103
+
104
+ const response = await api.apps.get<ApiPaginatedActionResult<Subscription>>(url);
105
+
106
+ if (response.status === 0) {
107
+ throw new ApiError(
108
+ response.message || "Erro ao consultar assinatura corrente",
109
+ "GET_CURRENT_SUBSCRIPTION_FAILED",
110
+ 400,
111
+ );
112
+ }
113
+
114
+ const raw = response.data[0];
115
+
116
+ return {
117
+ success: true,
118
+ data: raw ? SubscriptionSchema.parse(raw) : null,
119
+ };
120
+ }
121
+
88
122
  async calculateSubscription(
89
123
  data: CalculateSubscriptionRequest,
90
124
  ): Promise<SuccessResult<CalculateSubscriptionResponse>> {
@@ -115,4 +115,6 @@ export interface FindSubscriptionsParams {
115
115
  date_hiring_start?: string;
116
116
  date_hiring_end?: string;
117
117
  limit?: number;
118
+ page?: number;
119
+ id_product?: number;
118
120
  }