@greatapps/common 1.1.746 → 1.1.747

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.
@@ -1,38 +1,43 @@
1
1
  "use client";
2
2
 
3
- import { IconLock, IconAlertTriangle } from '@tabler/icons-react';
3
+ import {
4
+ IconLock,
5
+ IconFile,
6
+ IconFolder,
7
+ IconWorld,
8
+ IconInfoCircle,
9
+ type IconProps,
10
+ } from '@tabler/icons-react';
11
+ import type { ComponentType } from 'react';
4
12
  import { useTranslations } from 'next-intl';
5
13
  import { Button } from '../ui/buttons/Button';
6
- import { Separator } from '../ui/data-display/Separator';
7
14
  import {
8
15
  Dialog,
9
16
  DialogContent,
10
17
  DialogDescription,
11
- DialogHeader,
12
18
  DialogTitle,
13
19
  } from '../ui/overlay/Dialog';
20
+ import { Tooltip, TooltipContent, TooltipTrigger } from '../ui/overlay/Tooltip';
14
21
  import { useWhitelabelUrls, useExternalContracting } from '../../providers/whitelabel.provider';
15
22
  import { useModalManager } from '../../store/useModalManager';
16
23
  import { formatShortDate } from '../../infra/utils/date';
17
24
 
18
- /* Lista do que o congelamento tira, na ordem do card. O texto mora no catálogo. */
19
- const BLOCKED_RESOURCE_KEYS = [
20
- 'pages',
21
- 'projects',
22
- 'leadsExport',
23
- 'pageSettings',
24
- 'domains',
25
- 'users',
26
- 'abTests',
27
- 'uploads',
28
- 'integrations',
29
- ] as const;
25
+ /* Os 9 recursos do card agrupados nos 3 grupos que o usuário reconhece na plataforma.
26
+ * Um ícone por grupo; o texto mora no catálogo. */
27
+ const RESOURCE_GROUPS: { key: string; icon: ComponentType<IconProps> }[] = [
28
+ { key: 'pages', icon: IconFile },
29
+ { key: 'projects', icon: IconFolder },
30
+ { key: 'domains', icon: IconWorld },
31
+ ];
30
32
 
31
- /* `freezeDate` no data = ainda VAI congelar (véspera); sem ele = já congelado. As duas
32
- * variantes listam os mesmos recursos: o ponto do "Detalhes" no banner é o usuário saber o
33
- * que perde ANTES de perder. */
33
+ /* `freezeDate` no data = ainda VAI congelar (véspera); sem ele = já congelado.
34
+ *
35
+ * A LISTA aparece quando o usuário PEDIU por ela, clicando em "Detalhes" no banner
36
+ * (`showResources`). Na modal que abre sozinha ao entrar numa conta bloqueada fica só o
37
+ * recado curto e o botão. */
34
38
  interface FrozenModalData {
35
39
  freezeDate?: Date | string | null;
40
+ showResources?: boolean;
36
41
  }
37
42
 
