@greatapps/common 1.1.713 → 1.1.715

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 (70) hide show
  1. package/dist/index.mjs +32 -38
  2. package/dist/index.mjs.map +1 -1
  3. package/dist/providers/auth.provider.mjs +0 -2
  4. package/dist/providers/auth.provider.mjs.map +1 -1
  5. package/dist/testing/constants.mjs +21 -0
  6. package/dist/testing/constants.mjs.map +1 -0
  7. package/dist/testing/factories/account.factory.mjs +38 -0
  8. package/dist/testing/factories/account.factory.mjs.map +1 -0
  9. package/dist/testing/factories/create-mock.mjs +16 -0
  10. package/dist/testing/factories/create-mock.mjs.map +1 -0
  11. package/dist/testing/factories/index.mjs +21 -0
  12. package/dist/testing/factories/index.mjs.map +1 -0
  13. package/dist/testing/factories/jwt.mjs +33 -0
  14. package/dist/testing/factories/jwt.mjs.map +1 -0
  15. package/dist/testing/factories/plan.factory.mjs +23 -0
  16. package/dist/testing/factories/plan.factory.mjs.map +1 -0
  17. package/dist/testing/factories/subscription.factory.mjs +52 -0
  18. package/dist/testing/factories/subscription.factory.mjs.map +1 -0
  19. package/dist/testing/factories/user.factory.mjs +36 -0
  20. package/dist/testing/factories/user.factory.mjs.map +1 -0
  21. package/dist/testing/factories/whitelabel.factory.mjs +66 -0
  22. package/dist/testing/factories/whitelabel.factory.mjs.map +1 -0
  23. package/dist/testing/index.mjs +10 -0
  24. package/dist/testing/index.mjs.map +1 -0
  25. package/dist/testing/msw/envelope.mjs +20 -0
  26. package/dist/testing/msw/envelope.mjs.map +1 -0
  27. package/dist/testing/msw/index.mjs +10 -0
  28. package/dist/testing/msw/index.mjs.map +1 -0
  29. package/dist/testing/msw/session.handlers.mjs +23 -0
  30. package/dist/testing/msw/session.handlers.mjs.map +1 -0
  31. package/dist/testing/react/index.mjs +8 -0
  32. package/dist/testing/react/index.mjs.map +1 -0
  33. package/dist/testing/react/providers.mjs +42 -0
  34. package/dist/testing/react/providers.mjs.map +1 -0
  35. package/dist/testing/react/test-query-client.mjs +13 -0
  36. package/dist/testing/react/test-query-client.mjs.map +1 -0
  37. package/dist/testing.mjs +2 -0
  38. package/dist/testing.mjs.map +1 -0
  39. package/package.json +21 -1
  40. package/src/index.ts +0 -3
  41. package/src/providers/auth.provider.tsx +0 -3
  42. package/src/testing/constants.ts +19 -0
  43. package/src/testing/factories/account.factory.ts +37 -0
  44. package/src/testing/factories/create-mock.ts +18 -0
  45. package/src/testing/factories/index.ts +7 -0
  46. package/src/testing/factories/jwt.ts +44 -0
  47. package/src/testing/factories/plan.factory.ts +21 -0
  48. package/src/testing/factories/subscription.factory.ts +51 -0
  49. package/src/testing/factories/user.factory.ts +35 -0
  50. package/src/testing/factories/whitelabel.factory.ts +67 -0
  51. package/src/testing/index.ts +7 -0
  52. package/src/testing/msw/envelope.ts +19 -0
  53. package/src/testing/msw/index.ts +2 -0
  54. package/src/testing/msw/session.handlers.ts +34 -0
  55. package/src/testing/react/index.ts +2 -0
  56. package/src/testing/react/providers.tsx +65 -0
  57. package/src/testing/react/test-query-client.ts +11 -0
  58. package/src/testing.ts +1 -0
  59. package/dist/components/modals/HexaPromoModal.mjs +0 -169
  60. package/dist/components/modals/HexaPromoModal.mjs.map +0 -1
  61. package/dist/components/modals/HexaPromoModalGate.mjs +0 -61
  62. package/dist/components/modals/HexaPromoModalGate.mjs.map +0 -1
  63. package/dist/store/useHexaPromoModal.mjs +0 -14
  64. package/dist/store/useHexaPromoModal.mjs.map +0 -1
  65. package/dist/utils/cookies/hexa-promo.mjs +0 -88
  66. package/dist/utils/cookies/hexa-promo.mjs.map +0 -1
  67. package/src/components/modals/HexaPromoModal.tsx +0 -218
  68. package/src/components/modals/HexaPromoModalGate.tsx +0 -93
  69. package/src/store/useHexaPromoModal.ts +0 -13
  70. package/src/utils/cookies/hexa-promo.ts +0 -114
