@greatapps/common 1.1.770 → 1.1.771
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.
- package/dist/components/account/AccountModals.mjs +6 -3
- package/dist/components/account/AccountModals.mjs.map +1 -1
- package/dist/components/account/sections/ChangeEmailModal.mjs +1 -2
- package/dist/components/account/sections/ChangeEmailModal.mjs.map +1 -1
- package/dist/components/account/sections/ChangePhoneModal.mjs +1 -2
- package/dist/components/account/sections/ChangePhoneModal.mjs.map +1 -1
- package/dist/components/account/sections/MyProfileSection.mjs +5 -2
- package/dist/components/account/sections/MyProfileSection.mjs.map +1 -1
- package/dist/components/layouts/UsersSelectorPopover.mjs.map +1 -1
- package/dist/components/modals/AccountFrozenModal.mjs +1 -1
- package/dist/components/modals/AccountFrozenModal.mjs.map +1 -1
- package/dist/components/modals/promo/AiCreditsOfferModal/AiCreditsOfferModal.mjs +3 -2
- package/dist/components/modals/promo/AiCreditsOfferModal/AiCreditsOfferModal.mjs.map +1 -1
- package/dist/components/modals/promo/AiCreditsOfferModal/components/LoseOfferDialog.mjs +2 -2
- package/dist/components/modals/promo/AiCreditsOfferModal/components/LoseOfferDialog.mjs.map +1 -1
- package/dist/components/modals/promo/AiCreditsOfferModal/components/OfferPaymentField.mjs +1 -1
- package/dist/components/modals/promo/AiCreditsOfferModal/components/OfferPaymentField.mjs.map +1 -1
- package/dist/components/ui/form/PhoneInput.mjs +1 -1
- package/dist/components/ui/form/PhoneInput.mjs.map +1 -1
- package/dist/components/ui/form/Select.mjs +4 -1
- package/dist/components/ui/form/Select.mjs.map +1 -1
- package/dist/components/ui/overlay/Dialog.mjs +1 -12
- package/dist/components/ui/overlay/Dialog.mjs.map +1 -1
- package/dist/components/ui/overlay/Popover.mjs +4 -1
- package/dist/components/ui/overlay/Popover.mjs.map +1 -1
- package/dist/components/ui/overlay/Sheet.mjs +9 -3
- package/dist/components/ui/overlay/Sheet.mjs.map +1 -1
- package/dist/components/ui/overlay/Tooltip.mjs +4 -1
- package/dist/components/ui/overlay/Tooltip.mjs.map +1 -1
- package/dist/components/ui/overlay/layers.mjs +35 -0
- package/dist/components/ui/overlay/layers.mjs.map +1 -0
- package/dist/components/widgets/ImageUpload/ImageCropModal.mjs +1 -2
- package/dist/components/widgets/ImageUpload/ImageCropModal.mjs.map +1 -1
- package/dist/components/widgets/ImageUpload/ImageTooSmallModal.mjs +2 -2
- package/dist/components/widgets/ImageUpload/ImageTooSmallModal.mjs.map +1 -1
- package/dist/components/widgets/ImageUpload/ImageUpload.mjs +4 -2
- package/dist/components/widgets/ImageUpload/ImageUpload.mjs.map +1 -1
- package/dist/index.mjs +3 -0
- package/dist/index.mjs.map +1 -1
- package/dist/modules/subscriptions/types/subscription.type.mjs +1 -7
- package/dist/modules/subscriptions/types/subscription.type.mjs.map +1 -1
- package/package.json +7 -8
- package/src/components/account/AccountModals.tsx +9 -3
- package/src/components/account/sections/ChangeEmailModal.tsx +1 -2
- package/src/components/account/sections/ChangePhoneModal.tsx +1 -2
- package/src/components/account/sections/MyProfileSection.tsx +6 -2
- package/src/components/layouts/UsersSelectorPopover.tsx +5 -0
- package/src/components/modals/AccountFrozenModal.tsx +1 -1
- package/src/components/modals/promo/AiCreditsOfferModal/AiCreditsOfferModal.tsx +12 -8
- package/src/components/modals/promo/AiCreditsOfferModal/components/LoseOfferDialog.tsx +2 -2
- package/src/components/modals/promo/AiCreditsOfferModal/components/OfferPaymentField.tsx +1 -3
- package/src/components/ui/form/PhoneInput.tsx +1 -1
- package/src/components/ui/form/Select.tsx +5 -1
- package/src/components/ui/overlay/Dialog.tsx +4 -16
- package/src/components/ui/overlay/Popover.tsx +5 -1
- package/src/components/ui/overlay/Sheet.tsx +17 -3
- package/src/components/ui/overlay/Tooltip.tsx +6 -1
- package/src/components/ui/overlay/layers.tsx +95 -0
- package/src/components/widgets/ImageUpload/ImageCropModal.tsx +2 -3
- package/src/components/widgets/ImageUpload/ImageTooSmallModal.tsx +2 -2
- package/src/components/widgets/ImageUpload/ImageUpload.tsx +15 -11
- package/src/index.ts +1 -0
- package/src/modules/subscriptions/types/subscription.type.ts +2 -8
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../src/components/modals/promo/AiCreditsOfferModal/AiCreditsOfferModal.tsx"],"sourcesContent":["'use client';\n\nimport { useCallback, useState } from 'react';\nimport { useStripe } from '@stripe/react-stripe-js';\nimport { IconCheck, IconX } from '@tabler/icons-react';\nimport { useLocale, useTranslations } from 'next-intl';\nimport { toast } from 'sonner';\nimport { Button } from '../../../ui/buttons/Button';\nimport { Dialog, DialogContent, DialogTitle } from '../../../ui/overlay/Dialog';\nimport { Toast } from '../../../ui/feedback/Toast';\nimport { useAuth } from '../../../../providers/auth.provider';\nimport { useWhitelabel, useWhitelabelUrls } from '../../../../providers/whitelabel.provider';\nimport { useActiveSubscription } from '../../../../modules/subscriptions/hooks/find-active-subscription.hook';\nimport { useCards } from '../../../../modules/cards/hooks/cards.hook';\nimport { useCurrencyFormatter } from '../../../../modules/accounts/hooks/use-currency-formatter.hook';\nimport { confirmPaymentIfRequired } from '../../../../modules/subscriptions/utils/confirm-payment-if-required';\nimport {\n AI_CREDITS_FIRST_PURCHASE_OFFER,\n AiCreditsOfferStage,\n PAYMENT_METHOD_CARD,\n} from '../../../../modules/ia-credits/constants/ai-credits-offer.constants';\nimport { useAiCreditsOfferEligibility } from '../../../../modules/ia-credits/hooks/use-offer-eligibility.hook';\nimport { usePurchaseIaCredits } from '../../../../modules/ia-credits/hooks/purchase-ia-credits.hook';\nimport type { PurchaseIaCreditsResult } from '../../../../modules/ia-credits/types/ai-credits-offer.type';\nimport { readOfferStageCookie, writeOfferStageCookie } from '../../../../modules/ia-credits/utils/offer-stage-cookie';\nimport { useAiCreditsOfferModal } from '../../../../store/useAiCreditsOfferModal';\nimport { useModalManager } from '../../../../store/useModalManager';\nimport { LoseOfferDialog } from './components/LoseOfferDialog';\nimport { OfferIllustration } from './components/OfferIllustration';\nimport { OfferPaymentField } from './components/OfferPaymentField';\n\n/**\n * Oferta de primeira compra de créditos de IA — a tela que a base gratuita vê quando o\n * `AiCreditsOfferGate` decide que é a vez dela.\n *\n * Duas colunas, como a referência de produto: à esquerda a oferta e o cartão (via\n * `OfferPaymentField`, que cadastra pelo `addCardModal` padrão da plataforma); à direita o\n * gradiente da tela de login com a ilustração do editor.\n *\n * Fechar não fecha: `X`, `Esc`, clique fora e \"Agora não\" caem todos no `LoseOfferDialog`, que\n * pergunta se a pessoa quer mesmo perder o desconto.\n *\n * ⚠️ PREÇO: o card manda só o SLUG da oferta (`AI_CREDITS_FIRST_PURCHASE_OFFER.slug`) — quem\n * resolve pacote, preço e elegibilidade é o backend (Asana ID-1217333331953496). O \"de/por\" desta\n * tela usa `AI_CREDITS_FIRST_PURCHASE_OFFER.price` (fixo, parte do contrato da oferta) contra\n * `anchorCents` do `useAiCreditsOfferEligibility` (derivado do preço real do addon no plano — nunca\n * hardcoded).\n *\n * ANIMAÇÃO: CSS puro, sem `motion/react` (a lib não depende dela). As classes `gp-offer-rise`,\n * `gp-offer-window-in`, `gp-offer-card-in` e `gp-offer-icon-pulse` vêm do `globals.css` de cada app\n * consumidor — bloco documentado abaixo, precisa estar presente nos 3 apps (gpages/gapps/editor):\n *\n * ```css\n * @keyframes gp-offer-rise {\n * from { opacity: 0; translate: 0 10px; }\n * to { opacity: 1; translate: 0 0; }\n * }\n * .gp-offer-rise { animation: gp-offer-rise 420ms cubic-bezier(0.22, 1, 0.36, 1) both; }\n *\n * @keyframes gp-offer-window-in {\n * from { opacity: 0; translate: 0 18px; scale: 0.985; }\n * to { opacity: 1; translate: 0 0; scale: 1; }\n * }\n * .gp-offer-window-in { animation: gp-offer-window-in 550ms cubic-bezier(0.22, 1, 0.36, 1) 50ms both; }\n *\n * @keyframes gp-offer-card-in {\n * from { opacity: 0; translate: 0 26px; scale: 0.97; }\n * to { opacity: 1; translate: 0 0; scale: 1; }\n * }\n * .gp-offer-card-in { animation: gp-offer-card-in 520ms cubic-bezier(0.34, 1.56, 0.64, 1) 340ms both; }\n *\n * @keyframes gp-offer-icon-pulse {\n * 0%, 100% { scale: 1; }\n * 50% { scale: 1.06; }\n * }\n * .gp-offer-icon-pulse { animation: gp-offer-icon-pulse 2.6s ease-in-out 1.6s infinite; }\n *\n * @media (prefers-reduced-motion: reduce) {\n * .gp-offer-rise, .gp-offer-window-in, .gp-offer-card-in, .gp-offer-icon-pulse { animation: none; }\n * }\n * ```\n *\n * `translate`/`scale` (propriedades CSS independentes, não `transform`) pra conviver sem conflito\n * com utilitários de posicionamento do Tailwind (mesmo problema que já forçou o `.gp-offer-modal`\n * de entrada/saída a sair de `@layer` — ver `AiCreditsOfferGate`/`globals.css`).\n */\nexport default function AiCreditsOfferModal() {\n const translate = useTranslations();\n const locale = useLocale();\n const stripe = useStripe();\n const { open, closeModal } = useAiCreditsOfferModal();\n const { openModal } = useModalManager();\n const { user } = useAuth();\n const { whitelabel } = useWhitelabel();\n const { accountsUrl } = useWhitelabelUrls();\n const { formatCurrencyNumber } = useCurrencyFormatter();\n\n const [confirmingExit, setConfirmingExit] = useState(false);\n const [pickedCardId, setPickedCardId] = useState('');\n\n const { data: cardsData } = useCards();\n const { data: subscriptionData } = useActiveSubscription();\n const eligibility = useAiCreditsOfferEligibility();\n const purchaseMutation = usePurchaseIaCredits();\n\n const subscription = subscriptionData?.data?.[0] ?? null;\n const subscriptionId = subscription?.id;\n const accountId = user?.id_account;\n\n const defaultCardId =\n cardsData?.data?.find((card) => card.is_default)?.id?.toString() ??\n cardsData?.data?.[0]?.id?.toString() ??\n '';\n\n // Os cartões chegam depois da abertura (query no client). Derivar — em vez de sincronizar num\n // efeito — faz o padrão da conta já entrar selecionado no render em que a lista chega, e a\n // escolha explícita da pessoa continua mandando.\n const pickedCardExists = cardsData?.data?.some((card) => card.id.toString() === pickedCardId);\n const selectedCardId = pickedCardExists ? pickedCardId : defaultCardId;\n\n const offerPrice = AI_CREDITS_FIRST_PURCHASE_OFFER.price;\n const fullPrice = eligibility.data?.anchorCents != null ? eligibility.data.anchorCents / 100 : null;\n const discountPercent = fullPrice != null ? Math.round((1 - offerPrice / fullPrice) * 100) : null;\n\n const creditsLabel = AI_CREDITS_FIRST_PURCHASE_OFFER.credits.toLocaleString(locale);\n const discountLabel =\n discountPercent != null\n ? translate('common.promo.aiCreditsOffer.discountBadge', { percent: String(discountPercent) })\n : '';\n\n const hasCard = Boolean(selectedCardId);\n const isSubmitting = purchaseMutation.isPending;\n\n /** Fecha de verdade — só o `LoseOfferDialog` e a compra concluída chegam aqui. */\n const dismiss = useCallback(() => {\n setConfirmingExit(false);\n closeModal();\n }, [closeModal]);\n\n /** Toda tentativa de sair passa por aqui — X, Esc, clique fora e \"Agora não\". */\n const requestClose = useCallback(() => setConfirmingExit(true), []);\n\n /**\n * Cobra o cartão informado e credita — sem tocar na recorrência do plano. Chamado tanto pelo CTA\n * com cartão já selecionado quanto (encadeado, sem segundo clique) pelo `onCardAdded` do\n * `addCardModal` quando a pessoa ainda não tinha cartão.\n */\n const submitPurchase = useCallback(\n async (cardId: string) => {\n // Sem assinatura não dá pra cobrar aqui — o CTA vira a porta do checkout do accounts em vez\n // de virar um botão morto.\n if (subscriptionId == null) {\n closeModal();\n window.location.assign(`${accountsUrl}/subscriptions?tab=plans`);\n return;\n }\n\n const input = {\n subscriptionId,\n offer: AI_CREDITS_FIRST_PURCHASE_OFFER.slug,\n paymentMethod: PAYMENT_METHOD_CARD,\n idCard: cardId,\n };\n\n try {\n let result: PurchaseIaCreditsResult = await purchaseMutation.mutateAsync(input);\n\n // 3DS: confirma o desafio e RE-POSTA o MESMO input — a chave de idempotência do backend\n // devolve a mesma cobrança já paga, então o segundo POST fecha com status 1.\n if (result.status === 2) {\n await confirmPaymentIfRequired(stripe, result, translate('common.payment.authFailed'));\n result = await purchaseMutation.mutateAsync(input);\n }\n\n // O `message` do status 0 é código de máquina do backend (`offer_consumed`,\n // `offer_card_only`…), não copy — a tela mostra o erro genérico do catálogo.\n if (result.status !== 1) {\n toast.custom((toastId) => (\n <Toast\n variant=\"error\"\n message={translate('common.promo.aiCreditsOffer.errorToast')}\n toastId={toastId}\n />\n ));\n return;\n }\n\n if (accountId != null) {\n const cookie = readOfferStageCookie(accountId);\n writeOfferStageCookie(\n accountId,\n {\n stage: AiCreditsOfferStage.CONVERTED,\n lastSessionIat: eligibility.data?.sessionIssuedAt ?? cookie.lastSessionIat,\n repeatCount: cookie.repeatCount,\n },\n whitelabel?.domain\n );\n }\n\n toast.custom((toastId) => (\n <Toast\n variant=\"success\"\n message={translate('common.promo.aiCreditsOffer.successToast', { credits: creditsLabel })}\n toastId={toastId}\n />\n ));\n dismiss();\n } catch {\n // Erro técnico (rede, HTTP não-JSON, resposta fora do `PurchaseIaCreditsResultSchema`) —\n // `error.message` aqui é texto de máquina (inclusive o dump do ZodError), nunca copy. Mesma\n // decisão do `status !== 1` acima: a tela mostra a mensagem genérica do catálogo.\n toast.custom((toastId) => (\n <Toast\n variant=\"error\"\n message={translate('common.promo.aiCreditsOffer.errorToast')}\n toastId={toastId}\n />\n ));\n }\n },\n [\n accountId,\n accountsUrl,\n closeModal,\n creditsLabel,\n dismiss,\n eligibility.data?.sessionIssuedAt,\n purchaseMutation,\n stripe,\n subscriptionId,\n translate,\n whitelabel?.domain,\n ]\n );\n\n const handleOpenAddCard = useCallback(() => {\n openModal('addCardModal', {\n onCardAdded: (cardId: string) => {\n setPickedCardId(cardId);\n void submitPurchase(cardId);\n },\n });\n }, [openModal, submitPurchase]);\n\n const handleConfirm = useCallback(async () => {\n // 1º passo do fluxo: sem cartão, o CTA é o próprio cadastro — a compra encadeia sozinha\n // assim que o cartão é criado (ver `handleOpenAddCard`).\n if (!hasCard) {\n handleOpenAddCard();\n return;\n }\n await submitPurchase(selectedCardId);\n }, [handleOpenAddCard, hasCard, selectedCardId, submitPurchase]);\n\n const bullets = [\n translate('common.promo.aiCreditsOffer.bullets.first'),\n translate('common.promo.aiCreditsOffer.bullets.second'),\n translate('common.promo.aiCreditsOffer.bullets.third'),\n ];\n\n return (\n <>\n <Dialog open={open} onOpenChange={() => {}}>\n <DialogContent\n showCloseButton={false}\n onOpenAutoFocus={(event) => event.preventDefault()}\n onEscapeKeyDown={(event) => {\n event.preventDefault();\n requestClose();\n }}\n onPointerDownOutside={(event) => {\n event.preventDefault();\n requestClose();\n }}\n onInteractOutside={(event) => event.preventDefault()}\n overlayClassName=\"duration-300\"\n className={[\n // mobile: folha de tela cheia (mesma geometria do BuyCreditsModal)\n 'flex flex-col gap-0 overflow-hidden rounded-t-2xl rounded-b-none border-0 p-0 shadow-elevated',\n 'top-0 right-0 bottom-0 left-0 h-dvh max-w-full translate-x-0 translate-y-0 sm:max-w-full',\n // desktop (≥ md): cartão de duas colunas, centralizado\n 'md:top-[50%] md:right-auto md:bottom-auto md:left-[50%] md:h-auto md:max-h-[92vh] md:w-[900px]! md:max-w-[900px]! md:translate-x-[-50%] md:translate-y-[-50%] md:rounded-xl md:border',\n // entrada de baixo pra cima: a classe (e não utilidade `animate-[...]`) porque o\n // `DialogContent` já traz `animate-in` e o merge do Tailwind não funde as duas — ver\n // `.gp-offer-modal` no globals.css.\n 'gp-offer-modal',\n ].join(' ')}\n >\n <div className=\"gm-scroll flex h-full flex-col overflow-y-auto md:h-auto md:min-h-[600px] md:flex-row md:items-stretch md:overflow-visible\">\n {/* Ilustração: topo no mobile, coluna da direita no desktop (`md:order-2`). */}\n <div className=\"relative h-[180px] w-full shrink-0 md:order-2 md:h-auto md:w-1/2\">\n <OfferIllustration credits={AI_CREDITS_FIRST_PURCHASE_OFFER.credits} locale={locale} />\n\n <button\n type=\"button\"\n onClick={requestClose}\n aria-label={translate('common.promo.aiCreditsOffer.close')}\n className=\"absolute top-4 right-4 z-10 flex size-8 cursor-pointer items-center justify-center rounded-lg text-gray-500 transition-colors hover:bg-gray-100 hover:text-gray-950\"\n >\n <IconX className=\"size-[18px]\" />\n </button>\n </div>\n\n {/* Coluna da oferta */}\n <div className=\"flex flex-1 flex-col gap-6 p-6 md:order-1 md:w-1/2 md:shrink-0 md:gap-7 md:p-10\">\n <span\n className=\"gp-offer-rise paragraph-xsmall-semibold w-fit rounded-full bg-cyan-50 px-2.5 py-1 text-cyan-700\"\n style={{ animationDelay: '160ms' }}\n >\n {translate('common.promo.aiCreditsOffer.eyebrow')}\n </span>\n\n {/* 26px (2px acima do `title-large`) e Outfit 700: a oferta bate mais forte que um\n título de modal comum. A quebra vem do catálogo, pela tag `<break>`, então cada\n idioma escolhe onde partir a frase. */}\n <div className=\"gp-offer-rise\" style={{ animationDelay: '220ms' }}>\n <DialogTitle className=\"title-large-semibold text-[26px] leading-8 font-bold tracking-[-0.4px] text-zinc-950\">\n {translate.rich('common.promo.aiCreditsOffer.title', {\n credits: creditsLabel,\n discount: discountLabel,\n break: () => <br />,\n })}\n </DialogTitle>\n </div>\n\n <ul className=\"flex flex-col gap-3.5\">\n {bullets.map((bullet, index) => (\n <li\n key={bullet}\n className=\"gp-offer-rise flex items-start gap-2.5\"\n style={{ animationDelay: `${300 + index * 70}ms` }}\n >\n <span className=\"mt-0.5 flex size-5 shrink-0 items-center justify-center rounded-full bg-cyan-50\">\n <IconCheck size={13} stroke={3} className=\"text-cyan-700\" />\n </span>\n <span className=\"paragraph-small-medium text-zinc-600\">{bullet}</span>\n </li>\n ))}\n </ul>\n\n {/* Preço de tabela em cima, pequeno e riscado; o de campanha embaixo, na mesma\n tipografia do título (Outfit 700) pra os dois números não competirem entre si. Sem\n âncora resolvida (elegibilidade ainda carregando) some a linha do preço cheio em\n vez de mostrar um \"de\" vazio. */}\n <div className=\"gp-offer-rise flex flex-col gap-0.5\" style={{ animationDelay: '520ms' }}>\n {fullPrice != null && (\n <span className=\"paragraph-small-medium text-zinc-400 line-through\">\n {formatCurrencyNumber(fullPrice)}\n </span>\n )}\n <div className=\"flex items-center gap-2.5\">\n <span className=\"title-large-semibold font-bold tracking-[-0.4px] text-zinc-950\">\n {formatCurrencyNumber(offerPrice)}\n </span>\n {discountLabel && (\n <span className=\"paragraph-xsmall-semibold rounded-md bg-green-50 px-2 py-1 text-green-600\">\n {discountLabel}\n </span>\n )}\n </div>\n </div>\n\n <div className=\"gp-offer-rise mt-2\" style={{ animationDelay: '580ms' }}>\n <OfferPaymentField cardId={selectedCardId} onCardAdded={setPickedCardId} />\n </div>\n\n <div\n className=\"gp-offer-rise mt-auto flex flex-col gap-2\"\n style={{ animationDelay: '640ms' }}\n >\n <Button className=\"w-full\" loading={isSubmitting} onClick={handleConfirm}>\n {hasCard\n ? translate('common.promo.aiCreditsOffer.cta', { discount: discountLabel })\n : translate('common.promo.aiCreditsOffer.ctaAddCard')}\n </Button>\n <Button\n variant=\"secondary\"\n className=\"h-10! w-full\"\n disabled={isSubmitting}\n onClick={requestClose}\n >\n {translate('common.promo.aiCreditsOffer.later')}\n </Button>\n </div>\n </div>\n </div>\n </DialogContent>\n </Dialog>\n\n <LoseOfferDialog\n open={confirmingExit}\n discountLabel={discountLabel}\n creditsLabel={creditsLabel}\n fullPriceLabel={fullPrice != null ? formatCurrencyNumber(fullPrice) : ''}\n onKeep={() => setConfirmingExit(false)}\n onLose={dismiss}\n />\n </>\n );\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../../../src/components/modals/promo/AiCreditsOfferModal/AiCreditsOfferModal.tsx"],"sourcesContent":["'use client';\n\nimport { useCallback, useState } from 'react';\nimport { useStripe } from '@stripe/react-stripe-js';\nimport { IconCheck, IconX } from '@tabler/icons-react';\nimport { useLocale, useTranslations } from 'next-intl';\nimport { toast } from 'sonner';\nimport { Button } from '../../../ui/buttons/Button';\nimport { Dialog, DialogContent, DialogTitle } from '../../../ui/overlay/Dialog';\nimport { Toast } from '../../../ui/feedback/Toast';\nimport { useAuth } from '../../../../providers/auth.provider';\nimport { useWhitelabel, useWhitelabelUrls } from '../../../../providers/whitelabel.provider';\nimport { useActiveSubscription } from '../../../../modules/subscriptions/hooks/find-active-subscription.hook';\nimport { useCards } from '../../../../modules/cards/hooks/cards.hook';\nimport { useCurrencyFormatter } from '../../../../modules/accounts/hooks/use-currency-formatter.hook';\nimport { confirmPaymentIfRequired } from '../../../../modules/subscriptions/utils/confirm-payment-if-required';\nimport {\n AI_CREDITS_FIRST_PURCHASE_OFFER,\n AiCreditsOfferStage,\n PAYMENT_METHOD_CARD,\n} from '../../../../modules/ia-credits/constants/ai-credits-offer.constants';\nimport { useAiCreditsOfferEligibility } from '../../../../modules/ia-credits/hooks/use-offer-eligibility.hook';\nimport { usePurchaseIaCredits } from '../../../../modules/ia-credits/hooks/purchase-ia-credits.hook';\nimport type { PurchaseIaCreditsResult } from '../../../../modules/ia-credits/types/ai-credits-offer.type';\nimport { readOfferStageCookie, writeOfferStageCookie } from '../../../../modules/ia-credits/utils/offer-stage-cookie';\nimport { useAiCreditsOfferModal } from '../../../../store/useAiCreditsOfferModal';\nimport { useModalManager } from '../../../../store/useModalManager';\nimport { LoseOfferDialog } from './components/LoseOfferDialog';\nimport { DialogLayer } from '../../../ui/overlay/layers';\nimport { OfferIllustration } from './components/OfferIllustration';\nimport { OfferPaymentField } from './components/OfferPaymentField';\n\n/**\n * Oferta de primeira compra de créditos de IA — a tela que a base gratuita vê quando o\n * `AiCreditsOfferGate` decide que é a vez dela.\n *\n * Duas colunas, como a referência de produto: à esquerda a oferta e o cartão (via\n * `OfferPaymentField`, que cadastra pelo `addCardModal` padrão da plataforma); à direita o\n * gradiente da tela de login com a ilustração do editor.\n *\n * Fechar não fecha: `X`, `Esc`, clique fora e \"Agora não\" caem todos no `LoseOfferDialog`, que\n * pergunta se a pessoa quer mesmo perder o desconto.\n *\n * ⚠️ PREÇO: o card manda só o SLUG da oferta (`AI_CREDITS_FIRST_PURCHASE_OFFER.slug`) — quem\n * resolve pacote, preço e elegibilidade é o backend (Asana ID-1217333331953496). O \"de/por\" desta\n * tela usa `AI_CREDITS_FIRST_PURCHASE_OFFER.price` (fixo, parte do contrato da oferta) contra\n * `anchorCents` do `useAiCreditsOfferEligibility` (derivado do preço real do addon no plano — nunca\n * hardcoded).\n *\n * ANIMAÇÃO: CSS puro, sem `motion/react` (a lib não depende dela). As classes `gp-offer-rise`,\n * `gp-offer-window-in`, `gp-offer-card-in` e `gp-offer-icon-pulse` vêm do `globals.css` de cada app\n * consumidor — bloco documentado abaixo, precisa estar presente nos 3 apps (gpages/gapps/editor):\n *\n * ```css\n * @keyframes gp-offer-rise {\n * from { opacity: 0; translate: 0 10px; }\n * to { opacity: 1; translate: 0 0; }\n * }\n * .gp-offer-rise { animation: gp-offer-rise 420ms cubic-bezier(0.22, 1, 0.36, 1) both; }\n *\n * @keyframes gp-offer-window-in {\n * from { opacity: 0; translate: 0 18px; scale: 0.985; }\n * to { opacity: 1; translate: 0 0; scale: 1; }\n * }\n * .gp-offer-window-in { animation: gp-offer-window-in 550ms cubic-bezier(0.22, 1, 0.36, 1) 50ms both; }\n *\n * @keyframes gp-offer-card-in {\n * from { opacity: 0; translate: 0 26px; scale: 0.97; }\n * to { opacity: 1; translate: 0 0; scale: 1; }\n * }\n * .gp-offer-card-in { animation: gp-offer-card-in 520ms cubic-bezier(0.34, 1.56, 0.64, 1) 340ms both; }\n *\n * @keyframes gp-offer-icon-pulse {\n * 0%, 100% { scale: 1; }\n * 50% { scale: 1.06; }\n * }\n * .gp-offer-icon-pulse { animation: gp-offer-icon-pulse 2.6s ease-in-out 1.6s infinite; }\n *\n * @media (prefers-reduced-motion: reduce) {\n * .gp-offer-rise, .gp-offer-window-in, .gp-offer-card-in, .gp-offer-icon-pulse { animation: none; }\n * }\n * ```\n *\n * `translate`/`scale` (propriedades CSS independentes, não `transform`) pra conviver sem conflito\n * com utilitários de posicionamento do Tailwind (mesmo problema que já forçou o `.gp-offer-modal`\n * de entrada/saída a sair de `@layer` — ver `AiCreditsOfferGate`/`globals.css`).\n */\nexport default function AiCreditsOfferModal() {\n const translate = useTranslations();\n const locale = useLocale();\n const stripe = useStripe();\n const { open, closeModal } = useAiCreditsOfferModal();\n const { openModal } = useModalManager();\n const { user } = useAuth();\n const { whitelabel } = useWhitelabel();\n const { accountsUrl } = useWhitelabelUrls();\n const { formatCurrencyNumber } = useCurrencyFormatter();\n\n const [confirmingExit, setConfirmingExit] = useState(false);\n const [pickedCardId, setPickedCardId] = useState('');\n\n const { data: cardsData } = useCards();\n const { data: subscriptionData } = useActiveSubscription();\n const eligibility = useAiCreditsOfferEligibility();\n const purchaseMutation = usePurchaseIaCredits();\n\n const subscription = subscriptionData?.data?.[0] ?? null;\n const subscriptionId = subscription?.id;\n const accountId = user?.id_account;\n\n const defaultCardId =\n cardsData?.data?.find((card) => card.is_default)?.id?.toString() ??\n cardsData?.data?.[0]?.id?.toString() ??\n '';\n\n // Os cartões chegam depois da abertura (query no client). Derivar — em vez de sincronizar num\n // efeito — faz o padrão da conta já entrar selecionado no render em que a lista chega, e a\n // escolha explícita da pessoa continua mandando.\n const pickedCardExists = cardsData?.data?.some((card) => card.id.toString() === pickedCardId);\n const selectedCardId = pickedCardExists ? pickedCardId : defaultCardId;\n\n const offerPrice = AI_CREDITS_FIRST_PURCHASE_OFFER.price;\n const fullPrice = eligibility.data?.anchorCents != null ? eligibility.data.anchorCents / 100 : null;\n const discountPercent = fullPrice != null ? Math.round((1 - offerPrice / fullPrice) * 100) : null;\n\n const creditsLabel = AI_CREDITS_FIRST_PURCHASE_OFFER.credits.toLocaleString(locale);\n const discountLabel =\n discountPercent != null\n ? translate('common.promo.aiCreditsOffer.discountBadge', { percent: String(discountPercent) })\n : '';\n\n const hasCard = Boolean(selectedCardId);\n const isSubmitting = purchaseMutation.isPending;\n\n /** Fecha de verdade — só o `LoseOfferDialog` e a compra concluída chegam aqui. */\n const dismiss = useCallback(() => {\n setConfirmingExit(false);\n closeModal();\n }, [closeModal]);\n\n /** Toda tentativa de sair passa por aqui — X, Esc, clique fora e \"Agora não\". */\n const requestClose = useCallback(() => setConfirmingExit(true), []);\n\n /**\n * Cobra o cartão informado e credita — sem tocar na recorrência do plano. Chamado tanto pelo CTA\n * com cartão já selecionado quanto (encadeado, sem segundo clique) pelo `onCardAdded` do\n * `addCardModal` quando a pessoa ainda não tinha cartão.\n */\n const submitPurchase = useCallback(\n async (cardId: string) => {\n // Sem assinatura não dá pra cobrar aqui — o CTA vira a porta do checkout do accounts em vez\n // de virar um botão morto.\n if (subscriptionId == null) {\n closeModal();\n window.location.assign(`${accountsUrl}/subscriptions?tab=plans`);\n return;\n }\n\n const input = {\n subscriptionId,\n offer: AI_CREDITS_FIRST_PURCHASE_OFFER.slug,\n paymentMethod: PAYMENT_METHOD_CARD,\n idCard: cardId,\n };\n\n try {\n let result: PurchaseIaCreditsResult = await purchaseMutation.mutateAsync(input);\n\n // 3DS: confirma o desafio e RE-POSTA o MESMO input — a chave de idempotência do backend\n // devolve a mesma cobrança já paga, então o segundo POST fecha com status 1.\n if (result.status === 2) {\n await confirmPaymentIfRequired(stripe, result, translate('common.payment.authFailed'));\n result = await purchaseMutation.mutateAsync(input);\n }\n\n // O `message` do status 0 é código de máquina do backend (`offer_consumed`,\n // `offer_card_only`…), não copy — a tela mostra o erro genérico do catálogo.\n if (result.status !== 1) {\n toast.custom((toastId) => (\n <Toast\n variant=\"error\"\n message={translate('common.promo.aiCreditsOffer.errorToast')}\n toastId={toastId}\n />\n ));\n return;\n }\n\n if (accountId != null) {\n const cookie = readOfferStageCookie(accountId);\n writeOfferStageCookie(\n accountId,\n {\n stage: AiCreditsOfferStage.CONVERTED,\n lastSessionIat: eligibility.data?.sessionIssuedAt ?? cookie.lastSessionIat,\n repeatCount: cookie.repeatCount,\n },\n whitelabel?.domain\n );\n }\n\n toast.custom((toastId) => (\n <Toast\n variant=\"success\"\n message={translate('common.promo.aiCreditsOffer.successToast', { credits: creditsLabel })}\n toastId={toastId}\n />\n ));\n dismiss();\n } catch {\n // Erro técnico (rede, HTTP não-JSON, resposta fora do `PurchaseIaCreditsResultSchema`) —\n // `error.message` aqui é texto de máquina (inclusive o dump do ZodError), nunca copy. Mesma\n // decisão do `status !== 1` acima: a tela mostra a mensagem genérica do catálogo.\n toast.custom((toastId) => (\n <Toast\n variant=\"error\"\n message={translate('common.promo.aiCreditsOffer.errorToast')}\n toastId={toastId}\n />\n ));\n }\n },\n [\n accountId,\n accountsUrl,\n closeModal,\n creditsLabel,\n dismiss,\n eligibility.data?.sessionIssuedAt,\n purchaseMutation,\n stripe,\n subscriptionId,\n translate,\n whitelabel?.domain,\n ]\n );\n\n const handleOpenAddCard = useCallback(() => {\n openModal('addCardModal', {\n onCardAdded: (cardId: string) => {\n setPickedCardId(cardId);\n void submitPurchase(cardId);\n },\n });\n }, [openModal, submitPurchase]);\n\n const handleConfirm = useCallback(async () => {\n // 1º passo do fluxo: sem cartão, o CTA é o próprio cadastro — a compra encadeia sozinha\n // assim que o cartão é criado (ver `handleOpenAddCard`).\n if (!hasCard) {\n handleOpenAddCard();\n return;\n }\n await submitPurchase(selectedCardId);\n }, [handleOpenAddCard, hasCard, selectedCardId, submitPurchase]);\n\n const bullets = [\n translate('common.promo.aiCreditsOffer.bullets.first'),\n translate('common.promo.aiCreditsOffer.bullets.second'),\n translate('common.promo.aiCreditsOffer.bullets.third'),\n ];\n\n return (\n <>\n <Dialog open={open} onOpenChange={() => {}}>\n <DialogContent\n showCloseButton={false}\n onOpenAutoFocus={(event) => event.preventDefault()}\n onEscapeKeyDown={(event) => {\n event.preventDefault();\n requestClose();\n }}\n onPointerDownOutside={(event) => {\n event.preventDefault();\n requestClose();\n }}\n onInteractOutside={(event) => event.preventDefault()}\n overlayClassName=\"duration-300\"\n className={[\n // mobile: folha de tela cheia (mesma geometria do BuyCreditsModal)\n 'flex flex-col gap-0 overflow-hidden rounded-t-2xl rounded-b-none border-0 p-0 shadow-elevated',\n 'top-0 right-0 bottom-0 left-0 h-dvh max-w-full translate-x-0 translate-y-0 sm:max-w-full',\n // desktop (≥ md): cartão de duas colunas, centralizado\n 'md:top-[50%] md:right-auto md:bottom-auto md:left-[50%] md:h-auto md:max-h-[92vh] md:w-[900px]! md:max-w-[900px]! md:translate-x-[-50%] md:translate-y-[-50%] md:rounded-xl md:border',\n // entrada de baixo pra cima: a classe (e não utilidade `animate-[...]`) porque o\n // `DialogContent` já traz `animate-in` e o merge do Tailwind não funde as duas — ver\n // `.gp-offer-modal` no globals.css.\n 'gp-offer-modal',\n ].join(' ')}\n >\n <div className=\"gm-scroll flex h-full flex-col overflow-y-auto md:h-auto md:min-h-[600px] md:flex-row md:items-stretch md:overflow-visible\">\n {/* Ilustração: topo no mobile, coluna da direita no desktop (`md:order-2`). */}\n <div className=\"relative h-[180px] w-full shrink-0 md:order-2 md:h-auto md:w-1/2\">\n <OfferIllustration credits={AI_CREDITS_FIRST_PURCHASE_OFFER.credits} locale={locale} />\n\n <button\n type=\"button\"\n onClick={requestClose}\n aria-label={translate('common.promo.aiCreditsOffer.close')}\n className=\"absolute top-4 right-4 z-10 flex size-8 cursor-pointer items-center justify-center rounded-lg text-gray-500 transition-colors hover:bg-gray-100 hover:text-gray-950\"\n >\n <IconX className=\"size-[18px]\" />\n </button>\n </div>\n\n {/* Coluna da oferta */}\n <div className=\"flex flex-1 flex-col gap-6 p-6 md:order-1 md:w-1/2 md:shrink-0 md:gap-7 md:p-10\">\n <span\n className=\"gp-offer-rise paragraph-xsmall-semibold w-fit rounded-full bg-cyan-50 px-2.5 py-1 text-cyan-700\"\n style={{ animationDelay: '160ms' }}\n >\n {translate('common.promo.aiCreditsOffer.eyebrow')}\n </span>\n\n {/* 26px (2px acima do `title-large`) e Outfit 700: a oferta bate mais forte que um\n título de modal comum. A quebra vem do catálogo, pela tag `<break>`, então cada\n idioma escolhe onde partir a frase. */}\n <div className=\"gp-offer-rise\" style={{ animationDelay: '220ms' }}>\n <DialogTitle className=\"title-large-semibold text-[26px] leading-8 font-bold tracking-[-0.4px] text-zinc-950\">\n {translate.rich('common.promo.aiCreditsOffer.title', {\n credits: creditsLabel,\n discount: discountLabel,\n break: () => <br />,\n })}\n </DialogTitle>\n </div>\n\n <ul className=\"flex flex-col gap-3.5\">\n {bullets.map((bullet, index) => (\n <li\n key={bullet}\n className=\"gp-offer-rise flex items-start gap-2.5\"\n style={{ animationDelay: `${300 + index * 70}ms` }}\n >\n <span className=\"mt-0.5 flex size-5 shrink-0 items-center justify-center rounded-full bg-cyan-50\">\n <IconCheck size={13} stroke={3} className=\"text-cyan-700\" />\n </span>\n <span className=\"paragraph-small-medium text-zinc-600\">{bullet}</span>\n </li>\n ))}\n </ul>\n\n {/* Preço de tabela em cima, pequeno e riscado; o de campanha embaixo, na mesma\n tipografia do título (Outfit 700) pra os dois números não competirem entre si. Sem\n âncora resolvida (elegibilidade ainda carregando) some a linha do preço cheio em\n vez de mostrar um \"de\" vazio. */}\n <div className=\"gp-offer-rise flex flex-col gap-0.5\" style={{ animationDelay: '520ms' }}>\n {fullPrice != null && (\n <span className=\"paragraph-small-medium text-zinc-400 line-through\">\n {formatCurrencyNumber(fullPrice)}\n </span>\n )}\n <div className=\"flex items-center gap-2.5\">\n <span className=\"title-large-semibold font-bold tracking-[-0.4px] text-zinc-950\">\n {formatCurrencyNumber(offerPrice)}\n </span>\n {discountLabel && (\n <span className=\"paragraph-xsmall-semibold rounded-md bg-green-50 px-2 py-1 text-green-600\">\n {discountLabel}\n </span>\n )}\n </div>\n </div>\n\n <div className=\"gp-offer-rise mt-2\" style={{ animationDelay: '580ms' }}>\n <OfferPaymentField cardId={selectedCardId} onCardAdded={setPickedCardId} />\n </div>\n\n <div\n className=\"gp-offer-rise mt-auto flex flex-col gap-2\"\n style={{ animationDelay: '640ms' }}\n >\n <Button className=\"w-full\" loading={isSubmitting} onClick={handleConfirm}>\n {hasCard\n ? translate('common.promo.aiCreditsOffer.cta', { discount: discountLabel })\n : translate('common.promo.aiCreditsOffer.ctaAddCard')}\n </Button>\n <Button\n variant=\"secondary\"\n className=\"h-10! w-full\"\n disabled={isSubmitting}\n onClick={requestClose}\n >\n {translate('common.promo.aiCreditsOffer.later')}\n </Button>\n </div>\n </div>\n </div>\n </DialogContent>\n </Dialog>\n\n {/* Irmao do `Dialog` acima, entao nasceria na mesma faixa de z. */}\n <DialogLayer>\n <LoseOfferDialog\n open={confirmingExit}\n discountLabel={discountLabel}\n creditsLabel={creditsLabel}\n fullPriceLabel={fullPrice != null ? formatCurrencyNumber(fullPrice) : ''}\n onKeep={() => setConfirmingExit(false)}\n onLose={dismiss}\n />\n </DialogLayer>\n </>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -25,8 +25,8 @@ function LoseOfferDialog({
|
|
|
25
25
|
{
|
|
26
26
|
showCloseButton: false,
|
|
27
27
|
onOpenAutoFocus: (event) => event.preventDefault(),
|
|
28
|
-
overlayClassName: "
|
|
29
|
-
className: "
|
|
28
|
+
overlayClassName: "bg-black/40",
|
|
29
|
+
className: "flex flex-col gap-0 p-0 max-[440px]:w-[calc(100%-32px)] md:w-[420px]! lg:w-[420px]! max-w-[420px]! data-[state=open]:animate-[gp-offer-confirm-in_260ms_cubic-bezier(0.16,1,0.3,1)]",
|
|
30
30
|
children: [
|
|
31
31
|
/* @__PURE__ */ jsxs(DialogHeader, { className: "gap-3 p-5 text-left", children: [
|
|
32
32
|
/* @__PURE__ */ jsx("div", { className: "flex w-fit items-center justify-center rounded-lg bg-red-50 p-2.5", children: /* @__PURE__ */ jsx(IconAlertTriangle, { size: 20, className: "text-red-500" }) }),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../src/components/modals/promo/AiCreditsOfferModal/components/LoseOfferDialog.tsx"],"sourcesContent":["'use client';\n\nimport { useTranslations } from 'next-intl';\nimport { Button } from '../../../../ui/buttons/Button';\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogHeader,\n DialogTitle,\n} from '../../../../ui/overlay/Dialog';\nimport { Separator } from '../../../../ui/data-display/Separator';\nimport { IconAlertTriangle } from '@tabler/icons-react';\n\ninterface LoseOfferDialogProps {\n open: boolean;\n /** Selo do desconto (\"50% OFF\") — repetido aqui porque é ele que dói. */\n discountLabel: string;\n /** Pacote ofertado, já formatado (\"1.000\"). */\n creditsLabel: string;\n /** Preço cheio formatado: o número que volta a valer. Vazio enquanto a âncora carrega. */\n fullPriceLabel: string;\n /** Volta pra oferta (fecha só esta confirmação). */\n onKeep: () => void;\n /** Abre mão da oferta e fecha tudo. */\n onLose: () => void;\n}\n\n/**\n * Confirmação de saída (\"quer mesmo perder essa oferta?\").\n *\n * É um Dialog SEPARADO, empilhado por cima da oferta com os mesmos z-index do\n * `InsufficientIaCreditsModal` (overlay 1002 / conteúdo 1003) — assim a oferta continua **visível\n * atrás do véu**: a pessoa decide olhando exatamente para o que está prestes a perder, que é o\n * ponto inteiro desta tela.\n *\n * O principal é VOLTAR para a oferta; perder fica como secundário, a um clique de distância. A\n * saída não está escondida — o peso da decisão está na frase, não no botão.\n */\nexport function LoseOfferDialog({\n open,\n discountLabel,\n creditsLabel,\n fullPriceLabel,\n onKeep,\n onLose,\n}: LoseOfferDialogProps) {\n const translate = useTranslations();\n\n return (\n <Dialog open={open} onOpenChange={(next) => !next && onKeep()}>\n <DialogContent\n showCloseButton={false}\n onOpenAutoFocus={(event) => event.preventDefault()}\n overlayClassName=\"
|
|
1
|
+
{"version":3,"sources":["../../../../../../src/components/modals/promo/AiCreditsOfferModal/components/LoseOfferDialog.tsx"],"sourcesContent":["'use client';\n\nimport { useTranslations } from 'next-intl';\nimport { Button } from '../../../../ui/buttons/Button';\nimport {\n Dialog,\n DialogContent,\n DialogDescription,\n DialogHeader,\n DialogTitle,\n} from '../../../../ui/overlay/Dialog';\nimport { Separator } from '../../../../ui/data-display/Separator';\nimport { IconAlertTriangle } from '@tabler/icons-react';\n\ninterface LoseOfferDialogProps {\n open: boolean;\n /** Selo do desconto (\"50% OFF\") — repetido aqui porque é ele que dói. */\n discountLabel: string;\n /** Pacote ofertado, já formatado (\"1.000\"). */\n creditsLabel: string;\n /** Preço cheio formatado: o número que volta a valer. Vazio enquanto a âncora carrega. */\n fullPriceLabel: string;\n /** Volta pra oferta (fecha só esta confirmação). */\n onKeep: () => void;\n /** Abre mão da oferta e fecha tudo. */\n onLose: () => void;\n}\n\n/**\n * Confirmação de saída (\"quer mesmo perder essa oferta?\").\n *\n * É um Dialog SEPARADO, empilhado por cima da oferta com os mesmos z-index do\n * `InsufficientIaCreditsModal` (overlay 1002 / conteúdo 1003) — assim a oferta continua **visível\n * atrás do véu**: a pessoa decide olhando exatamente para o que está prestes a perder, que é o\n * ponto inteiro desta tela.\n *\n * O principal é VOLTAR para a oferta; perder fica como secundário, a um clique de distância. A\n * saída não está escondida — o peso da decisão está na frase, não no botão.\n */\nexport function LoseOfferDialog({\n open,\n discountLabel,\n creditsLabel,\n fullPriceLabel,\n onKeep,\n onLose,\n}: LoseOfferDialogProps) {\n const translate = useTranslations();\n\n return (\n <Dialog open={open} onOpenChange={(next) => !next && onKeep()}>\n <DialogContent\n showCloseButton={false}\n onOpenAutoFocus={(event) => event.preventDefault()}\n overlayClassName=\"bg-black/40\"\n className=\"flex flex-col gap-0 p-0 max-[440px]:w-[calc(100%-32px)] md:w-[420px]! lg:w-[420px]! max-w-[420px]! data-[state=open]:animate-[gp-offer-confirm-in_260ms_cubic-bezier(0.16,1,0.3,1)]\"\n >\n <DialogHeader className=\"gap-3 p-5 text-left\">\n <div className=\"flex w-fit items-center justify-center rounded-lg bg-red-50 p-2.5\">\n <IconAlertTriangle size={20} className=\"text-red-500\" />\n </div>\n <div className=\"flex flex-col gap-2\">\n <DialogTitle className=\"paragraph-medium-semibold text-zinc-950\">\n {translate('common.promo.aiCreditsOffer.exit.title', { discount: discountLabel })}\n </DialogTitle>\n <DialogDescription className=\"paragraph-small-regular text-zinc-600\">\n {/* Sem preço resolvido (elegibilidade ainda carregando) a frase cai na versão sem\n número — prometer \"volta a custar\" sem dizer quanto é pior do que não dizer. */}\n {fullPriceLabel\n ? translate('common.promo.aiCreditsOffer.exit.description', {\n credits: creditsLabel,\n price: fullPriceLabel,\n })\n : translate('common.promo.aiCreditsOffer.exit.descriptionNoPrice', {\n credits: creditsLabel,\n })}\n </DialogDescription>\n </div>\n </DialogHeader>\n\n <Separator />\n\n {/* Rodapé com os dois botões juntos à esquerda (`gap-2`, sem `justify-between`), o\n principal primeiro. Ficar é o caminho principal; perder a oferta continua a um clique,\n mas como ação secundária. */}\n <div className=\"flex items-center gap-2 p-5\">\n <Button className=\"h-10! transition-colors\" onClick={onKeep}>\n {translate('common.promo.aiCreditsOffer.exit.keep')}\n </Button>\n <Button variant=\"secondary\" className=\"h-10! transition-colors\" onClick={onLose}>\n {translate('common.promo.aiCreditsOffer.exit.lose')}\n </Button>\n </div>\n </DialogContent>\n </Dialog>\n );\n}\n"],"mappings":";AA2DY,cAEF,YAFE;AAzDZ,SAAS,uBAAuB;AAChC,SAAS,cAAc;AACvB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AACP,SAAS,iBAAiB;AAC1B,SAAS,yBAAyB;AA2B3B,SAAS,gBAAgB;AAAA,EAC9B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAAyB;AACvB,QAAM,YAAY,gBAAgB;AAElC,SACE,oBAAC,UAAO,MAAY,cAAc,CAAC,SAAS,CAAC,QAAQ,OAAO,GAC1D;AAAA,IAAC;AAAA;AAAA,MACC,iBAAiB;AAAA,MACjB,iBAAiB,CAAC,UAAU,MAAM,eAAe;AAAA,MACjD,kBAAiB;AAAA,MACjB,WAAU;AAAA,MAEV;AAAA,6BAAC,gBAAa,WAAU,uBACtB;AAAA,8BAAC,SAAI,WAAU,qEACb,8BAAC,qBAAkB,MAAM,IAAI,WAAU,gBAAe,GACxD;AAAA,UACA,qBAAC,SAAI,WAAU,uBACb;AAAA,gCAAC,eAAY,WAAU,2CACpB,oBAAU,0CAA0C,EAAE,UAAU,cAAc,CAAC,GAClF;AAAA,YACA,oBAAC,qBAAkB,WAAU,yCAG1B,2BACG,UAAU,gDAAgD;AAAA,cACxD,SAAS;AAAA,cACT,OAAO;AAAA,YACT,CAAC,IACD,UAAU,uDAAuD;AAAA,cAC/D,SAAS;AAAA,YACX,CAAC,GACP;AAAA,aACF;AAAA,WACF;AAAA,QAEA,oBAAC,aAAU;AAAA,QAKX,qBAAC,SAAI,WAAU,+BACb;AAAA,8BAAC,UAAO,WAAU,2BAA0B,SAAS,QAClD,oBAAU,uCAAuC,GACpD;AAAA,UACA,oBAAC,UAAO,SAAQ,aAAY,WAAU,2BAA0B,SAAS,QACtE,oBAAU,uCAAuC,GACpD;AAAA,WACF;AAAA;AAAA;AAAA,EACF,GACF;AAEJ;","names":[]}
|
|
@@ -37,7 +37,7 @@ function OfferPaymentField({ cardId, onCardAdded }) {
|
|
|
37
37
|
children: /* @__PURE__ */ jsx(IconPencil, { size: 18 })
|
|
38
38
|
}
|
|
39
39
|
) }),
|
|
40
|
-
/* @__PURE__ */ jsx(TooltipContent, { side: "top", sideOffset: 6,
|
|
40
|
+
/* @__PURE__ */ jsx(TooltipContent, { side: "top", sideOffset: 6, children: translate("common.promo.aiCreditsOffer.editPayment") })
|
|
41
41
|
] })
|
|
42
42
|
] })
|
|
43
43
|
] }) : /* @__PURE__ */ jsxs(
|
package/dist/components/modals/promo/AiCreditsOfferModal/components/OfferPaymentField.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../../../src/components/modals/promo/AiCreditsOfferModal/components/OfferPaymentField.tsx"],"sourcesContent":["'use client';\n\nimport { useTranslations } from 'next-intl';\nimport { CardBrandIcon } from '../../../../ui/data-display/CardBrandIcons';\nimport { Tooltip, TooltipContent, TooltipTrigger } from '../../../../ui/overlay/Tooltip';\nimport { useCards } from '../../../../../modules/cards/hooks/cards.hook';\nimport { useModalManager } from '../../../../../store/useModalManager';\nimport { IconPencil, IconPlus } from '@tabler/icons-react';\n\ninterface OfferPaymentFieldProps {\n /** Cartão em uso (padrão da conta ou o que acabou de ser cadastrado). */\n cardId: string;\n /** Recebe o id do cartão cadastrado no `addCardModal`. */\n onCardAdded: (cardId: string) => void;\n}\n\n/**\n * Campo de pagamento da oferta — versão de **um cartão só**.\n *\n * Nasceu do `PaymentInfoCard` (mesma moldura, mesma linha \"Pagamento\", mesmo `CardBrandIcon`,\n * mesmas chaves de tradução `common.cards.*`), mas sem o seletor e sem o \"Nova forma de\n * pagamento\": aqui a pessoa está numa compra única de primeira vez, então ou ela ainda não tem\n * cartão (e o campo é só o botão de cadastrar) ou já tem (e o campo é só o cartão). Oferecer uma\n * lista de cartões numa tela cujo objetivo é UM clique era ruído.\n *\n * Com cartão, o lápis fica sempre visível e abre o `addCardModal` — o mesmo fluxo padrão da\n * plataforma, que é por onde se troca a forma de pagamento (não existe edição de cartão: o\n * gateway guarda o dado, então \"editar\" é cadastrar outro). Nada de formulário próprio.\n */\nexport function OfferPaymentField({ cardId, onCardAdded }: OfferPaymentFieldProps) {\n const translate = useTranslations();\n const { openModal } = useModalManager();\n const { data: cardsData } = useCards();\n\n const card = cardsData?.data?.find((item) => item.id.toString() === cardId.toString());\n\n const handleOpenCardModal = () => {\n openModal('addCardModal', { onCardAdded });\n };\n\n return (\n <div className=\"overflow-hidden rounded-lg border border-zinc-200\">\n <div className=\"flex h-[60px] items-center gap-3 px-4\">\n <span className=\"paragraph-small-medium w-[100px] text-zinc-500\">\n {translate('common.cards.paymentInfo.payment')}\n </span>\n\n {card ? (\n <>\n <div className=\"flex min-w-0 flex-1 flex-col gap-0.5\">\n <span className=\"paragraph-small-semibold truncate text-zinc-950\">{card.name}</span>\n <span className=\"paragraph-small-medium text-zinc-950\">\n •••• {card.number?.replaceAll(/\\D/g, '')?.slice(-4)}\n </span>\n </div>\n\n <div className=\"flex shrink-0 items-center gap-2\">\n <CardBrandIcon brand={card.brand} />\n
|
|
1
|
+
{"version":3,"sources":["../../../../../../src/components/modals/promo/AiCreditsOfferModal/components/OfferPaymentField.tsx"],"sourcesContent":["'use client';\n\nimport { useTranslations } from 'next-intl';\nimport { CardBrandIcon } from '../../../../ui/data-display/CardBrandIcons';\nimport { Tooltip, TooltipContent, TooltipTrigger } from '../../../../ui/overlay/Tooltip';\nimport { useCards } from '../../../../../modules/cards/hooks/cards.hook';\nimport { useModalManager } from '../../../../../store/useModalManager';\nimport { IconPencil, IconPlus } from '@tabler/icons-react';\n\ninterface OfferPaymentFieldProps {\n /** Cartão em uso (padrão da conta ou o que acabou de ser cadastrado). */\n cardId: string;\n /** Recebe o id do cartão cadastrado no `addCardModal`. */\n onCardAdded: (cardId: string) => void;\n}\n\n/**\n * Campo de pagamento da oferta — versão de **um cartão só**.\n *\n * Nasceu do `PaymentInfoCard` (mesma moldura, mesma linha \"Pagamento\", mesmo `CardBrandIcon`,\n * mesmas chaves de tradução `common.cards.*`), mas sem o seletor e sem o \"Nova forma de\n * pagamento\": aqui a pessoa está numa compra única de primeira vez, então ou ela ainda não tem\n * cartão (e o campo é só o botão de cadastrar) ou já tem (e o campo é só o cartão). Oferecer uma\n * lista de cartões numa tela cujo objetivo é UM clique era ruído.\n *\n * Com cartão, o lápis fica sempre visível e abre o `addCardModal` — o mesmo fluxo padrão da\n * plataforma, que é por onde se troca a forma de pagamento (não existe edição de cartão: o\n * gateway guarda o dado, então \"editar\" é cadastrar outro). Nada de formulário próprio.\n */\nexport function OfferPaymentField({ cardId, onCardAdded }: OfferPaymentFieldProps) {\n const translate = useTranslations();\n const { openModal } = useModalManager();\n const { data: cardsData } = useCards();\n\n const card = cardsData?.data?.find((item) => item.id.toString() === cardId.toString());\n\n const handleOpenCardModal = () => {\n openModal('addCardModal', { onCardAdded });\n };\n\n return (\n <div className=\"overflow-hidden rounded-lg border border-zinc-200\">\n <div className=\"flex h-[60px] items-center gap-3 px-4\">\n <span className=\"paragraph-small-medium w-[100px] text-zinc-500\">\n {translate('common.cards.paymentInfo.payment')}\n </span>\n\n {card ? (\n <>\n <div className=\"flex min-w-0 flex-1 flex-col gap-0.5\">\n <span className=\"paragraph-small-semibold truncate text-zinc-950\">{card.name}</span>\n <span className=\"paragraph-small-medium text-zinc-950\">\n •••• {card.number?.replaceAll(/\\D/g, '')?.slice(-4)}\n </span>\n </div>\n\n <div className=\"flex shrink-0 items-center gap-2\">\n <CardBrandIcon brand={card.brand} />\n <Tooltip>\n <TooltipTrigger asChild>\n <button\n type=\"button\"\n onClick={handleOpenCardModal}\n aria-label={translate('common.promo.aiCreditsOffer.editPayment')}\n className=\"flex size-8 cursor-pointer items-center justify-center rounded-lg text-zinc-400 transition-colors hover:bg-zinc-100 hover:text-zinc-950\"\n >\n <IconPencil size={18} />\n </button>\n </TooltipTrigger>\n <TooltipContent side=\"top\" sideOffset={6}>\n {translate('common.promo.aiCreditsOffer.editPayment')}\n </TooltipContent>\n </Tooltip>\n </div>\n </>\n ) : (\n <button\n type=\"button\"\n onClick={handleOpenCardModal}\n className=\"paragraph-small-semibold flex h-8 w-fit cursor-pointer items-center rounded-md border border-zinc-200 px-2 text-zinc-950 transition-colors hover:border-zinc-400\"\n >\n <IconPlus className=\"size-[18px]\" />\n <span className=\"px-1.5\">{translate('common.cards.paymentInfo.addCard')}</span>\n </button>\n )}\n </div>\n </div>\n );\n}\n"],"mappings":";AA2CQ,SAKE,UALF,KAQM,YARN;AAzCR,SAAS,uBAAuB;AAChC,SAAS,qBAAqB;AAC9B,SAAS,SAAS,gBAAgB,sBAAsB;AACxD,SAAS,gBAAgB;AACzB,SAAS,uBAAuB;AAChC,SAAS,YAAY,gBAAgB;AAsB9B,SAAS,kBAAkB,EAAE,QAAQ,YAAY,GAA2B;AACjF,QAAM,YAAY,gBAAgB;AAClC,QAAM,EAAE,UAAU,IAAI,gBAAgB;AACtC,QAAM,EAAE,MAAM,UAAU,IAAI,SAAS;AAErC,QAAM,OAAO,WAAW,MAAM,KAAK,CAAC,SAAS,KAAK,GAAG,SAAS,MAAM,OAAO,SAAS,CAAC;AAErF,QAAM,sBAAsB,MAAM;AAChC,cAAU,gBAAgB,EAAE,YAAY,CAAC;AAAA,EAC3C;AAEA,SACE,oBAAC,SAAI,WAAU,qDACb,+BAAC,SAAI,WAAU,yCACb;AAAA,wBAAC,UAAK,WAAU,kDACb,oBAAU,kCAAkC,GAC/C;AAAA,IAEC,OACC,iCACE;AAAA,2BAAC,SAAI,WAAU,wCACb;AAAA,4BAAC,UAAK,WAAU,mDAAmD,eAAK,MAAK;AAAA,QAC7E,qBAAC,UAAK,WAAU,wCAAuC;AAAA;AAAA,UAC/C,KAAK,QAAQ,WAAW,OAAO,EAAE,GAAG,MAAM,EAAE;AAAA,WACpD;AAAA,SACF;AAAA,MAEA,qBAAC,SAAI,WAAU,oCACb;AAAA,4BAAC,iBAAc,OAAO,KAAK,OAAO;AAAA,QAClC,qBAAC,WACC;AAAA,8BAAC,kBAAe,SAAO,MACrB;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,SAAS;AAAA,cACT,cAAY,UAAU,yCAAyC;AAAA,cAC/D,WAAU;AAAA,cAEV,8BAAC,cAAW,MAAM,IAAI;AAAA;AAAA,UACxB,GACF;AAAA,UACA,oBAAC,kBAAe,MAAK,OAAM,YAAY,GACpC,oBAAU,yCAAyC,GACtD;AAAA,WACF;AAAA,SACF;AAAA,OACF,IAEA;AAAA,MAAC;AAAA;AAAA,QACC,MAAK;AAAA,QACL,SAAS;AAAA,QACT,WAAU;AAAA,QAEV;AAAA,8BAAC,YAAS,WAAU,eAAc;AAAA,UAClC,oBAAC,UAAK,WAAU,UAAU,oBAAU,kCAAkC,GAAE;AAAA;AAAA;AAAA,IAC1E;AAAA,KAEJ,GACF;AAEJ;","names":[]}
|
|
@@ -112,7 +112,7 @@ function PhoneInput({
|
|
|
112
112
|
]
|
|
113
113
|
}
|
|
114
114
|
) }),
|
|
115
|
-
/* @__PURE__ */ jsxs(PopoverContent, { className: "w-72 p-0
|
|
115
|
+
/* @__PURE__ */ jsxs(PopoverContent, { className: "w-72 p-0", align: "start", side: "top", children: [
|
|
116
116
|
/* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2 p-2 border-b border-zinc-100", children: [
|
|
117
117
|
/* @__PURE__ */ jsx(IconSearch, { size: 14, className: "text-zinc-400 shrink-0" }),
|
|
118
118
|
/* @__PURE__ */ jsx(
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/components/ui/form/PhoneInput.tsx"],"sourcesContent":["'use client';\n\nimport { useState, useEffect, useRef } from 'react';\nimport { useTranslations } from 'next-intl';\nimport { AsYouType, CountryCode } from 'libphonenumber-js';\nimport { Input, Separator, Popover, PopoverTrigger, PopoverContent, cn } from '@greatapps/common';\nimport { IconChevronDown, IconSearch } from '@tabler/icons-react';\nimport { COUNTRIES, Country, flagUrl, parsePhoneValue } from '../../../utils/countries';\n\nfunction formatNational(digits: string, iso: string): string {\n const raw = digits.replace(/\\D/g, '');\n if (!raw) return '';\n return new AsYouType(iso as CountryCode).input(raw);\n}\n\ninterface PhoneInputProps {\n value?: string;\n onChange?: (value: string) => void;\n label?: string;\n optional?: boolean;\n required?: boolean;\n hintMessage?: string;\n error?: boolean;\n errorMessage?: string;\n placeholder?: string;\n disabled?: boolean;\n classnameContainer?: string;\n className?: string;\n}\n\nexport default function PhoneInput({\n value,\n onChange,\n label,\n optional = false,\n required = false,\n hintMessage,\n error = false,\n errorMessage,\n placeholder,\n disabled = false,\n classnameContainer,\n className,\n}: PhoneInputProps) {\n const translate = useTranslations();\n const parsed = parsePhoneValue(value ?? '');\n const [selectedCountry, setSelectedCountry] = useState<Country>(parsed.country);\n const [phoneNumber, setPhoneNumber] = useState(parsed.number);\n const [search, setSearch] = useState('');\n const [open, setOpen] = useState(false);\n const prevValueRef = useRef(value);\n const inputRef = useRef<HTMLInputElement>(null);\n\n useEffect(() => {\n if (value !== prevValueRef.current) {\n prevValueRef.current = value;\n const p = parsePhoneValue(value ?? '');\n setSelectedCountry(p.country);\n setPhoneNumber(formatNational(p.number, p.country.iso));\n }\n }, [value]);\n\n const emit = (country: Country, number: string) => {\n onChange?.(`${country.dial} ${number}`);\n };\n\n const handleCountrySelect = (country: Country) => {\n const reformatted = formatNational(phoneNumber, country.iso);\n setSelectedCountry(country);\n setPhoneNumber(reformatted);\n setOpen(false);\n setSearch('');\n emit(country, reformatted);\n };\n\n const handleNumberChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const rawInput = e.target.value;\n const digits = rawInput.replace(/\\D/g, '');\n if (digits.length > 15) return;\n\n const cursorPos = e.target.selectionStart ?? rawInput.length;\n const digitsBeforeCursor = rawInput.slice(0, cursorPos).replace(/\\D/g, '').length;\n\n const formatted = formatNational(rawInput, selectedCountry.iso);\n setPhoneNumber(formatted);\n emit(selectedCountry, formatted);\n\n requestAnimationFrame(() => {\n if (!inputRef.current) return;\n let digitCount = 0;\n let newPos = formatted.length;\n for (let i = 0; i < formatted.length; i++) {\n if (/\\d/.test(formatted[i])) {\n digitCount++;\n if (digitCount === digitsBeforeCursor) {\n newPos = i + 1;\n break;\n }\n }\n }\n inputRef.current.setSelectionRange(newPos, newPos);\n });\n };\n\n const filtered = COUNTRIES.filter(\n (c) =>\n c.name.toLowerCase().includes(search.toLowerCase()) ||\n c.dial.includes(search) ||\n c.iso.toLowerCase().includes(search.toLowerCase())\n );\n\n const finalMessage = error && errorMessage ? errorMessage : hintMessage;\n const showMessage = finalMessage && finalMessage.trim() !== '';\n\n return (\n <div className=\"flex flex-col gap-1.5\">\n {label && (\n <div className=\"flex items-center gap-1 paragraph-xsmall-semibold\">\n <span className=\"text-zinc-600\">{label}</span>\n {optional && <span className=\"text-zinc-500\">{translate('common.forms.phoneInput.optional')}</span>}\n {required && <span className=\"text-red-600\">*</span>}\n </div>\n )}\n\n <div\n className={cn(\n 'flex gap-2 items-center border rounded-lg h-10 transition-colors',\n error\n ? 'border-red-600 hover:border-red-700'\n : 'border-zinc-200 hover:border-zinc-400 focus-within:border-zinc-950',\n disabled && 'cursor-not-allowed',\n classnameContainer\n )}\n >\n <Popover open={open} onOpenChange={setOpen}>\n <PopoverTrigger asChild>\n <button\n type=\"button\"\n className=\"flex items-center gap-1.5 px-3 h-full shrink-0 focus:outline-none\"\n disabled={disabled}\n >\n <img src={flagUrl(selectedCountry.iso)} alt={selectedCountry.iso} className=\"w-5 h-5 rounded-full\" />\n <span className=\"paragraph-small-regular text-zinc-950\">{selectedCountry.dial}</span>\n <IconChevronDown size={14} className={cn(\"text-zinc-500 transition-transform duration-200\", open && \"rotate-180\")} />\n </button>\n </PopoverTrigger>\n <PopoverContent className=\"w-72 p-0 z-[1010]\" align=\"start\" side=\"top\">\n <div className=\"flex items-center gap-2 p-2 border-b border-zinc-100\">\n <IconSearch size={14} className=\"text-zinc-400 shrink-0\" />\n <input\n type=\"text\"\n className=\"flex-1 text-sm outline-none placeholder:text-zinc-400\"\n placeholder={translate('common.forms.phoneInput.searchPlaceholder')}\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n autoFocus\n />\n </div>\n <div className=\"max-h-60 overflow-y-auto overscroll-contain\" onTouchMove={(e) => e.stopPropagation()} onWheel={(e) => e.stopPropagation()}>\n {filtered.map((country) => (\n <button\n key={country.iso}\n type=\"button\"\n className={cn(\n 'flex items-center gap-2.5 w-full px-3 py-2 text-left hover:bg-zinc-50 transition-colors',\n selectedCountry.iso === country.iso && 'bg-zinc-100'\n )}\n onClick={() => handleCountrySelect(country)}\n >\n <img src={flagUrl(country.iso)} alt={country.iso} className=\"w-5 h-5 rounded-full shrink-0\" />\n <span className=\"paragraph-small-regular text-zinc-950 flex-1 truncate\">\n {country.name}\n </span>\n <span className=\"paragraph-small-regular text-zinc-500 shrink-0\">\n {country.dial}\n </span>\n </button>\n ))}\n {filtered.length === 0 && (\n <p className=\"text-sm text-zinc-500 p-3 text-center\">{translate('common.forms.phoneInput.emptyMessage')}</p>\n )}\n </div>\n </PopoverContent>\n </Popover>\n\n <Separator orientation=\"vertical\" />\n\n <Input\n ref={inputRef}\n type=\"tel\"\n value={phoneNumber}\n onChange={handleNumberChange}\n placeholder={placeholder}\n disabled={disabled}\n className={cn(\n 'paragraph-small-medium w-full h-full text-zinc-950 border-0 focus-visible:ring-0 focus-visible:ring-offset-0',\n disabled && 'cursor-not-allowed',\n className\n )}\n />\n </div>\n\n {showMessage && (\n <span className={cn('paragraph-xsmall-medium', error ? 'text-red-600' : 'text-zinc-600')}>\n {finalMessage}\n </span>\n )}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../../../../src/components/ui/form/PhoneInput.tsx"],"sourcesContent":["'use client';\n\nimport { useState, useEffect, useRef } from 'react';\nimport { useTranslations } from 'next-intl';\nimport { AsYouType, CountryCode } from 'libphonenumber-js';\nimport { Input, Separator, Popover, PopoverTrigger, PopoverContent, cn } from '@greatapps/common';\nimport { IconChevronDown, IconSearch } from '@tabler/icons-react';\nimport { COUNTRIES, Country, flagUrl, parsePhoneValue } from '../../../utils/countries';\n\nfunction formatNational(digits: string, iso: string): string {\n const raw = digits.replace(/\\D/g, '');\n if (!raw) return '';\n return new AsYouType(iso as CountryCode).input(raw);\n}\n\ninterface PhoneInputProps {\n value?: string;\n onChange?: (value: string) => void;\n label?: string;\n optional?: boolean;\n required?: boolean;\n hintMessage?: string;\n error?: boolean;\n errorMessage?: string;\n placeholder?: string;\n disabled?: boolean;\n classnameContainer?: string;\n className?: string;\n}\n\nexport default function PhoneInput({\n value,\n onChange,\n label,\n optional = false,\n required = false,\n hintMessage,\n error = false,\n errorMessage,\n placeholder,\n disabled = false,\n classnameContainer,\n className,\n}: PhoneInputProps) {\n const translate = useTranslations();\n const parsed = parsePhoneValue(value ?? '');\n const [selectedCountry, setSelectedCountry] = useState<Country>(parsed.country);\n const [phoneNumber, setPhoneNumber] = useState(parsed.number);\n const [search, setSearch] = useState('');\n const [open, setOpen] = useState(false);\n const prevValueRef = useRef(value);\n const inputRef = useRef<HTMLInputElement>(null);\n\n useEffect(() => {\n if (value !== prevValueRef.current) {\n prevValueRef.current = value;\n const p = parsePhoneValue(value ?? '');\n setSelectedCountry(p.country);\n setPhoneNumber(formatNational(p.number, p.country.iso));\n }\n }, [value]);\n\n const emit = (country: Country, number: string) => {\n onChange?.(`${country.dial} ${number}`);\n };\n\n const handleCountrySelect = (country: Country) => {\n const reformatted = formatNational(phoneNumber, country.iso);\n setSelectedCountry(country);\n setPhoneNumber(reformatted);\n setOpen(false);\n setSearch('');\n emit(country, reformatted);\n };\n\n const handleNumberChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const rawInput = e.target.value;\n const digits = rawInput.replace(/\\D/g, '');\n if (digits.length > 15) return;\n\n const cursorPos = e.target.selectionStart ?? rawInput.length;\n const digitsBeforeCursor = rawInput.slice(0, cursorPos).replace(/\\D/g, '').length;\n\n const formatted = formatNational(rawInput, selectedCountry.iso);\n setPhoneNumber(formatted);\n emit(selectedCountry, formatted);\n\n requestAnimationFrame(() => {\n if (!inputRef.current) return;\n let digitCount = 0;\n let newPos = formatted.length;\n for (let i = 0; i < formatted.length; i++) {\n if (/\\d/.test(formatted[i])) {\n digitCount++;\n if (digitCount === digitsBeforeCursor) {\n newPos = i + 1;\n break;\n }\n }\n }\n inputRef.current.setSelectionRange(newPos, newPos);\n });\n };\n\n const filtered = COUNTRIES.filter(\n (c) =>\n c.name.toLowerCase().includes(search.toLowerCase()) ||\n c.dial.includes(search) ||\n c.iso.toLowerCase().includes(search.toLowerCase())\n );\n\n const finalMessage = error && errorMessage ? errorMessage : hintMessage;\n const showMessage = finalMessage && finalMessage.trim() !== '';\n\n return (\n <div className=\"flex flex-col gap-1.5\">\n {label && (\n <div className=\"flex items-center gap-1 paragraph-xsmall-semibold\">\n <span className=\"text-zinc-600\">{label}</span>\n {optional && <span className=\"text-zinc-500\">{translate('common.forms.phoneInput.optional')}</span>}\n {required && <span className=\"text-red-600\">*</span>}\n </div>\n )}\n\n <div\n className={cn(\n 'flex gap-2 items-center border rounded-lg h-10 transition-colors',\n error\n ? 'border-red-600 hover:border-red-700'\n : 'border-zinc-200 hover:border-zinc-400 focus-within:border-zinc-950',\n disabled && 'cursor-not-allowed',\n classnameContainer\n )}\n >\n <Popover open={open} onOpenChange={setOpen}>\n <PopoverTrigger asChild>\n <button\n type=\"button\"\n className=\"flex items-center gap-1.5 px-3 h-full shrink-0 focus:outline-none\"\n disabled={disabled}\n >\n <img src={flagUrl(selectedCountry.iso)} alt={selectedCountry.iso} className=\"w-5 h-5 rounded-full\" />\n <span className=\"paragraph-small-regular text-zinc-950\">{selectedCountry.dial}</span>\n <IconChevronDown size={14} className={cn(\"text-zinc-500 transition-transform duration-200\", open && \"rotate-180\")} />\n </button>\n </PopoverTrigger>\n <PopoverContent className=\"w-72 p-0\" align=\"start\" side=\"top\">\n <div className=\"flex items-center gap-2 p-2 border-b border-zinc-100\">\n <IconSearch size={14} className=\"text-zinc-400 shrink-0\" />\n <input\n type=\"text\"\n className=\"flex-1 text-sm outline-none placeholder:text-zinc-400\"\n placeholder={translate('common.forms.phoneInput.searchPlaceholder')}\n value={search}\n onChange={(e) => setSearch(e.target.value)}\n autoFocus\n />\n </div>\n <div className=\"max-h-60 overflow-y-auto overscroll-contain\" onTouchMove={(e) => e.stopPropagation()} onWheel={(e) => e.stopPropagation()}>\n {filtered.map((country) => (\n <button\n key={country.iso}\n type=\"button\"\n className={cn(\n 'flex items-center gap-2.5 w-full px-3 py-2 text-left hover:bg-zinc-50 transition-colors',\n selectedCountry.iso === country.iso && 'bg-zinc-100'\n )}\n onClick={() => handleCountrySelect(country)}\n >\n <img src={flagUrl(country.iso)} alt={country.iso} className=\"w-5 h-5 rounded-full shrink-0\" />\n <span className=\"paragraph-small-regular text-zinc-950 flex-1 truncate\">\n {country.name}\n </span>\n <span className=\"paragraph-small-regular text-zinc-500 shrink-0\">\n {country.dial}\n </span>\n </button>\n ))}\n {filtered.length === 0 && (\n <p className=\"text-sm text-zinc-500 p-3 text-center\">{translate('common.forms.phoneInput.emptyMessage')}</p>\n )}\n </div>\n </PopoverContent>\n </Popover>\n\n <Separator orientation=\"vertical\" />\n\n <Input\n ref={inputRef}\n type=\"tel\"\n value={phoneNumber}\n onChange={handleNumberChange}\n placeholder={placeholder}\n disabled={disabled}\n className={cn(\n 'paragraph-small-medium w-full h-full text-zinc-950 border-0 focus-visible:ring-0 focus-visible:ring-offset-0',\n disabled && 'cursor-not-allowed',\n className\n )}\n />\n </div>\n\n {showMessage && (\n <span className={cn('paragraph-xsmall-medium', error ? 'text-red-600' : 'text-zinc-600')}>\n {finalMessage}\n </span>\n )}\n </div>\n );\n}\n"],"mappings":"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","names":[]}
|
|
@@ -3,6 +3,7 @@ import { jsx, jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import * as SelectPrimitive from "@radix-ui/react-select";
|
|
4
4
|
import { IconCheck, IconChevronDown, IconChevronUp } from "@tabler/icons-react";
|
|
5
5
|
import { cn } from "../../../infra/utils/clsx";
|
|
6
|
+
import { useDialogLayer } from "../overlay/layers";
|
|
6
7
|
function Select({
|
|
7
8
|
...props
|
|
8
9
|
}) {
|
|
@@ -54,12 +55,14 @@ function SelectContent({
|
|
|
54
55
|
align = "center",
|
|
55
56
|
...props
|
|
56
57
|
}) {
|
|
58
|
+
const layer = useDialogLayer();
|
|
57
59
|
return /* @__PURE__ */ jsx(SelectPrimitive.Portal, { children: /* @__PURE__ */ jsxs(
|
|
58
60
|
SelectPrimitive.Content,
|
|
59
61
|
{
|
|
60
62
|
"data-slot": "select-content",
|
|
61
63
|
className: cn(
|
|
62
|
-
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative
|
|
64
|
+
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative max-h-(--radix-select-content-available-height) min-w-32 origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border shadow-md",
|
|
65
|
+
layer.popover,
|
|
63
66
|
position === "popper" && "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
|
|
64
67
|
className
|
|
65
68
|
),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/components/ui/form/Select.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport * as SelectPrimitive from \"@radix-ui/react-select\";\nimport { IconCheck, IconChevronDown, IconChevronUp } from '@tabler/icons-react';\nimport { cn } from \"../../../infra/utils/clsx\";\n\nfunction Select({\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.Root>) {\n return <SelectPrimitive.Root data-slot=\"select\" {...props} />;\n}\n\nfunction SelectGroup({\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.Group>) {\n return <SelectPrimitive.Group data-slot=\"select-group\" {...props} />;\n}\n\nfunction SelectValue({\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.Value>) {\n return <SelectPrimitive.Value data-slot=\"select-value\" {...props} />;\n}\n\nfunction SelectTrigger({\n className,\n size = \"default\",\n children,\n icon,\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.Trigger> & {\n size?: \"sm\" | \"default\";\n icon?: React.ReactNode;\n}) {\n return (\n <SelectPrimitive.Trigger\n data-slot=\"select-trigger\"\n data-size={size}\n className={cn(\n \"group/data-select border-input data-placeholder:text-muted-foreground [&_svg:not([class*='text-'])]:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 dark:hover:bg-input/50 flex w-fit items-center justify-between gap-2 rounded-md border bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 cursor-pointer\",\n className,\n )}\n {...props}\n >\n {children}\n\n <SelectPrimitive.Icon asChild>\n {icon ?? (\n <IconChevronDown\n className=\"\n size-4 opacity-50 transition-all duration-200\n group-data-[state=open]/data-select:rotate-180\n group-data-[state=open]/data-select:opacity-100\n group-data-[state=open]/data-select:text-gray-950!\n \"\n />\n )}\n </SelectPrimitive.Icon>\n </SelectPrimitive.Trigger>\n );\n}\n\nfunction SelectContent({\n className,\n children,\n position = \"popper\",\n align = \"center\",\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.Content>) {\n return (\n <SelectPrimitive.Portal>\n <SelectPrimitive.Content\n data-slot=\"select-content\"\n className={cn(\n \"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative
|
|
1
|
+
{"version":3,"sources":["../../../../src/components/ui/form/Select.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport * as SelectPrimitive from \"@radix-ui/react-select\";\nimport { IconCheck, IconChevronDown, IconChevronUp } from '@tabler/icons-react';\nimport { cn } from \"../../../infra/utils/clsx\";\nimport { useDialogLayer } from \"../overlay/layers\";\n\nfunction Select({\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.Root>) {\n return <SelectPrimitive.Root data-slot=\"select\" {...props} />;\n}\n\nfunction SelectGroup({\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.Group>) {\n return <SelectPrimitive.Group data-slot=\"select-group\" {...props} />;\n}\n\nfunction SelectValue({\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.Value>) {\n return <SelectPrimitive.Value data-slot=\"select-value\" {...props} />;\n}\n\nfunction SelectTrigger({\n className,\n size = \"default\",\n children,\n icon,\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.Trigger> & {\n size?: \"sm\" | \"default\";\n icon?: React.ReactNode;\n}) {\n return (\n <SelectPrimitive.Trigger\n data-slot=\"select-trigger\"\n data-size={size}\n className={cn(\n \"group/data-select border-input data-placeholder:text-muted-foreground [&_svg:not([class*='text-'])]:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 dark:hover:bg-input/50 flex w-fit items-center justify-between gap-2 rounded-md border bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 cursor-pointer\",\n className,\n )}\n {...props}\n >\n {children}\n\n <SelectPrimitive.Icon asChild>\n {icon ?? (\n <IconChevronDown\n className=\"\n size-4 opacity-50 transition-all duration-200\n group-data-[state=open]/data-select:rotate-180\n group-data-[state=open]/data-select:opacity-100\n group-data-[state=open]/data-select:text-gray-950!\n \"\n />\n )}\n </SelectPrimitive.Icon>\n </SelectPrimitive.Trigger>\n );\n}\n\nfunction SelectContent({\n className,\n children,\n position = \"popper\",\n align = \"center\",\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.Content>) {\n // Portaliza para o body, entao disputa z com o conteudo do modal que o abriu.\n const layer = useDialogLayer();\n return (\n <SelectPrimitive.Portal>\n <SelectPrimitive.Content\n data-slot=\"select-content\"\n className={cn(\n \"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative max-h-(--radix-select-content-available-height) min-w-32 origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border shadow-md\",\n layer.popover,\n position === \"popper\" &&\n \"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1\",\n className,\n )}\n position={position}\n align={align}\n {...props}\n >\n <SelectScrollUpButton />\n <SelectPrimitive.Viewport\n className={cn(\n \"p-1\",\n position === \"popper\" &&\n \"h-(--radix-select-trigger-height) w-full min-w-(--radix-select-trigger-width) scroll-my-1\",\n )}\n >\n {children}\n </SelectPrimitive.Viewport>\n <SelectScrollDownButton />\n </SelectPrimitive.Content>\n </SelectPrimitive.Portal>\n );\n}\n\nfunction SelectLabel({\n className,\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.Label>) {\n return (\n <SelectPrimitive.Label\n data-slot=\"select-label\"\n className={cn(\"text-muted-foreground px-2 py-1.5 text-xs\", className)}\n {...props}\n />\n );\n}\n\nfunction SelectItem({\n className,\n children,\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.Item>) {\n return (\n <SelectPrimitive.Item\n data-slot=\"select-item\"\n className={cn(\n \"focus:bg-accent focus:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex w-full items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2 cursor-pointer\",\n className,\n )}\n {...props}\n >\n <span className=\"absolute right-2 flex size-3.5 items-center justify-center\">\n <SelectPrimitive.ItemIndicator>\n <IconCheck className=\"size-4\" />\n </SelectPrimitive.ItemIndicator>\n </span>\n <SelectPrimitive.ItemText className=\"cursor-pointer\">\n {children}\n </SelectPrimitive.ItemText>\n </SelectPrimitive.Item>\n );\n}\n\nfunction SelectSeparator({\n className,\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.Separator>) {\n return (\n <SelectPrimitive.Separator\n data-slot=\"select-separator\"\n className={cn(\"bg-border pointer-events-none -mx-1 my-1 h-px\", className)}\n {...props}\n />\n );\n}\n\nfunction SelectScrollUpButton({\n className,\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.ScrollUpButton>) {\n return (\n <SelectPrimitive.ScrollUpButton\n data-slot=\"select-scroll-up-button\"\n className={cn(\n \"flex cursor-default items-center justify-center py-1\",\n className,\n )}\n {...props}\n >\n <IconChevronUp className=\"size-4\" />\n </SelectPrimitive.ScrollUpButton>\n );\n}\n\nfunction SelectScrollDownButton({\n className,\n ...props\n}: React.ComponentProps<typeof SelectPrimitive.ScrollDownButton>) {\n return (\n <SelectPrimitive.ScrollDownButton\n data-slot=\"select-scroll-down-button\"\n className={cn(\n \"flex cursor-default items-center justify-center py-1\",\n className,\n )}\n {...props}\n >\n <IconChevronDown className=\"size-4\" />\n </SelectPrimitive.ScrollDownButton>\n );\n}\n\nexport {\n Select,\n SelectContent,\n SelectGroup,\n SelectItem,\n SelectLabel,\n SelectScrollDownButton,\n SelectScrollUpButton,\n SelectSeparator,\n SelectTrigger,\n SelectValue,\n};\n"],"mappings":"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","names":[]}
|
|
@@ -4,18 +4,7 @@ import * as React from "react";
|
|
|
4
4
|
import * as DialogPrimitive from "@radix-ui/react-dialog";
|
|
5
5
|
import { IconX } from "@tabler/icons-react";
|
|
6
6
|
import { cn } from "../../../infra/utils/clsx";
|
|
7
|
-
|
|
8
|
-
const DIALOG_LAYERS = [
|
|
9
|
-
{ overlay: "z-[1000]", content: "z-[1001]" },
|
|
10
|
-
{ overlay: "z-[1010]", content: "z-[1011]" },
|
|
11
|
-
{ overlay: "z-[1020]", content: "z-[1021]" },
|
|
12
|
-
{ overlay: "z-[1030]", content: "z-[1031]" }
|
|
13
|
-
];
|
|
14
|
-
function useDialogLayer() {
|
|
15
|
-
const depth = React.useContext(DialogDepthContext);
|
|
16
|
-
const index = Math.min(Math.max(depth - 1, 0), DIALOG_LAYERS.length - 1);
|
|
17
|
-
return DIALOG_LAYERS[index];
|
|
18
|
-
}
|
|
7
|
+
import { DialogDepthContext, useDialogLayer } from "./layers";
|
|
19
8
|
function Dialog({
|
|
20
9
|
...props
|
|
21
10
|
}) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/components/ui/overlay/Dialog.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport * as DialogPrimitive from \"@radix-ui/react-dialog\";\nimport { IconX } from '@tabler/icons-react';\nimport { cn } from \"../../../infra/utils/clsx\";\n\n// Cada `Dialog` aninhado sobe uma
|
|
1
|
+
{"version":3,"sources":["../../../../src/components/ui/overlay/Dialog.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport * as DialogPrimitive from \"@radix-ui/react-dialog\";\nimport { IconX } from '@tabler/icons-react';\nimport { cn } from \"../../../infra/utils/clsx\";\nimport { DialogDepthContext, useDialogLayer } from \"./layers\";\n\n// Cada `Dialog` aninhado sobe uma faixa de z-index, para que o overlay do modal\n// de cima cubra o conteudo do modal de baixo. A escala (e os dropdowns e\n// tooltips que precisam acompanhar) vive em `./layers`.\n\nfunction Dialog({\n ...props\n}: React.ComponentProps<typeof DialogPrimitive.Root>) {\n const depth = React.useContext(DialogDepthContext);\n return (\n <DialogDepthContext.Provider value={depth + 1}>\n <DialogPrimitive.Root data-slot=\"dialog\" {...props} />\n </DialogDepthContext.Provider>\n );\n}\n\nfunction DialogTrigger({\n ...props\n}: React.ComponentProps<typeof DialogPrimitive.Trigger>) {\n return <DialogPrimitive.Trigger data-slot=\"dialog-trigger\" {...props} />;\n}\n\nfunction DialogPortal({\n ...props\n}: React.ComponentProps<typeof DialogPrimitive.Portal>) {\n return <DialogPrimitive.Portal data-slot=\"dialog-portal\" {...props} />;\n}\n\nfunction DialogClose({\n ...props\n}: React.ComponentProps<typeof DialogPrimitive.Close>) {\n return <DialogPrimitive.Close data-slot=\"dialog-close\" {...props} />;\n}\n\nfunction DialogOverlay({\n className,\n ...props\n}: React.ComponentProps<typeof DialogPrimitive.Overlay>) {\n const layer = useDialogLayer();\n return (\n <DialogPrimitive.Overlay\n data-slot=\"dialog-overlay\"\n className={cn(\n \"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 fixed inset-0 bg-black/50\",\n layer.overlay,\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction DialogContent({\n className,\n children,\n showCloseButton = true,\n overlayClassName,\n hideOverlay = false,\n ...props\n}: React.ComponentProps<typeof DialogPrimitive.Content> & {\n showCloseButton?: boolean;\n overlayClassName?: string;\n hideOverlay?: boolean;\n}) {\n const layer = useDialogLayer();\n return (\n <DialogPortal data-slot=\"dialog-portal\">\n {!hideOverlay && <DialogOverlay className={overlayClassName} />}\n <DialogPrimitive.Content\n data-slot=\"dialog-content\"\n className={cn(\n \"bg-background data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 fixed top-[50%] left-[50%] grid w-full max-w-[calc(100%-2rem)] translate-x-[-50%] translate-y-[-50%] gap-4 rounded-lg border p-6 shadow-lg duration-200 sm:max-w-lg\",\n layer.content,\n className,\n )}\n {...props}\n >\n {children}\n {showCloseButton && (\n <div className=\"absolute top-0.5 right-0\">\n <div className=\"p-4\">\n <DialogPrimitive.Close\n data-slot=\"dialog-close\"\n className=\"hover:cursor-pointer flex size-8 items-center justify-center rounded-lg p-2 text-gray-500 transition-colors hover:bg-gray-100 hover:text-gray-950 focus:outline-none\"\n >\n <IconX className=\"size-[18px]\" />\n <span className=\"sr-only\">Close</span>\n </DialogPrimitive.Close>\n </div>\n </div>\n )}\n </DialogPrimitive.Content>\n </DialogPortal>\n );\n}\n\nfunction DialogHeader({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"dialog-header\"\n className={cn(\n \"p-5 flex flex-col gap-2 text-center sm:text-left\",\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction DialogFooter({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"dialog-footer\"\n className={cn(\n \"flex flex-col-reverse gap-2 sm:flex-row sm:justify-end\",\n className,\n )}\n {...props}\n />\n );\n}\n\nfunction DialogTitle({\n className,\n ...props\n}: React.ComponentProps<typeof DialogPrimitive.Title>) {\n return (\n <DialogPrimitive.Title\n data-slot=\"dialog-title\"\n className={cn(\"text-lg leading-none font-semibold\", className)}\n {...props}\n />\n );\n}\n\nfunction DialogDescription({\n className,\n ...props\n}: React.ComponentProps<typeof DialogPrimitive.Description>) {\n return (\n <DialogPrimitive.Description\n data-slot=\"dialog-description\"\n className={cn(\"text-muted-foreground text-sm\", className)}\n {...props}\n />\n );\n}\n\nexport {\n Dialog,\n DialogClose,\n DialogContent,\n DialogDescription,\n DialogFooter,\n DialogHeader,\n DialogOverlay,\n DialogPortal,\n DialogTitle,\n DialogTrigger,\n};\n"],"mappings":"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","names":[]}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { jsx } from "react/jsx-runtime";
|
|
3
3
|
import * as PopoverPrimitive from "@radix-ui/react-popover";
|
|
4
4
|
import { cn } from "../../../infra/utils/clsx";
|
|
5
|
+
import { useDialogLayer } from "./layers";
|
|
5
6
|
function Popover({
|
|
6
7
|
...props
|
|
7
8
|
}) {
|
|
@@ -18,6 +19,7 @@ function PopoverContent({
|
|
|
18
19
|
sideOffset = 4,
|
|
19
20
|
...props
|
|
20
21
|
}) {
|
|
22
|
+
const layer = useDialogLayer();
|
|
21
23
|
return /* @__PURE__ */ jsx(PopoverPrimitive.Portal, { children: /* @__PURE__ */ jsx(
|
|
22
24
|
PopoverPrimitive.Content,
|
|
23
25
|
{
|
|
@@ -25,7 +27,8 @@ function PopoverContent({
|
|
|
25
27
|
align,
|
|
26
28
|
sideOffset,
|
|
27
29
|
className: cn(
|
|
28
|
-
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2
|
|
30
|
+
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 w-72 origin-(--radix-popover-content-transform-origin) rounded-md border p-4 shadow-md outline-hidden",
|
|
31
|
+
layer.popover,
|
|
29
32
|
className
|
|
30
33
|
),
|
|
31
34
|
...props
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/components/ui/overlay/Popover.tsx"],"sourcesContent":["'use client';\n\n\"use client\"\n\nimport * as React from \"react\"\nimport * as PopoverPrimitive from \"@radix-ui/react-popover\"\nimport { cn } from \"../../../infra/utils/clsx\"\n\nfunction Popover({\n ...props\n}: React.ComponentProps<typeof PopoverPrimitive.Root>) {\n return <PopoverPrimitive.Root data-slot=\"popover\" {...props} />\n}\n\nfunction PopoverTrigger({\n ...props\n}: React.ComponentProps<typeof PopoverPrimitive.Trigger>) {\n return <PopoverPrimitive.Trigger data-slot=\"popover-trigger\" {...props} />\n}\n\nfunction PopoverContent({\n className,\n align = \"center\",\n sideOffset = 4,\n ...props\n}: React.ComponentProps<typeof PopoverPrimitive.Content>) {\n return (\n <PopoverPrimitive.Portal>\n <PopoverPrimitive.Content\n data-slot=\"popover-content\"\n align={align}\n sideOffset={sideOffset}\n className={cn(\n \"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2
|
|
1
|
+
{"version":3,"sources":["../../../../src/components/ui/overlay/Popover.tsx"],"sourcesContent":["'use client';\n\n\"use client\"\n\nimport * as React from \"react\"\nimport * as PopoverPrimitive from \"@radix-ui/react-popover\"\nimport { cn } from \"../../../infra/utils/clsx\"\nimport { useDialogLayer } from \"./layers\"\n\nfunction Popover({\n ...props\n}: React.ComponentProps<typeof PopoverPrimitive.Root>) {\n return <PopoverPrimitive.Root data-slot=\"popover\" {...props} />\n}\n\nfunction PopoverTrigger({\n ...props\n}: React.ComponentProps<typeof PopoverPrimitive.Trigger>) {\n return <PopoverPrimitive.Trigger data-slot=\"popover-trigger\" {...props} />\n}\n\nfunction PopoverContent({\n className,\n align = \"center\",\n sideOffset = 4,\n ...props\n}: React.ComponentProps<typeof PopoverPrimitive.Content>) {\n // Portaliza para o body, entao disputa z com o conteudo do modal que o abriu.\n const layer = useDialogLayer()\n return (\n <PopoverPrimitive.Portal>\n <PopoverPrimitive.Content\n data-slot=\"popover-content\"\n align={align}\n sideOffset={sideOffset}\n className={cn(\n \"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 w-72 origin-(--radix-popover-content-transform-origin) rounded-md border p-4 shadow-md outline-hidden\",\n layer.popover,\n className\n )}\n {...props}\n />\n </PopoverPrimitive.Portal>\n )\n}\n\nfunction PopoverAnchor({\n ...props\n}: React.ComponentProps<typeof PopoverPrimitive.Anchor>) {\n return <PopoverPrimitive.Anchor data-slot=\"popover-anchor\" {...props} />\n}\n\nexport { Popover, PopoverTrigger, PopoverContent, PopoverAnchor }\n"],"mappings":";AAYS;AAPT,YAAY,sBAAsB;AAClC,SAAS,UAAU;AACnB,SAAS,sBAAsB;AAE/B,SAAS,QAAQ;AAAA,EACf,GAAG;AACL,GAAuD;AACrD,SAAO,oBAAC,iBAAiB,MAAjB,EAAsB,aAAU,WAAW,GAAG,OAAO;AAC/D;AAEA,SAAS,eAAe;AAAA,EACtB,GAAG;AACL,GAA0D;AACxD,SAAO,oBAAC,iBAAiB,SAAjB,EAAyB,aAAU,mBAAmB,GAAG,OAAO;AAC1E;AAEA,SAAS,eAAe;AAAA,EACtB;AAAA,EACA,QAAQ;AAAA,EACR,aAAa;AAAA,EACb,GAAG;AACL,GAA0D;AAExD,QAAM,QAAQ,eAAe;AAC7B,SACE,oBAAC,iBAAiB,QAAjB,EACC;AAAA,IAAC,iBAAiB;AAAA,IAAjB;AAAA,MACC,aAAU;AAAA,MACV;AAAA,MACA;AAAA,MACA,WAAW;AAAA,QACT;AAAA,QACA,MAAM;AAAA,QACN;AAAA,MACF;AAAA,MACC,GAAG;AAAA;AAAA,EACN,GACF;AAEJ;AAEA,SAAS,cAAc;AAAA,EACrB,GAAG;AACL,GAAyD;AACvD,SAAO,oBAAC,iBAAiB,QAAjB,EAAwB,aAAU,kBAAkB,GAAG,OAAO;AACxE;","names":[]}
|
|
@@ -5,13 +5,15 @@ import * as SheetPrimitive from "@radix-ui/react-dialog";
|
|
|
5
5
|
import { IconX } from "@tabler/icons-react";
|
|
6
6
|
import { cn } from "../../../infra/utils/clsx";
|
|
7
7
|
import { hideCrisp, showCrisp } from "../../embeds/CrispEmbed";
|
|
8
|
+
import { DialogDepthContext, useDialogLayer } from "./layers";
|
|
8
9
|
function Sheet({ onOpenChange, modal = false, ...props }) {
|
|
9
10
|
const handleOpenChange = React.useCallback((open) => {
|
|
10
11
|
if (open) hideCrisp();
|
|
11
12
|
else showCrisp();
|
|
12
13
|
onOpenChange?.(open);
|
|
13
14
|
}, [onOpenChange]);
|
|
14
|
-
|
|
15
|
+
const depth = React.useContext(DialogDepthContext);
|
|
16
|
+
return /* @__PURE__ */ jsx(DialogDepthContext.Provider, { value: depth + 1, children: /* @__PURE__ */ jsx(SheetPrimitive.Root, { "data-slot": "sheet", modal, onOpenChange: handleOpenChange, ...props }) });
|
|
15
17
|
}
|
|
16
18
|
function SheetTrigger({
|
|
17
19
|
...props
|
|
@@ -32,12 +34,14 @@ function SheetOverlay({
|
|
|
32
34
|
className,
|
|
33
35
|
...props
|
|
34
36
|
}) {
|
|
37
|
+
const layer = useDialogLayer();
|
|
35
38
|
return /* @__PURE__ */ jsx(SheetPrimitive.Close, { tabIndex: -1, asChild: true, children: /* @__PURE__ */ jsx(
|
|
36
39
|
"div",
|
|
37
40
|
{
|
|
38
41
|
"data-slot": "sheet-overlay",
|
|
39
42
|
className: cn(
|
|
40
|
-
"animate-in fade-in-0 fixed inset-0
|
|
43
|
+
"animate-in fade-in-0 fixed inset-0 bg-black/50 cursor-default",
|
|
44
|
+
layer.overlay,
|
|
41
45
|
className
|
|
42
46
|
),
|
|
43
47
|
...props
|
|
@@ -52,6 +56,7 @@ function SheetContent({
|
|
|
52
56
|
noAnimation = false,
|
|
53
57
|
...props
|
|
54
58
|
}) {
|
|
59
|
+
const layer = useDialogLayer();
|
|
55
60
|
return /* @__PURE__ */ jsxs(SheetPortal, { children: [
|
|
56
61
|
/* @__PURE__ */ jsx(SheetOverlay, { className: noAnimation ? "!animate-none" : void 0 }),
|
|
57
62
|
/* @__PURE__ */ jsxs(
|
|
@@ -61,7 +66,8 @@ function SheetContent({
|
|
|
61
66
|
onInteractOutside: (e) => e.preventDefault(),
|
|
62
67
|
onPointerDownOutside: (e) => e.preventDefault(),
|
|
63
68
|
className: cn(
|
|
64
|
-
"bg-background fixed
|
|
69
|
+
"bg-background fixed flex flex-col gap-4 shadow-lg",
|
|
70
|
+
layer.content,
|
|
65
71
|
!noAnimation && "data-[state=open]:animate-in data-[state=closed]:animate-out transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500",
|
|
66
72
|
!noAnimation && side === "right" && "data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right",
|
|
67
73
|
!noAnimation && side === "left" && "data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/components/ui/overlay/Sheet.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport * as SheetPrimitive from \"@radix-ui/react-dialog\";\nimport { IconX } from '@tabler/icons-react';\nimport { cn } from \"../../../infra/utils/clsx\";\nimport { hideCrisp, showCrisp } from \"../../embeds/CrispEmbed\";\n\nfunction Sheet({ onOpenChange, modal = false, ...props }: React.ComponentProps<typeof SheetPrimitive.Root>) {\n const handleOpenChange = React.useCallback((open: boolean) => {\n if (open) hideCrisp();\n else showCrisp();\n onOpenChange?.(open);\n }, [onOpenChange]);\n\n return <SheetPrimitive.Root data-slot=\"sheet\" modal={modal} onOpenChange={handleOpenChange} {...props}
|
|
1
|
+
{"version":3,"sources":["../../../../src/components/ui/overlay/Sheet.tsx"],"sourcesContent":["\"use client\";\n\nimport * as React from \"react\";\nimport * as SheetPrimitive from \"@radix-ui/react-dialog\";\nimport { IconX } from '@tabler/icons-react';\nimport { cn } from \"../../../infra/utils/clsx\";\nimport { hideCrisp, showCrisp } from \"../../embeds/CrispEmbed\";\nimport { DialogDepthContext, useDialogLayer } from \"./layers\";\n\nfunction Sheet({ onOpenChange, modal = false, ...props }: React.ComponentProps<typeof SheetPrimitive.Root>) {\n const handleOpenChange = React.useCallback((open: boolean) => {\n if (open) hideCrisp();\n else showCrisp();\n onOpenChange?.(open);\n }, [onOpenChange]);\n\n // Mesma faixa de z do `Dialog`: um `Dialog` aberto de dentro de um `Sheet` era\n // profundidade 1, entao o overlay dele (1000) nascia sob o conteudo do sheet\n // (1001). Somando aqui tambem, ele passa para a faixa de cima.\n const depth = React.useContext(DialogDepthContext);\n\n return (\n <DialogDepthContext.Provider value={depth + 1}>\n <SheetPrimitive.Root data-slot=\"sheet\" modal={modal} onOpenChange={handleOpenChange} {...props} />\n </DialogDepthContext.Provider>\n );\n}\n\nfunction SheetTrigger({\n ...props\n}: React.ComponentProps<typeof SheetPrimitive.Trigger>) {\n return <SheetPrimitive.Trigger data-slot=\"sheet-trigger\" {...props} />;\n}\n\nfunction SheetClose({\n ...props\n}: React.ComponentProps<typeof SheetPrimitive.Close>) {\n return <SheetPrimitive.Close data-slot=\"sheet-close\" {...props} />;\n}\n\nfunction SheetPortal({\n ...props\n}: React.ComponentProps<typeof SheetPrimitive.Portal>) {\n return <SheetPrimitive.Portal data-slot=\"sheet-portal\" {...props} />;\n}\n\nfunction SheetOverlay({\n className,\n ...props\n}: React.ComponentProps<\"div\">) {\n const layer = useDialogLayer();\n return (\n <SheetPrimitive.Close tabIndex={-1} asChild>\n <div\n data-slot=\"sheet-overlay\"\n className={cn(\n \"animate-in fade-in-0 fixed inset-0 bg-black/50 cursor-default\",\n layer.overlay,\n className,\n )}\n {...props}\n />\n </SheetPrimitive.Close>\n );\n}\n\nfunction SheetContent({\n className,\n children,\n side = \"right\",\n showCloseButton = true,\n noAnimation = false,\n ...props\n}: React.ComponentProps<typeof SheetPrimitive.Content> & {\n side?: \"top\" | \"right\" | \"bottom\" | \"left\";\n showCloseButton?: boolean;\n noAnimation?: boolean;\n}) {\n const layer = useDialogLayer();\n return (\n <SheetPortal>\n <SheetOverlay className={noAnimation ? \"!animate-none\" : undefined} />\n <SheetPrimitive.Content\n data-slot=\"sheet-content\"\n onInteractOutside={(e) => e.preventDefault()}\n onPointerDownOutside={(e) => e.preventDefault()}\n className={cn(\n \"bg-background fixed flex flex-col gap-4 shadow-lg\",\n layer.content,\n !noAnimation &&\n \"data-[state=open]:animate-in data-[state=closed]:animate-out transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500\",\n !noAnimation &&\n side === \"right\" &&\n \"data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right\",\n !noAnimation &&\n side === \"left\" &&\n \"data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left\",\n !noAnimation &&\n side === \"top\" &&\n \"data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top\",\n !noAnimation &&\n side === \"bottom\" &&\n \"data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom\",\n side === \"right\" &&\n \"inset-y-0 right-0 h-full w-3/4 border-l sm:max-w-sm\",\n side === \"left\" &&\n \"inset-y-0 left-0 h-full w-3/4 border-r sm:max-w-sm\",\n side === \"top\" && \"inset-x-0 top-0 h-auto border-b\",\n side === \"bottom\" && \"inset-x-0 bottom-0 h-auto border-t\",\n className,\n )}\n {...props}\n >\n {children}\n {showCloseButton && (\n <SheetPrimitive.Close\n data-slot=\"sheet-close\"\n className=\"absolute top-3 right-3 hover:cursor-pointer flex size-8 items-center justify-center rounded-lg p-2 text-gray-500 transition-colors hover:bg-gray-100 hover:text-gray-950 focus:outline-none\"\n >\n <IconX className=\"size-[18px]\" />\n <span className=\"sr-only\">Close</span>\n </SheetPrimitive.Close>\n )}\n </SheetPrimitive.Content>\n </SheetPortal>\n );\n}\n\nfunction SheetHeader({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"sheet-header\"\n className={cn(\"flex flex-col gap-1.5 p-4\", className)}\n {...props}\n />\n );\n}\n\nfunction SheetFooter({ className, ...props }: React.ComponentProps<\"div\">) {\n return (\n <div\n data-slot=\"sheet-footer\"\n className={cn(\"mt-auto flex flex-col gap-2 p-4\", className)}\n {...props}\n />\n );\n}\n\nfunction SheetTitle({\n className,\n ...props\n}: React.ComponentProps<typeof SheetPrimitive.Title>) {\n return (\n <SheetPrimitive.Title\n data-slot=\"sheet-title\"\n className={cn(\"text-foreground font-semibold\", className)}\n {...props}\n />\n );\n}\n\nfunction SheetDescription({\n className,\n ...props\n}: React.ComponentProps<typeof SheetPrimitive.Description>) {\n return (\n <SheetPrimitive.Description\n data-slot=\"sheet-description\"\n className={cn(\"text-muted-foreground text-sm\", className)}\n {...props}\n />\n );\n}\n\nexport {\n Sheet,\n SheetTrigger,\n SheetClose,\n SheetContent,\n SheetHeader,\n SheetFooter,\n SheetTitle,\n SheetDescription,\n};\n"],"mappings":"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","names":[]}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import * as TooltipPrimitive from "@radix-ui/react-tooltip";
|
|
4
4
|
import { cn } from "../../../infra/utils/clsx";
|
|
5
|
+
import { useDialogLayer } from "./layers";
|
|
5
6
|
function TooltipProvider({
|
|
6
7
|
delayDuration = 0,
|
|
7
8
|
...props
|
|
@@ -31,13 +32,15 @@ function TooltipContent({
|
|
|
31
32
|
children,
|
|
32
33
|
...props
|
|
33
34
|
}) {
|
|
35
|
+
const layer = useDialogLayer();
|
|
34
36
|
return /* @__PURE__ */ jsx(TooltipPrimitive.Portal, { children: /* @__PURE__ */ jsxs(
|
|
35
37
|
TooltipPrimitive.Content,
|
|
36
38
|
{
|
|
37
39
|
"data-slot": "tooltip-content",
|
|
38
40
|
sideOffset,
|
|
39
41
|
className: cn(
|
|
40
|
-
"bg-foreground text-background animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2
|
|
42
|
+
"bg-foreground text-background animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 w-fit origin-(--radix-tooltip-content-transform-origin) rounded-md px-3 py-1.5 text-xs text-balance pointer-events-none",
|
|
43
|
+
layer.tooltip,
|
|
41
44
|
className
|
|
42
45
|
),
|
|
43
46
|
...props,
|