38
43
  export default function AccountFrozenModal() {
@@ -42,8 +47,10 @@ export default function AccountFrozenModal() {
42
47
  const { accountsUrl } = useWhitelabelUrls();
43
48
  const { redirectToExternal } = useExternalContracting();
44
49
 
45
- const freezeDate = (modalData as FrozenModalData | undefined)?.freezeDate ?? null;
50
+ const data = modalData as FrozenModalData | undefined;
51
+ const freezeDate = data?.freezeDate ?? null;
46
52
  const isWarning = Boolean(freezeDate);
53
+ const showResources = Boolean(data?.showResources);
47
54
 
48
55
  const title = isWarning
49
56
  ? translate('common.subscription.frozen.modal.warningTitle', {
@@ -51,9 +58,18 @@ export default function AccountFrozenModal() {
51
58
  })
52
59
  : translate('common.subscription.frozen.modal.title');
53
60
 
61
+ /* Sem a lista o texto tem que fechar sozinho; com a lista ele a INTRODUZ (termina em ":"). */
54
62
  const description = isWarning
55
- ? translate('common.subscription.frozen.modal.warningDescription')
56
- : translate('common.subscription.frozen.modal.description');
63
+ ? translate(
64
+ showResources
65
+ ? 'common.subscription.frozen.modal.warningDescriptionWithResources'
66
+ : 'common.subscription.frozen.modal.warningDescription',
67
+ )
68
+ : translate(
69
+ showResources
70
+ ? 'common.subscription.frozen.modal.descriptionWithResources'
71
+ : 'common.subscription.frozen.modal.description',
72
+ );
57
73
 
58
74
  const handleRegularize = () => {
59
75
  if (redirectToExternal('plans')) return;
@@ -62,41 +78,93 @@ export default function AccountFrozenModal() {
62
78
 
63
79
  return (
64
80
  <Dialog open={isOpen} onOpenChange={() => closeModal()}>
65
- <DialogContent className="flex flex-col p-0 gap-0 w-full md:w-[410px] lg:w-[410px]">
66
- <DialogHeader className="gap-3 text-left p-5">
67
- <div
68
- className={`flex items-center justify-center w-10 h-10 rounded-lg ${isWarning ? 'bg-orange-50' : 'bg-zinc-50'}`}
69
- >
70
- {isWarning ? (
71
- <IconAlertTriangle size={20} className="text-orange-500" />
72
- ) : (
73
- <IconLock size={24} className="text-zinc-950" />
74
- )}
81
+ <DialogContent
82
+ showCloseButton
83
+ /* Sem isto o Radix move o foco para o primeiro focável ao abrir — que aqui é o
84
+ * gatilho do tooltip e o tooltip abre em FOCO, não só em hover: a modal
85
+ * aparecia com um balão já aberto. O foco segue preso no dialog. */
86
+ onOpenAutoFocus={(event) => event.preventDefault()}
87
+ className="flex flex-col p-0! gap-0! max-w-[calc(100%-2rem)]! w-full sm:max-w-[400px]! lg:max-w-[420px]! rounded-lg border"
88
+ >
89
+ <div className="flex flex-col gap-5 p-4 lg:p-5">
90
+ <div className="flex items-center justify-center w-10 h-10 bg-zinc-50 rounded-lg">
91
+ <IconLock size={20} className="text-zinc-950" />
75
92
  </div>
93
+
76
94
  <div className="flex flex-col gap-2">
77
95
  <DialogTitle className="paragraph-medium-semibold text-zinc-950">
78
96
  {title}
79
97
  </DialogTitle>
80
- <DialogDescription className="paragraph-small-regular text-zinc-600" asChild>
81
- <div className="flex flex-col gap-3">
82
- <p>{description}</p>
83
- <ul className="flex flex-col gap-1.5 list-disc pl-4">
84
- {BLOCKED_RESOURCE_KEYS.map((key) => (
85
- <li key={key}>
86
- {translate(`common.subscription.frozen.modal.resources.${key}`)}
87
- </li>
88
- ))}
89
- </ul>
90
- <p>{translate('common.subscription.frozen.modal.keepsWorking')}</p>
91
- </div>
98
+ <DialogDescription className="paragraph-small-regular text-zinc-500">
99
+ {description}
92
100
  </DialogDescription>
93
101
  </div>
94
- </DialogHeader>
95
102
 
96
- <Separator />
103
+ {showResources && (
104
+ <>
105
+ {/* Um bloco só, grupos separados por linha. A descrição de cada grupo
106
+ * vive no tooltip do ícone de info à direita: na modal ela empilhava
107
+ * três parágrafos e virava um paredão. */}
108
+ <div className="bg-white border border-zinc-100 rounded-lg">
109
+ {RESOURCE_GROUPS.map(({ key, icon: Icon }, index) => (
110
+ <div key={key}>
111
+ {index > 0 && <div className="h-px bg-zinc-100" />}
112
+ <div className="flex items-center justify-between gap-3 p-4">
113
+ <div className="flex items-center gap-3">
114
+ <Icon size={20} className="text-zinc-950 shrink-0" />
115
+ <span className="paragraph-small-semibold text-zinc-950">
116
+ {translate(
117
+ `common.subscription.frozen.modal.resources.${key}.title`,
118
+ )}
119
+ </span>
120
+ </div>
121
+ <Tooltip>
122
+ <TooltipTrigger asChild>
123
+ <button
124
+ type="button"
125
+ aria-label={translate(
126
+ `common.subscription.frozen.modal.resources.${key}.title`,
127
+ )}
128
+ className="flex items-center justify-center shrink-0 cursor-pointer"
129
+ >
130
+ <IconInfoCircle className="size-4 text-zinc-400" />
131
+ </button>
132
+ </TooltipTrigger>
133
+ <TooltipContent
134
+ side="top"
135
+ /* Acima do dialog: o globals.css do gapps-app
136
+ * sobe o [data-slot='dialog-content'] para
137
+ * z-index 1011 !important (o componente
138
+ * declara 1001), então o z-50 padrão do
139
+ * tooltip ficava atrás. */
140
+ className="z-[1100] max-w-[240px]"
141
+ >
142
+ {translate(
143
+ `common.subscription.frozen.modal.resources.${key}.description`,
144
+ )}
145
+ </TooltipContent>
146
+ </Tooltip>
147
+ </div>
148
+ </div>
149
+ ))}
150
+ </div>
151
+
152
+ {/* Bloco de informação padrão da plataforma. Ciano fixo: o congelamento
153
+ * é exclusivo da wl 1, então não existe o caso whitelabel/zinc aqui. */}
154
+ <div className="flex items-center gap-3 p-4 rounded-lg bg-cyan-50">
155
+ <IconInfoCircle className="size-4 text-zinc-950 shrink-0" />
156
+ <span className="paragraph-small-regular text-zinc-950">
157
+ {translate('common.subscription.frozen.modal.keepsWorking')}
158
+ </span>
159
+ </div>
160
+ </>
161
+ )}
162
+ </div>
163
+
164
+ <div className="h-px bg-zinc-200" />
97
165
 
98
- <div className="flex items-center gap-2 p-5">
99
- <Button className="w-fit h-10!" onClick={handleRegularize}>
166
+ <div className="flex items-center gap-2 p-4 lg:p-5">
167
+ <Button className="h-10 w-fit" onClick={handleRegularize}>
100
168
  {translate('common.subscription.frozen.modal.cta')}
101
169
  </Button>
102
170
  </div>
@@ -93,7 +93,7 @@ export function SubscriptionBanner({
93
93
  // que responde outra pergunta. Aqui "Detalhes" significa "o que eu perco".
94
94
  if (freezeState === "frozen") {
95
95
  return (
96
- <FrozenAccountBanner onDetails={() => openModal("accountFrozenModal")} />
96
+ <FrozenAccountBanner onDetails={() => openModal("accountFrozenModal", { showResources: true })} />
97
97
  );
98
98
  }
99
99
  if (freezeState === "warning") {
@@ -101,7 +101,7 @@ export function SubscriptionBanner({
101
101
  return (
102
102
  <FreezeWarningBanner
103
103
  freezeDate={freezeDate}
104
- onDetails={() => openModal("accountFrozenModal", { freezeDate })}
104
+ onDetails={() => openModal("accountFrozenModal", { freezeDate, showResources: true })}
105
105
  />
106
106
  );
107
107
  }
@@ -444,20 +444,25 @@ const messages = {
444
444
  },
445
445
  modal: {
446
446
  title: 'Your account resources are blocked',
447
- description: 'We could not identify your payment, so these resources are blocked:',
447
+ description: 'Your account resources are blocked due to missing payment. Update your subscription to unlock them.',
448
+ descriptionWithResources: 'We could not identify your payment, so these resources are blocked:',
449
+ warningDescriptionWithResources: 'Your payment is overdue. If we do not identify it by then, these resources will be blocked:',
448
450
  warningTitle: 'Your resources will be blocked on {freezeDate}',
449
- warningDescription: 'Your payment is overdue. If we do not identify it by then, these resources will be blocked:',
451
+ warningDescription: 'Your payment is overdue. Settle it to keep access to your account resources.',
450
452
  keepsWorking: 'Your published pages stay online and your leads keep coming in. Everything is restored automatically once the payment goes through.',
451
453
  resources: {
452
- pages: 'Publishing, creating and editing pages',
453
- projects: 'Creating and editing projects',
454
- leadsExport: 'Exporting leads',
455
- pageSettings: 'Page settings, including lead notifications',
456
- domains: 'Connecting new domains',
457
- users: 'Inviting users and changing team permissions',
458
- abTests: 'Creating and editing A/B tests',
459
- uploads: 'Uploading images, PDFs and other files',
460
- integrations: 'Managing integrations',
454
+ pages: {
455
+ title: 'Manage pages',
456
+ description: 'Publishing, creating and editing pages, settings, A/B tests, file uploads and exporting leads',
457
+ },
458
+ projects: {
459
+ title: 'Manage projects',
460
+ description: 'Creating and editing projects, inviting users and changing team permissions',
461
+ },
462
+ domains: {
463
+ title: 'Manage domains',
464
+ description: 'Connecting new domains and managing integrations',
465
+ },
461
466
  },
462
467
  cta: 'Update subscription',
463
468
  },
@@ -445,20 +445,25 @@ const messages = {
445
445
  },
446
446
  modal: {
447
447
  title: 'Los recursos de tu cuenta están bloqueados',
448
- description: 'No identificamos el pago, así que estos recursos están bloqueados:',
448
+ description: 'Los recursos de tu cuenta están bloqueados por falta de pago. Regulariza la suscripción para liberarlos.',
449
+ descriptionWithResources: 'No identificamos el pago, así que estos recursos están bloqueados:',
450
+ warningDescriptionWithResources: 'El pago está atrasado. Si no lo identificamos hasta entonces, estos recursos se bloquearán:',
449
451
  warningTitle: 'Tus recursos se bloquearán el {freezeDate}',
450
- warningDescription: 'El pago está atrasado. Si no lo identificamos hasta entonces, estos recursos se bloquearán:',
452
+ warningDescription: 'El pago está atrasado. Regulariza para no perder el acceso a los recursos de tu cuenta.',
451
453
  keepsWorking: 'Tus páginas publicadas siguen en línea y tus leads se siguen capturando. Todo vuelve automáticamente en cuanto se identifique el pago.',
452
454
  resources: {
453
- pages: 'Publicar, crear y editar páginas',
454
- projects: 'Crear y editar proyectos',
455
- leadsExport: 'Exportar leads',
456
- pageSettings: 'Configuración de la página, incluidas las notificaciones de leads',
457
- domains: 'Conectar nuevos dominios',
458
- users: 'Invitar usuarios y cambiar permisos del equipo',
459
- abTests: 'Crear y editar pruebas A/B',
460
- uploads: 'Subir imágenes, PDF y otros archivos',
461
- integrations: 'Gestionar integraciones',
455
+ pages: {
456
+ title: 'Gestionar páginas',
457
+ description: 'Publicar, crear y editar páginas, configuración, pruebas A/B, subida de archivos y exportar leads',
458
+ },
459
+ projects: {
460
+ title: 'Gestionar proyectos',
461
+ description: 'Crear y editar proyectos, invitar usuarios y cambiar permisos del equipo',
462
+ },
463
+ domains: {
464
+ title: 'Gestionar dominios',
465
+ description: 'Conectar nuevos dominios y gestionar integraciones',
466
+ },
462
467
  },
463
468
  cta: 'Regularizar suscripción',
464
469
  },
@@ -462,20 +462,25 @@ const messages = {
462
462
  },
463
463
  modal: {
464
464
  title: 'Recursos da sua conta bloqueados',
465
- description: 'O pagamento não foi identificado, então estes recursos estão bloqueados:',
465
+ description: 'Sua conta está com os recursos bloqueados por falta de pagamento. Regularize a assinatura para liberar.',
466
+ descriptionWithResources: 'O pagamento não foi identificado, então estes recursos estão bloqueados:',
467
+ warningDescriptionWithResources: 'O pagamento está em atraso. Se não for identificado até lá, estes recursos serão bloqueados:',
466
468
  warningTitle: 'Seus recursos serão bloqueados em {freezeDate}',
467
- warningDescription: 'O pagamento está em atraso. Se não for identificado até lá, estes recursos serão bloqueados:',
469
+ warningDescription: 'O pagamento está em atraso. Regularize para não perder o acesso aos recursos da sua conta.',
468
470
  keepsWorking: 'Suas páginas publicadas continuam no ar e seus leads seguem sendo capturados. Assim que o pagamento for identificado, tudo volta automaticamente.',
469
471
  resources: {
470
- pages: 'Publicar, criar e editar páginas',
471
- projects: 'Criar e editar projetos',
472
- leadsExport: 'Exportar leads',
473
- pageSettings: 'Configurações da página, incluindo notificações de leads',
474
- domains: 'Conectar novos domínios',
475
- users: 'Convidar usuários e alterar permissões da equipe',
476
- abTests: 'Criar e editar testes A/B',
477
- uploads: 'Enviar imagens, PDFs e outros arquivos',
478
- integrations: 'Gerenciar integrações',
472
+ pages: {
473
+ title: 'Gerenciar páginas',
474
+ description: 'Publicar, criar e editar páginas, configurações, testes A/B, upload de arquivos e exportar leads',
475
+ },
476
+ projects: {
477
+ title: 'Gerenciar projetos',
478
+ description: 'Criar e editar projetos, convidar usuários e alterar permissões da equipe',
479
+ },
480
+ domains: {
481
+ title: 'Gerenciar domínios',
482
+ description: 'Conectar novos domínios e gerenciar integrações',
483
+ },
479
484
  },
480
485
  cta: 'Regularizar assinatura',
481
486
  },