@@ -0,0 +1,44 @@
1
+ import type { JWTPayload } from '../../modules/users/schema';
2
+ import { TEST_IDS } from '../constants';
3
+
4
+ function b64(value: unknown): string {
5
+ // getUserContext decodes the payload with `atob(token.split('.')[1])`, so standard base64 works.
6
+ return btoa(JSON.stringify(value));
7
+ }
8
+
9
+ export interface TestJwtClaims {
10
+ id_wl?: number;
11
+ id_account?: number;
12
+ id_user?: number;
13
+ // Country used by resolveLocale (session.location.country).
14
+ country?: string;
15
+ }
16
+
17
+ // Builds a fake JWT (placeholder signature) whose payload is decodable by getUserContext and
18
+ // resolveLocale. Used as DUMMY_AUTH_TOKEN: the common auth bypasses verification when that env is
19
+ // set, so only the payload matters.
20
+ export function createTestJwt(claims: TestJwtClaims = {}): string {
21
+ const payload: JWTPayload = {
22
+ session: {
23
+ location: {
24
+ continent: 'SA',
25
+ country: claims.country ?? 'BR',
26
+ region: 'SP',
27
+ city: 'Sao Paulo',
28
+ latitude: '0',
29
+ longitude: '0',
30
+ },
31
+ ip: '127.0.0.1',
32
+ timezone: 'America/Sao_Paulo',
33
+ agent: 'test',
34
+ code: null,
35
+ },
36
+ id_wl: String(claims.id_wl ?? TEST_IDS.wl),
37
+ id_account: claims.id_account ?? TEST_IDS.account,
38
+ id_user: claims.id_user ?? TEST_IDS.user,
39
+ iss: 'test',
40
+ aud: 'test',
41
+ iat: 1700000000,
42
+ };
43
+ return `${b64({ alg: 'none', typ: 'JWT' })}.${b64(payload)}.sig`;
44
+ }
@@ -0,0 +1,21 @@
1
+ import { faker } from '@faker-js/faker';
2
+ import { Plan, PlanItem } from '../../modules/plans/types/plan.type';
3
+ import { createMock } from './create-mock';
4
+
5
+ export const createPlanItemMock = createMock<PlanItem, Partial<PlanItem>>((overrides) => ({
6
+ id_addon: overrides?.id_addon ?? 2,
7
+ quantity: overrides?.quantity ?? 10,
8
+ value: overrides?.value ?? 9990,
9
+ base: overrides?.base ?? true,
10
+ }));
11
+
12
+ export const createPlanMock = createMock<Plan, Partial<Plan>>((overrides) => ({
13
+ id: overrides?.id ?? faker.number.int({ min: 1, max: 9999 }),
14
+ name: overrides?.name || faker.commerce.productName(),
15
+ value: overrides?.value ?? 9990,
16
+ currency: overrides?.currency ?? 'brl',
17
+ type: overrides?.type ?? 'paid',
18
+ discount_semester: overrides?.discount_semester ?? 0,
19
+ discount_annual: overrides?.discount_annual ?? 0,
20
+ items: overrides?.items ?? [createPlanItemMock()],
21
+ }));
@@ -0,0 +1,51 @@
1
+ import { faker } from '@faker-js/faker';
2
+ import { Subscription, SubscriptionItem } from '../../modules/subscriptions/types/subscription.type';
3
+ import { TEST_IDS } from '../constants';
4
+ import { createMock } from './create-mock';
5
+
6
+ export const createSubscriptionItemMock = createMock<SubscriptionItem, Partial<SubscriptionItem>>(
7
+ (overrides) => ({
8
+ id: overrides?.id ?? faker.number.int({ min: 1, max: 99999 }),
9
+ deleted: overrides?.deleted ?? 0,
10
+ datetime_add: overrides?.datetime_add ?? new Date('2025-01-01T00:00:00Z'),
11
+ id_wl: overrides?.id_wl ?? TEST_IDS.wl,
12
+ id_subscription: overrides?.id_subscription ?? 1,
13
+ id_addon: overrides?.id_addon ?? null,
14
+ value: overrides?.value ?? 9990,
15
+ currency: overrides?.currency ?? 'brl',
16
+ quantity: overrides?.quantity ?? 1,
17
+ payment_status: overrides?.payment_status ?? 1,
18
+ id_charge: overrides?.id_charge ?? null,
19
+ trial: overrides?.trial ?? true,
20
+ }),
21
+ );
22
+
23
+ // Default: active with a future due date (a valid trial).
24
+ export const createSubscriptionMock = createMock<Subscription, Partial<Subscription>>(
25
+ (overrides) => ({
26
+ id: overrides?.id ?? faker.number.int({ min: 1, max: 99999 }),
27
+ deleted: overrides?.deleted ?? 0,
28
+ datetime_add: overrides?.datetime_add ?? new Date('2025-01-01T00:00:00Z'),
29
+ id_wl: overrides?.id_wl ?? TEST_IDS.wl,
30
+ id_account: overrides?.id_account ?? TEST_IDS.account,
31
+ id_card: overrides?.id_card ?? null,
32
+ id_coupon: overrides?.id_coupon ?? null,
33
+ type: overrides?.type ?? 'recurring',
34
+ payment_method: overrides?.payment_method ?? 1,
35
+ periodicity: overrides?.periodicity ?? 1,
36
+ value: overrides?.value ?? 9990,
37
+ id_product: overrides?.id_product ?? 1,
38
+ id_plan: overrides?.id_plan ?? 1,
39
+ id_gateway: overrides?.id_gateway ?? null,
40
+ gateway_account: overrides?.gateway_account ?? null,
41
+ date_hiring: overrides?.date_hiring ?? new Date('2025-01-01'),
42
+ date_due: overrides?.date_due ?? new Date('2099-01-01'),
43
+ date_cancellation: overrides?.date_cancellation ?? null,
44
+ date_verification: overrides?.date_verification ?? null,
45
+ cancellation_reason: overrides?.cancellation_reason ?? null,
46
+ cancellation_description: overrides?.cancellation_description ?? null,
47
+ active: overrides?.active ?? true,
48
+ charged: overrides?.charged ?? true,
49
+ items: overrides?.items ?? [createSubscriptionItemMock()],
50
+ }),
51
+ );
@@ -0,0 +1,35 @@
1
+ import { faker } from '@faker-js/faker';
2
+ import { User, UserProfile } from '../../modules/users/schema';
3
+ import { TEST_IDS } from '../constants';
4
+ import { createMock } from './create-mock';
5
+
6
+ // Identity fields (id/id_wl/id_account) are fixed to TEST_IDS by default — the auth chain and the
7
+ // request-URL matching depend on them; override explicitly when needed. Cosmetic fields use faker.
8
+ export const createUserMock = createMock<User, Partial<User>>((overrides) => ({
9
+ id: overrides?.id ?? TEST_IDS.user,
10
+ id_wl: overrides?.id_wl ?? TEST_IDS.wl,
11
+ id_account: overrides?.id_account ?? TEST_IDS.account,
12
+ id_api: overrides?.id_api ?? 1,
13
+ name: overrides?.name || faker.person.firstName(),
14
+ last_name: overrides?.last_name || faker.person.lastName(),
15
+ email: overrides?.email || faker.internet.email().toLowerCase(),
16
+ ddi: overrides?.ddi || '55',
17
+ phone: overrides?.phone || faker.string.numeric(11),
18
+ rg: overrides?.rg ?? null,
19
+ cpf: overrides?.cpf ?? null,
20
+ gender: overrides?.gender ?? 0,
21
+ two_factor_authentication: overrides?.two_factor_authentication ?? false,
22
+ google_sub: overrides?.google_sub ?? null,
23
+ google_linked_at: overrides?.google_linked_at ?? null,
24
+ profile: overrides?.profile ?? UserProfile.owner,
25
+ language: overrides?.language ?? 'pt-br',
26
+ receive_sms: overrides?.receive_sms ?? false,
27
+ receive_email: overrides?.receive_email ?? true,
28
+ photo: overrides?.photo ?? null,
29
+ deleted: overrides?.deleted ?? 0,
30
+ datetime_alt: overrides?.datetime_alt || '2025-01-01 00:00:00',
31
+ datetime_del: overrides?.datetime_del ?? null,
32
+ datetime_add: overrides?.datetime_add || '2025-01-01 00:00:00',
33
+ total_projects: overrides?.total_projects ?? 1,
34
+ projects: overrides?.projects ?? [{ id: 1, title: faker.company.name() }],
35
+ }));
@@ -0,0 +1,67 @@
1
+ import { faker } from '@faker-js/faker';
2
+ import { WhitelabelData, WhitelabelTokenData } from '../../modules/whitelabel/schema';
3
+ import { TEST_API, TEST_IDS } from '../constants';
4
+ import { createMock } from './create-mock';
5
+ import { createTestJwt } from './jwt';
6
+
7
+ // id/domain stay aligned with TEST_IDS/TEST_API because resolveLocale/branding and the
8
+ // domain-based whitelabel resolution depend on them.
9
+ export const createWhitelabelMock = createMock<WhitelabelData, Partial<WhitelabelData>>(
10
+ (overrides) => {
11
+ const domain = overrides?.domain ?? TEST_API.domain;
12
+ return {
13
+ id: overrides?.id ?? TEST_IDS.wl,
14
+ deleted: overrides?.deleted ?? 0,
15
+ datetime_alt: overrides?.datetime_alt || '2025-01-01 00:00:00',
16
+ datetime_del: overrides?.datetime_del ?? null,
17
+ datetime_add: overrides?.datetime_add || '2025-01-01 00:00:00',
18
+ id_account: overrides?.id_account ?? TEST_IDS.account,
19
+ active: overrides?.active ?? true,
20
+ name: overrides?.name || faker.company.name(),
21
+ email: overrides?.email || faker.internet.email().toLowerCase(),
22
+ email_financial: overrides?.email_financial || faker.internet.email().toLowerCase(),
23
+ email_transactional: overrides?.email_transactional || faker.internet.email().toLowerCase(),
24
+ domain,
25
+ domain_free: overrides?.domain_free ?? null,
26
+ url_help: overrides?.url_help ?? `https://${domain}/help`,
27
+ url_login: overrides?.url_login ?? `https://${domain}/login`,
28
+ url_site: overrides?.url_site ?? `https://${domain}`,
29
+ url_signup: overrides?.url_signup ?? `https://${domain}/signup`,
30
+ codes: overrides?.codes ?? null,
31
+ id_gateway: overrides?.id_gateway ?? 1,
32
+ gateway_product: overrides?.gateway_product ?? 'prod_test',
33
+ external_contracting: overrides?.external_contracting ?? false,
34
+ logo: overrides?.logo ?? `https://${domain}/logo.png`,
35
+ secondary_logo: overrides?.secondary_logo ?? null,
36
+ favicon: overrides?.favicon ?? `https://${domain}/favicon.ico`,
37
+ general: overrides?.general ?? {
38
+ primary_color: faker.color.rgb(),
39
+ hover_color: faker.color.rgb(),
40
+ link_color: faker.color.rgb(),
41
+ },
42
+ signin_page: overrides?.signin_page ?? null,
43
+ signup_page: overrides?.signup_page ?? null,
44
+ dashboard: overrides?.dashboard ?? null,
45
+ use_policy_url: overrides?.use_policy_url ?? `https://${domain}/terms`,
46
+ cookie_policy_url: overrides?.cookie_policy_url ?? `https://${domain}/cookies`,
47
+ privacy_policy_url: overrides?.privacy_policy_url ?? `https://${domain}/privacy`,
48
+ editor_url: overrides?.editor_url ?? `https://editor.${domain}`,
49
+ accounts_url: overrides?.accounts_url ?? `https://accounts.${domain}`,
50
+ pages_url: overrides?.pages_url ?? `https://pages.${domain}`,
51
+ pages_preview_url: overrides?.pages_preview_url ?? `https://preview.${domain}`,
52
+ api_url: overrides?.api_url ?? TEST_API.gpages,
53
+ cname_url: overrides?.cname_url ?? `cname.${domain}`,
54
+ video_url: overrides?.video_url ?? null,
55
+ external_contracting_url: overrides?.external_contracting_url ?? null,
56
+ external_plans_url: overrides?.external_plans_url ?? null,
57
+ };
58
+ },
59
+ );
60
+
61
+ // Variant with `token` — what getTokenByDomain returns for the ApiClient to build the header.
62
+ export const createWhitelabelTokenMock = createMock<WhitelabelTokenData, Partial<WhitelabelTokenData>>(
63
+ (overrides) => ({
64
+ ...createWhitelabelMock(overrides),
65
+ token: overrides?.token ?? createTestJwt(),
66
+ }),
67
+ );
@@ -0,0 +1,7 @@
1
+ // `@greatapps/common/testing` — light, node/E2E-safe (factories + session handlers only). The
2
+ // React providers live in `@greatapps/common/testing/react` so the client barrel and its
3
+ // load-time side effects (e.g. zustand stores reading localStorage) don't leak into
4
+ // reducer/route-handler tests. `msw` and `@faker-js/faker` are optional peer dependencies.
5
+ export { TEST_IDS, TEST_API, TEST_API_VERSION, TEST_API_LOCALE } from './constants';
6
+ export * from './factories';
7
+ export * from './msw';
@@ -0,0 +1,19 @@
1
+ import { HttpResponse } from 'msw';
2
+
3
+ // The envelope GreatApps backends return. Services read `data` as an array and take `data[0]` on
4
+ // finds, so the success default is a list; use `apiOne` for the few endpoints returning an object.
5
+ export function apiList<T>(items: T[], total = items.length) {
6
+ return HttpResponse.json({ status: 1, data: items, total });
7
+ }
8
+
9
+ export function apiFind<T>(item: T) {
10
+ return apiList([item], 1);
11
+ }
12
+
13
+ export function apiOne<T>(data: T) {
14
+ return HttpResponse.json({ status: 1, data });
15
+ }
16
+
17
+ export function apiError(message = 'test error', status = 400) {
18
+ return HttpResponse.json({ status: 0, message }, { status });
19
+ }
@@ -0,0 +1,2 @@
1
+ export { apiList, apiFind, apiOne, apiError } from './envelope';
2
+ export { makeSessionHandlers, type SessionHandlersOptions } from './session.handlers';
@@ -0,0 +1,34 @@
1
+ import { http } from 'msw';
2
+ import type { Account } from '../../modules/accounts/types';
3
+ import type { User } from '../../modules/users/schema';
4
+ import type { WhitelabelTokenData } from '../../modules/whitelabel/schema';
5
+ import { TEST_API } from '../constants';
6
+ import { createAccountMock } from '../factories/account.factory';
7
+ import { createUserMock } from '../factories/user.factory';
8
+ import { createWhitelabelTokenMock } from '../factories/whitelabel.factory';
9
+ import { apiFind } from './envelope';
10
+
11
+ export interface SessionHandlersOptions {
12
+ gappsBase?: string;
13
+ user?: User;
14
+ account?: Account;
15
+ whitelabel?: WhitelabelTokenData;
16
+ }
17
+
18
+ // Server-side session resolution any app triggers when booting an authenticated route with
19
+ // DUMMY_AUTH_TOKEN (whitelabel by domain, token by wl id, user, account). Compose with app
20
+ // handlers: setupServer(...makeSessionHandlers(), ...myPageHandlers).
21
+ export function makeSessionHandlers(opts: SessionHandlersOptions = {}) {
22
+ const base = opts.gappsBase ?? TEST_API.gapps;
23
+ const user = opts.user ?? createUserMock();
24
+ const account = opts.account ?? createAccountMock();
25
+ const whitelabel = opts.whitelabel ?? createWhitelabelTokenMock();
26
+
27
+ return [
28
+ http.get(`${base}/v1/:locale/:wl/whitelabel/:host/token`, () => apiFind(whitelabel)),
29
+ http.get(`${base}/v1/:locale/:wl/tokens`, () => apiFind(whitelabel)),
30
+ // The users path has more segments, so it must come before /accounts/:acc.
31
+ http.get(`${base}/v1/:locale/:wl/accounts/:acc/users/:user`, () => apiFind(user)),
32
+ http.get(`${base}/v1/:locale/:wl/accounts/:acc`, () => apiFind(account)),
33
+ ];
34
+ }
@@ -0,0 +1,2 @@
1
+ export { makeTestQueryClient } from './test-query-client';
2
+ export { TestProviders, makeProvidersWrapper, type TestProvidersOptions } from './providers';
@@ -0,0 +1,65 @@
1
+ import { QueryClient, QueryClientProvider } from '@tanstack/react-query';
2
+ import type { ContextType, ReactNode } from 'react';
3
+ import type { Account } from '../../modules/accounts/types';
4
+ import type { User } from '../../modules/users/schema';
5
+ import type { WhitelabelData } from '../../modules/whitelabel/schema';
6
+ import { AuthContext } from '../../providers/auth.provider';
7
+ import { WhitelabelProvider } from '../../providers/whitelabel.provider';
8
+ import { createAccountMock } from '../factories/account.factory';
9
+ import { createUserMock } from '../factories/user.factory';
10
+ import { createWhitelabelMock } from '../factories/whitelabel.factory';
11
+ import { makeTestQueryClient } from './test-query-client';
12
+
13
+ type AuthValue = NonNullable<ContextType<typeof AuthContext>>;
14
+
15
+ // The real AuthProvider runs session queries/actions that don't work in jsdom, so we feed the same
16
+ // (exported) AuthContext a mocked value. The auth actions throw if called — mock them per test.
17
+ const notImpl = async (): Promise<never> => {
18
+ throw new Error('auth action is not available in TestProviders — mock it in the test if needed');
19
+ };
20
+
21
+ export interface TestProvidersOptions {
22
+ queryClient?: QueryClient;
23
+ user?: User | null;
24
+ account?: Account | null;
25
+ whitelabel?: WhitelabelData | null;
26
+ }
27
+
28
+ export function TestProviders({
29
+ children,
30
+ ...opts
31
+ }: TestProvidersOptions & { children: ReactNode }) {
32
+ const queryClient = opts.queryClient ?? makeTestQueryClient();
33
+ const user = opts.user === undefined ? createUserMock() : opts.user;
34
+ const account = opts.account === undefined ? createAccountMock() : opts.account;
35
+ const whitelabel = opts.whitelabel === undefined ? createWhitelabelMock() : opts.whitelabel;
36
+
37
+ const authValue: AuthValue = {
38
+ user,
39
+ account,
40
+ isAuthenticated: !!user,
41
+ isLoading: false,
42
+ login: notImpl,
43
+ register: notImpl,
44
+ logout: notImpl,
45
+ loginWithGoogle: notImpl,
46
+ completeGoogleOnboarding: notImpl,
47
+ };
48
+
49
+ return (
50
+ <QueryClientProvider client={queryClient}>
51
+ <WhitelabelProvider whitelabel={whitelabel}>
52
+ <AuthContext.Provider value={authValue}>{children}</AuthContext.Provider>
53
+ </WhitelabelProvider>
54
+ </QueryClientProvider>
55
+ );
56
+ }
57
+
58
+ // Wrapper for Testing Library: `render(ui, { wrapper: makeProvidersWrapper(opts) })`. The
59
+ // @testing-library/react dependency stays in the app, not the common lib, hence a wrapper here
60
+ // rather than a renderWithProviders.
61
+ export function makeProvidersWrapper(opts: TestProvidersOptions = {}) {
62
+ return function ProvidersWrapper({ children }: { children: ReactNode }) {
63
+ return <TestProviders {...opts}>{children}</TestProviders>;
64
+ };
65
+ }
@@ -0,0 +1,11 @@
1
+ import { QueryClient } from '@tanstack/react-query';
2
+
3
+ // retry off (errors surface immediately) and gcTime 0 (no cache across tests). Create one per test.
4
+ export function makeTestQueryClient(): QueryClient {
5
+ return new QueryClient({
6
+ defaultOptions: {
7
+ queries: { retry: false, gcTime: 0, staleTime: 0, refetchOnWindowFocus: false },
8
+ mutations: { retry: false },
9
+ },
10
+ });
11
+ }
package/src/testing.ts ADDED
@@ -0,0 +1 @@
1
+ export * from './testing/index';
@@ -1,169 +0,0 @@
1
- "use client";
2
- import { jsx, jsxs } from "react/jsx-runtime";
3
- import { toast } from "sonner";
4
- import {
5
- IconCheck,
6
- IconCopy,
7
- IconInfoCircle,
8
- IconStar,
9
- IconTicket,
10
- IconX
11
- } from "@tabler/icons-react";
12
- import { Dialog, DialogClose, DialogContent, DialogTitle } from "../ui/overlay/Dialog";
13
- import { Toast } from "../ui/feedback/Toast";
14
- import { Button } from "../ui/buttons/Button";
15
- import useCopyToClipboard from "../../hooks/copy-to-clipboard.hook";
16
- import { useModalManager } from "../../store/useModalManager";
17
- import { useWhitelabelUrls } from "../../providers/whitelabel.provider";
18
- import { cn } from "../../infra/utils/clsx";
19
- const COPY = {
20
- title: "Rumo ao Hexa!",
21
- subtitlePrefix: "Ofertas exclusivas at\xE9 ",
22
- subtitleDate: "24/06",
23
- mostChosen: "Mais escolhido",
24
- couponLabel: "Cupom",
25
- choosePlan: "Escolher plano",
26
- couponCopied: "Cupom copiado!",
27
- close: "Fechar",
28
- footerPrefix: "V\xE1lido para a ",
29
- footerBold: "primeira contrata\xE7\xE3o",
30
- footerSuffix: " dos planos Essencial, Neg\xF3cio ou Ag\xEAncia"
31
- };
32
- const COMBOS = [
33
- {
34
- coupon: "MENSAL10",
35
- name: "Combo convoca\xE7\xE3o",
36
- highlighted: false,
37
- period: "monthly",
38
- features: ["10% OFF no plano mensal", "Webinars exclusivos"]
39
- },
40
- {
41
- coupon: "SEMESTRAL10",
42
- name: "Combo Hexa",
43
- highlighted: true,
44
- period: "semiannual",
45
- features: ["10% OFF no plano semestral", "3 templates exclusivos", "Webinars exclusivos"]
46
- },
47
- {
48
- coupon: "ANUAL10",
49
- name: "Combo escala\xE7\xE3o",
50
- highlighted: false,
51
- period: "annual",
52
- features: ["10% OFF no plano anual", "3 templates exclusivos"]
53
- }
54
- ];
55
- const GRADIENT = "linear-gradient(180deg, #08c44f 0%, #7bd237 46.6%, #ffe31b 100%)";
56
- function HexaPromoModal() {
57
- const { activeModal, closeModal } = useModalManager();
58
- const { accountsUrl } = useWhitelabelUrls();
59
- const isOpen = activeModal === "hexaPromoModal";
60
- const goToPlans = (coupon, period) => {
61
- closeModal();
62
- const url = `${accountsUrl}/subscriptions?tab=plans&coupon=${encodeURIComponent(coupon)}&period=${encodeURIComponent(period)}`;
63
- window.location.assign(url);
64
- };
65
- return /* @__PURE__ */ jsx(Dialog, { open: isOpen, onOpenChange: () => closeModal(), children: /* @__PURE__ */ jsxs(
66
- DialogContent,
67
- {
68
- showCloseButton: false,
69
- onOpenAutoFocus: (e) => e.preventDefault(),
70
- className: "border-0 bg-transparent p-0 shadow-none w-[calc(100%-32px)] sm:max-w-[940px] md:w-[924px] lg:w-[924px] max-h-[calc(100dvh-32px)] overflow-y-auto",
71
- children: [
72
- /* @__PURE__ */ jsx(DialogTitle, { className: "sr-only", children: COPY.title }),
73
- /* @__PURE__ */ jsx("div", { className: "rounded-[12px] p-1", style: { background: GRADIENT }, children: /* @__PURE__ */ jsxs("div", { className: "relative overflow-hidden rounded-[10px] bg-white p-8 shadow-[0px_2px_10px_0px_rgba(26,26,26,0.05)]", children: [
74
- /* @__PURE__ */ jsx(
75
- "img",
76
- {
77
- src: "/promo/hexa-confetti.png",
78
- alt: "",
79
- "aria-hidden": true,
80
- className: "pointer-events-none absolute -top-10 right-0 w-[280px] rotate-[16deg] select-none"
81
- }
82
- ),
83
- /* @__PURE__ */ jsxs(DialogClose, { className: "absolute right-3 top-3 z-10 flex size-[34px] cursor-pointer items-center justify-center rounded-lg bg-white p-2 text-zinc-500 transition-colors hover:bg-zinc-100 hover:text-zinc-950 focus:outline-none", children: [
84
- /* @__PURE__ */ jsx("span", { className: "sr-only", children: COPY.close }),
85
- /* @__PURE__ */ jsx(IconX, { className: "size-[18px]" })
86
- ] }),
87
- /* @__PURE__ */ jsxs("div", { className: "relative z-[1] flex w-fit items-center gap-[9px] rounded-lg bg-zinc-100 px-[9px] py-2", children: [
88
- /* @__PURE__ */ jsx("img", { src: "/promo/hexa-logo.svg", alt: "", "aria-hidden": true, className: "size-6 select-none" }),
89
- /* @__PURE__ */ jsx("img", { src: "/promo/hexa-flag.png", alt: "", "aria-hidden": true, className: "size-6 rounded select-none" })
90
- ] }),
91
- /* @__PURE__ */ jsxs("div", { className: "relative z-[1] mt-4 flex flex-col gap-1", children: [
92
- /* @__PURE__ */ jsx("h2", { className: "font-['Outfit'] text-[32px] font-semibold leading-8 text-zinc-950", children: COPY.title }),
93
- /* @__PURE__ */ jsxs("p", { className: "paragraph-medium-medium text-zinc-950", children: [
94
- COPY.subtitlePrefix,
95
- /* @__PURE__ */ jsx("span", { className: "font-bold", children: COPY.subtitleDate })
96
- ] })
97
- ] }),
98
- /* @__PURE__ */ jsx("div", { className: "relative z-[1] mt-10 flex flex-col items-stretch gap-3 md:flex-row", children: COMBOS.map((combo) => /* @__PURE__ */ jsx(ComboCard, { combo, onChoose: () => goToPlans(combo.coupon, combo.period) }, combo.coupon)) }),
99
- /* @__PURE__ */ jsxs("div", { className: "relative z-[1] mt-3 flex items-center justify-center gap-2.5 rounded-[10px] bg-zinc-100 p-3 text-center", children: [
100
- /* @__PURE__ */ jsx(IconInfoCircle, { className: "size-4 shrink-0 text-zinc-950" }),
101
- /* @__PURE__ */ jsxs("p", { className: "paragraph-small-medium text-zinc-950", children: [
102
- COPY.footerPrefix,
103
- /* @__PURE__ */ jsx("span", { className: "font-semibold", children: COPY.footerBold }),
104
- COPY.footerSuffix
105
- ] })
106
- ] })
107
- ] }) })
108
- ]
109
- }
110
- ) });
111
- }
112
- function ComboCard({ combo, onChoose }) {
113
- const { isCopied, copy } = useCopyToClipboard({
114
- onSuccess: () => {
115
- toast.custom((t) => /* @__PURE__ */ jsx(Toast, { variant: "success", message: COPY.couponCopied, toastId: t }));
116
- }
117
- });
118
- return /* @__PURE__ */ jsxs(
119
- "div",
120
- {
121
- className: cn(
122
- "relative flex flex-1 flex-col rounded-[10px] border bg-white pb-3 md:w-[276px]",
123
- combo.highlighted ? "border-cyan-500" : "border-zinc-200"
124
- ),
125
- children: [
126
- combo.highlighted && /* @__PURE__ */ jsxs("div", { className: "absolute left-1/2 top-0 z-[1] flex h-6 -translate-x-1/2 -translate-y-1/2 items-center justify-center gap-[5px] rounded-md border-4 border-white bg-cyan-100 px-2", children: [
127
- /* @__PURE__ */ jsx(IconStar, { className: "size-3 text-cyan-600" }),
128
- /* @__PURE__ */ jsx("span", { className: "paragraph-xsmall-semibold whitespace-nowrap text-cyan-600", children: COPY.mostChosen }),
129
- /* @__PURE__ */ jsx(IconStar, { className: "size-3 text-cyan-600" })
130
- ] }),
131
- /* @__PURE__ */ jsx("h3", { className: "border-b border-zinc-200 px-4 py-4 text-center font-['Outfit'] text-base font-semibold text-zinc-950", children: combo.name }),
132
- /* @__PURE__ */ jsx("ul", { className: "flex flex-1 flex-col gap-5 px-5 pt-5", children: combo.features.map((feature) => /* @__PURE__ */ jsxs("li", { className: "flex items-center gap-2.5", children: [
133
- /* @__PURE__ */ jsx(IconCheck, { className: "size-5 shrink-0 text-cyan-500" }),
134
- /* @__PURE__ */ jsx("span", { className: "paragraph-small-medium text-zinc-950", children: feature })
135
- ] }, feature)) }),
136
- /* @__PURE__ */ jsxs("div", { className: "mx-3 mt-5 flex items-center gap-3 rounded-lg bg-cyan-50 py-2 pl-3.5 pr-2.5", children: [
137
- /* @__PURE__ */ jsx(IconTicket, { className: "size-5 shrink-0 text-cyan-600" }),
138
- /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 flex-col", children: [
139
- /* @__PURE__ */ jsx("span", { className: "paragraph-xsmall-medium text-zinc-400", children: COPY.couponLabel }),
140
- /* @__PURE__ */ jsx("span", { className: "paragraph-small-semibold text-zinc-950", children: combo.coupon })
141
- ] }),
142
- /* @__PURE__ */ jsx(
143
- "button",
144
- {
145
- type: "button",
146
- "aria-label": COPY.couponLabel,
147
- onClick: () => copy(combo.coupon),
148
- className: "flex size-8 shrink-0 cursor-pointer items-center justify-center rounded-md text-zinc-500 transition-colors hover:bg-cyan-100 hover:text-zinc-950",
149
- children: isCopied ? /* @__PURE__ */ jsx(IconCheck, { className: "size-[18px]" }) : /* @__PURE__ */ jsx(IconCopy, { className: "size-[18px]" })
150
- }
151
- )
152
- ] }),
153
- /* @__PURE__ */ jsx("div", { className: "mx-3 mt-3", children: /* @__PURE__ */ jsx(
154
- Button,
155
- {
156
- variant: combo.highlighted ? "default" : "secondary",
157
- className: cn("h-11! w-full", combo.highlighted && "bg-cyan-500 text-white hover:bg-cyan-600"),
158
- onClick: onChoose,
159
- children: COPY.choosePlan
160
- }
161
- ) })
162
- ]
163
- }
164
- );
165
- }
166
- export {
167
- HexaPromoModal as default
168
- };
169
- //# sourceMappingURL=HexaPromoModal.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../../src/components/modals/HexaPromoModal.tsx"],"sourcesContent":["'use client';\n\nimport { toast } from 'sonner';\nimport {\n IconCheck,\n IconCopy,\n IconInfoCircle,\n IconStar,\n IconTicket,\n IconX,\n} from '@tabler/icons-react';\nimport { Dialog, DialogClose, DialogContent, DialogTitle } from '../ui/overlay/Dialog';\nimport { Toast } from '../ui/feedback/Toast';\nimport { Button } from '../ui/buttons/Button';\nimport useCopyToClipboard from '../../hooks/copy-to-clipboard.hook';\nimport { useModalManager } from '../../store/useModalManager';\nimport { useWhitelabelUrls } from '../../providers/whitelabel.provider';\nimport { cn } from '../../infra/utils/clsx';\n\ntype ComboPeriod = 'monthly' | 'semiannual' | 'annual';\n\ninterface HexaCombo {\n coupon: string;\n name: string;\n features: string[];\n highlighted: boolean;\n period: ComboPeriod;\n}\n\n// Campanha \"Rumo ao Hexa\" — exclusiva da whitelabel 1 (público pt-br). O texto é fixo de\n// propósito: a modal precisa renderizar também no gapps, que não tem provider de next-intl.\nconst COPY = {\n title: 'Rumo ao Hexa!',\n subtitlePrefix: 'Ofertas exclusivas até ',\n subtitleDate: '24/06',\n mostChosen: 'Mais escolhido',\n couponLabel: 'Cupom',\n choosePlan: 'Escolher plano',\n couponCopied: 'Cupom copiado!',\n close: 'Fechar',\n footerPrefix: 'Válido para a ',\n footerBold: 'primeira contratação',\n footerSuffix: ' dos planos Essencial, Negócio ou Agência',\n};\n\nconst COMBOS: HexaCombo[] = [\n {\n coupon: 'MENSAL10',\n name: 'Combo convocação',\n highlighted: false,\n period: 'monthly',\n features: ['10% OFF no plano mensal', 'Webinars exclusivos'],\n },\n {\n coupon: 'SEMESTRAL10',\n name: 'Combo Hexa',\n highlighted: true,\n period: 'semiannual',\n features: ['10% OFF no plano semestral', '3 templates exclusivos', 'Webinars exclusivos'],\n },\n {\n coupon: 'ANUAL10',\n name: 'Combo escalação',\n highlighted: false,\n period: 'annual',\n features: ['10% OFF no plano anual', '3 templates exclusivos'],\n },\n];\n\n// Cores promocionais (verde/amarelo do Brasil) — específicas desta campanha, fora do design\n// system, por isso ficam hardcoded apenas aqui.\nconst GRADIENT = 'linear-gradient(180deg, #08c44f 0%, #7bd237 46.6%, #ffe31b 100%)';\n\nexport default function HexaPromoModal() {\n const { activeModal, closeModal } = useModalManager();\n const { accountsUrl } = useWhitelabelUrls();\n const isOpen = activeModal === 'hexaPromoModal';\n\n const goToPlans = (coupon: string, period: ComboPeriod) => {\n closeModal();\n const url = `${accountsUrl}/subscriptions?tab=plans&coupon=${encodeURIComponent(coupon)}&period=${encodeURIComponent(period)}`;\n window.location.assign(url);\n };\n\n return (\n <Dialog open={isOpen} onOpenChange={() => closeModal()}>\n <DialogContent\n showCloseButton={false}\n onOpenAutoFocus={(e) => e.preventDefault()}\n className=\"border-0 bg-transparent p-0 shadow-none w-[calc(100%-32px)] sm:max-w-[940px] md:w-[924px] lg:w-[924px] max-h-[calc(100dvh-32px)] overflow-y-auto\"\n >\n <DialogTitle className=\"sr-only\">{COPY.title}</DialogTitle>\n\n <div className=\"rounded-[12px] p-1\" style={{ background: GRADIENT }}>\n <div className=\"relative overflow-hidden rounded-[10px] bg-white p-8 shadow-[0px_2px_10px_0px_rgba(26,26,26,0.05)]\">\n {/* Arte decorativa (festão/bandeira) no canto superior direito */}\n <img\n src=\"/promo/hexa-confetti.png\"\n alt=\"\"\n aria-hidden\n className=\"pointer-events-none absolute -top-10 right-0 w-[280px] rotate-[16deg] select-none\"\n />\n\n <DialogClose className=\"absolute right-3 top-3 z-10 flex size-[34px] cursor-pointer items-center justify-center rounded-lg bg-white p-2 text-zinc-500 transition-colors hover:bg-zinc-100 hover:text-zinc-950 focus:outline-none\">\n <span className=\"sr-only\">{COPY.close}</span>\n <IconX className=\"size-[18px]\" />\n </DialogClose>\n\n {/* Header: logo + bandeira */}\n <div className=\"relative z-[1] flex w-fit items-center gap-[9px] rounded-lg bg-zinc-100 px-[9px] py-2\">\n <img src=\"/promo/hexa-logo.svg\" alt=\"\" aria-hidden className=\"size-6 select-none\" />\n <img src=\"/promo/hexa-flag.png\" alt=\"\" aria-hidden className=\"size-6 rounded select-none\" />\n </div>\n\n {/* Título */}\n <div className=\"relative z-[1] mt-4 flex flex-col gap-1\">\n <h2 className=\"font-['Outfit'] text-[32px] font-semibold leading-8 text-zinc-950\">\n {COPY.title}\n </h2>\n <p className=\"paragraph-medium-medium text-zinc-950\">\n {COPY.subtitlePrefix}\n <span className=\"font-bold\">{COPY.subtitleDate}</span>\n </p>\n </div>\n\n {/* Cards */}\n <div className=\"relative z-[1] mt-10 flex flex-col items-stretch gap-3 md:flex-row\">\n {COMBOS.map((combo) => (\n <ComboCard key={combo.coupon} combo={combo} onChoose={() => goToPlans(combo.coupon, combo.period)} />\n ))}\n </div>\n\n {/* Rodapé informativo */}\n <div className=\"relative z-[1] mt-3 flex items-center justify-center gap-2.5 rounded-[10px] bg-zinc-100 p-3 text-center\">\n <IconInfoCircle className=\"size-4 shrink-0 text-zinc-950\" />\n <p className=\"paragraph-small-medium text-zinc-950\">\n {COPY.footerPrefix}\n <span className=\"font-semibold\">{COPY.footerBold}</span>\n {COPY.footerSuffix}\n </p>\n </div>\n </div>\n </div>\n </DialogContent>\n </Dialog>\n );\n}\n\ninterface ComboCardProps {\n combo: HexaCombo;\n onChoose: () => void;\n}\n\nfunction ComboCard({ combo, onChoose }: ComboCardProps) {\n const { isCopied, copy } = useCopyToClipboard({\n onSuccess: () => {\n toast.custom((t) => <Toast variant=\"success\" message={COPY.couponCopied} toastId={t} />);\n },\n });\n\n return (\n <div\n className={cn(\n 'relative flex flex-1 flex-col rounded-[10px] border bg-white pb-3 md:w-[276px]',\n combo.highlighted ? 'border-cyan-500' : 'border-zinc-200',\n )}\n >\n {combo.highlighted && (\n <div className=\"absolute left-1/2 top-0 z-[1] flex h-6 -translate-x-1/2 -translate-y-1/2 items-center justify-center gap-[5px] rounded-md border-4 border-white bg-cyan-100 px-2\">\n <IconStar className=\"size-3 text-cyan-600\" />\n <span className=\"paragraph-xsmall-semibold whitespace-nowrap text-cyan-600\">\n {COPY.mostChosen}\n </span>\n <IconStar className=\"size-3 text-cyan-600\" />\n </div>\n )}\n\n <h3 className=\"border-b border-zinc-200 px-4 py-4 text-center font-['Outfit'] text-base font-semibold text-zinc-950\">\n {combo.name}\n </h3>\n\n <ul className=\"flex flex-1 flex-col gap-5 px-5 pt-5\">\n {combo.features.map((feature) => (\n <li key={feature} className=\"flex items-center gap-2.5\">\n <IconCheck className=\"size-5 shrink-0 text-cyan-500\" />\n <span className=\"paragraph-small-medium text-zinc-950\">{feature}</span>\n </li>\n ))}\n </ul>\n\n <div className=\"mx-3 mt-5 flex items-center gap-3 rounded-lg bg-cyan-50 py-2 pl-3.5 pr-2.5\">\n <IconTicket className=\"size-5 shrink-0 text-cyan-600\" />\n <div className=\"flex min-w-0 flex-1 flex-col\">\n <span className=\"paragraph-xsmall-medium text-zinc-400\">{COPY.couponLabel}</span>\n <span className=\"paragraph-small-semibold text-zinc-950\">{combo.coupon}</span>\n </div>\n <button\n type=\"button\"\n aria-label={COPY.couponLabel}\n onClick={() => copy(combo.coupon)}\n className=\"flex size-8 shrink-0 cursor-pointer items-center justify-center rounded-md text-zinc-500 transition-colors hover:bg-cyan-100 hover:text-zinc-950\"\n >\n {isCopied ? <IconCheck className=\"size-[18px]\" /> : <IconCopy className=\"size-[18px]\" />}\n </button>\n </div>\n\n <div className=\"mx-3 mt-3\">\n <Button\n variant={combo.highlighted ? 'default' : 'secondary'}\n className={cn('h-11! w-full', combo.highlighted && 'bg-cyan-500 text-white hover:bg-cyan-600')}\n onClick={onChoose}\n >\n {COPY.choosePlan}\n </Button>\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":[]}
@@ -1,61 +0,0 @@
1
- "use client";
2
- import { useEffect, useRef } from "react";
3
- import { useActiveSubscription } from "../../modules/subscriptions/hooks/find-active-subscription.hook";
4
- import { useAuth } from "../../providers/auth.provider";
5
- import { useWhitelabel } from "../../providers/whitelabel.provider";
6
- import { useModalManager } from "../../store/useModalManager";
7
- import {
8
- getHexaPromoSeenDay,
9
- getHexaPromoSince,
10
- hexaPromoDayKey,
11
- setHexaPromoSeenDay,
12
- setHexaPromoSince
13
- } from "../../utils/cookies/hexa-promo";
14
- const ONLINE_DELAY_MS = 3 * 60 * 1e3;
15
- const FORCE_OPEN_FOR_TEST = false;
16
- function HexaPromoModalGate() {
17
- const { isAuthenticated } = useAuth();
18
- const { whitelabel } = useWhitelabel();
19
- const { activeModal, openModal, closeModal } = useModalManager();
20
- const { data } = useActiveSubscription();
21
- const subscription = data?.data?.[0] ?? null;
22
- const eligible = isAuthenticated && whitelabel?.id === 1 && subscription?.type === "trial";
23
- const eligibleRef = useRef(eligible);
24
- eligibleRef.current = eligible;
25
- const forcedRef = useRef(false);
26
- useEffect(() => {
27
- if (FORCE_OPEN_FOR_TEST) return;
28
- if (!eligible && activeModal === "hexaPromoModal") closeModal();
29
- }, [eligible, activeModal, closeModal]);
30
- useEffect(() => {
31
- if (typeof window === "undefined") return;
32
- if (FORCE_OPEN_FOR_TEST) {
33
- if (forcedRef.current || activeModal) return;
34
- forcedRef.current = true;
35
- openModal("hexaPromoModal");
36
- return;
37
- }
38
- if (!eligible) return;
39
- if (activeModal) return;
40
- const todayKey = hexaPromoDayKey();
41
- if (getHexaPromoSeenDay() === todayKey) return;
42
- let since = getHexaPromoSince();
43
- if (!since || since.day !== todayKey) {
44
- since = { day: todayKey, ms: Date.now() };
45
- setHexaPromoSince(since.day, since.ms);
46
- }
47
- const remaining = Math.max(0, since.ms + ONLINE_DELAY_MS - Date.now());
48
- const timer = setTimeout(() => {
49
- if (!eligibleRef.current) return;
50
- if (getHexaPromoSeenDay() === todayKey) return;
51
- setHexaPromoSeenDay(todayKey);
52
- openModal("hexaPromoModal");
53
- }, remaining);
54
- return () => clearTimeout(timer);
55
- }, [eligible, activeModal, openModal]);
56
- return null;
57
- }
58
- export {
59
- HexaPromoModalGate as default
60
- };
61
- //# sourceMappingURL=HexaPromoModalGate.mjs.map