@orechio/platform-angular 0.7.2 → 0.7.4
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/fesm2022/{orechio-platform-angular-billing-billing-format-DFY2U19Y.mjs → orechio-platform-angular-billing-billing-format-C8QPXulm.mjs} +15 -2
- package/fesm2022/orechio-platform-angular-billing-billing-format-C8QPXulm.mjs.map +1 -0
- package/fesm2022/{orechio-platform-angular-billing-checkout.page-CNl0PPiE.mjs → orechio-platform-angular-billing-checkout.page-Dr6ZGDkd.mjs} +12 -6
- package/fesm2022/orechio-platform-angular-billing-checkout.page-Dr6ZGDkd.mjs.map +1 -0
- package/fesm2022/{orechio-platform-angular-billing-invoice.page-CLojIJkf.mjs → orechio-platform-angular-billing-invoice.page-CWThsx-_.mjs} +9 -8
- package/fesm2022/orechio-platform-angular-billing-invoice.page-CWThsx-_.mjs.map +1 -0
- package/fesm2022/{orechio-platform-angular-billing-invoices.page-COsCT5NY.mjs → orechio-platform-angular-billing-invoices.page-DytE1Lcz.mjs} +5 -4
- package/fesm2022/orechio-platform-angular-billing-invoices.page-DytE1Lcz.mjs.map +1 -0
- package/fesm2022/{orechio-platform-angular-billing-pix-panel.component-fdORTehK.mjs → orechio-platform-angular-billing-pix-panel.component-DrfNi22S.mjs} +26 -6
- package/fesm2022/orechio-platform-angular-billing-pix-panel.component-DrfNi22S.mjs.map +1 -0
- package/fesm2022/{orechio-platform-angular-billing-profile.page-DHjsB3jJ.mjs → orechio-platform-angular-billing-profile.page-DRkoYOZa.mjs} +2 -2
- package/fesm2022/{orechio-platform-angular-billing-profile.page-DHjsB3jJ.mjs.map → orechio-platform-angular-billing-profile.page-DRkoYOZa.mjs.map} +1 -1
- package/fesm2022/{orechio-platform-angular-billing-subscription.page-Cotupt22.mjs → orechio-platform-angular-billing-subscription.page-Dbk-Znvg.mjs} +2 -2
- package/fesm2022/{orechio-platform-angular-billing-subscription.page-Cotupt22.mjs.map → orechio-platform-angular-billing-subscription.page-Dbk-Znvg.mjs.map} +1 -1
- package/fesm2022/orechio-platform-angular-billing.mjs +5 -5
- package/fesm2022/orechio-platform-angular.mjs +25 -10
- package/fesm2022/orechio-platform-angular.mjs.map +1 -1
- package/package.json +1 -1
- package/types/orechio-platform-angular.d.ts +7 -0
- package/fesm2022/orechio-platform-angular-billing-billing-format-DFY2U19Y.mjs.map +0 -1
- package/fesm2022/orechio-platform-angular-billing-checkout.page-CNl0PPiE.mjs.map +0 -1
- package/fesm2022/orechio-platform-angular-billing-invoice.page-CLojIJkf.mjs.map +0 -1
- package/fesm2022/orechio-platform-angular-billing-invoices.page-COsCT5NY.mjs.map +0 -1
- package/fesm2022/orechio-platform-angular-billing-pix-panel.component-fdORTehK.mjs.map +0 -1
|
@@ -166,6 +166,19 @@ function moeda(valor) {
|
|
|
166
166
|
function data(iso) {
|
|
167
167
|
return iso ? new Date(iso).toLocaleDateString('pt-BR') : '';
|
|
168
168
|
}
|
|
169
|
+
/**
|
|
170
|
+
* O vencimento para a tela: "02/10/2026". É uma DATA, sem hora: a Platform a manda como meia-noite
|
|
171
|
+
* UTC ("2026-10-02T00:00:00Z") ou "2026-10-02", e passar por `new Date()` a deslocaria para o dia
|
|
172
|
+
* anterior no fuso do navegador (Manaus, Brasília). Lê os 10 primeiros caracteres AAAA-MM-DD e
|
|
173
|
+
* formata por PARTES DO TEXTO. Formato inesperado volta como veio.
|
|
174
|
+
*/
|
|
175
|
+
function dataDoVencimento(valor) {
|
|
176
|
+
const partes = /^(\d{4})-(\d{2})-(\d{2})(?![\d])/.exec(valor.trim());
|
|
177
|
+
if (!partes)
|
|
178
|
+
return valor;
|
|
179
|
+
const [, ano, mes, dia] = partes;
|
|
180
|
+
return `${dia}/${mes}/${ano}`;
|
|
181
|
+
}
|
|
169
182
|
const FUSO_DE_BRASILIA = 'America/Sao_Paulo';
|
|
170
183
|
const SUFIXO_DE_BRASILIA = '(horário de Brasília)';
|
|
171
184
|
/** "AAAA-MM-DD HH:mm[:ss]" — o `expirationDate` do ASAAS: sem fuso, já em horário de Brasília. */
|
|
@@ -299,5 +312,5 @@ const ESTILO_BASE = `
|
|
|
299
312
|
}
|
|
300
313
|
`;
|
|
301
314
|
|
|
302
|
-
export { BillingClient as B, STATUS_DA_FATURA as S, BillingErrorComponent as a, mascararTelefone as b, mascararDocumento as c, data as d, mascararCpf as e, mascararCnpj as f, moeda as g, mascararCep as m, soDigitos as s, validadeDoPix as v };
|
|
303
|
-
//# sourceMappingURL=orechio-platform-angular-billing-billing-format-
|
|
315
|
+
export { BillingClient as B, STATUS_DA_FATURA as S, BillingErrorComponent as a, mascararTelefone as b, mascararDocumento as c, data as d, mascararCpf as e, mascararCnpj as f, moeda as g, dataDoVencimento as h, mascararCep as m, soDigitos as s, validadeDoPix as v };
|
|
316
|
+
//# sourceMappingURL=orechio-platform-angular-billing-billing-format-C8QPXulm.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"orechio-platform-angular-billing-billing-format-C8QPXulm.mjs","sources":["../../../projects/platform-angular/billing/src/billing-client.ts","../../../projects/platform-angular/billing/src/billing-errors.ts","../../../projects/platform-angular/billing/src/billing-error.component.ts","../../../projects/platform-angular/billing/src/billing-format.ts"],"sourcesContent":["import { HttpClient, HttpErrorResponse, HttpParams } from '@angular/common/http';\r\nimport { Injectable, inject } from '@angular/core';\r\nimport { Observable, catchError, throwError } from 'rxjs';\r\nimport { ProblemDetails, normalizeProblemDetails } from '@orechio/platform-angular';\r\nimport { ORECHIO_BILLING_CONFIG } from './billing-config';\r\nimport {\r\n BillingAddressRequest,\r\n BillingContactRequest,\r\n BillingCycle,\r\n BillingProfile,\r\n CheckoutResult,\r\n CreateBillingProfileRequest,\r\n InvoiceDetail,\r\n InvoicesPage,\r\n Plan,\r\n PricingPreview,\r\n RefreshedPayment,\r\n Subscription,\r\n} from './billing-models';\r\n\r\n/**\r\n * O cliente do BFF de cobrança (`Orechio.Platform.Bff.Account` 0.2.0,\r\n * `MapOrechioBillingEndpoints`). Same-origin, só com o BFF do host — mesmo desenho do\r\n * `AccountClient`, e o erro sai pelo mesmo `normalizeProblemDetails`.\r\n */\r\n@Injectable({ providedIn: 'root' })\r\nexport class BillingClient {\r\n private readonly http = inject(HttpClient);\r\n private readonly config = inject(ORECHIO_BILLING_CONFIG);\r\n\r\n getSubscription(): Observable<Subscription> {\r\n return this.falha(this.http.get<Subscription>(this.url('/subscription')));\r\n }\r\n\r\n getProfile(): Observable<BillingProfile> {\r\n return this.falha(this.http.get<BillingProfile>(this.url('/profile')));\r\n }\r\n\r\n createProfile(request: CreateBillingProfileRequest): Observable<{ id: string }> {\r\n return this.falha(this.http.post<{ id: string }>(this.url('/profile'), request));\r\n }\r\n\r\n updateProfileContact(request: BillingContactRequest): Observable<void> {\r\n return this.falha(this.http.patch<void>(this.url('/profile/contact'), request));\r\n }\r\n\r\n updateProfileAddress(request: BillingAddressRequest): Observable<void> {\r\n return this.falha(this.http.patch<void>(this.url('/profile/address'), request));\r\n }\r\n\r\n listPlans(): Observable<Plan[]> {\r\n return this.falha(this.http.get<Plan[]>(this.url('/plans')));\r\n }\r\n\r\n previewPricing(planId: string, branchCount: number, cycle: BillingCycle): Observable<PricingPreview> {\r\n const params = new HttpParams({ fromObject: { planId, branchCount, cycle } });\r\n return this.falha(this.http.get<PricingPreview>(this.url('/pricing/preview'), { params }));\r\n }\r\n\r\n /**\r\n * Checkout PIX. O corpo é o `CheckoutRequest` da Platform (`cycle`, `method`), sem\r\n * `applicationId`: com o token de produto, a Platform usa o app do token.\r\n */\r\n checkout(cycle: BillingCycle): Observable<CheckoutResult> {\r\n return this.falha(this.http.post<CheckoutResult>(this.url('/checkout'), { cycle, method: 'pix' }));\r\n }\r\n\r\n listInvoices(page = 1, pageSize = 20): Observable<InvoicesPage> {\r\n const params = new HttpParams({ fromObject: { page, pageSize } });\r\n return this.falha(this.http.get<InvoicesPage>(this.url('/invoices'), { params }));\r\n }\r\n\r\n getInvoice(id: string): Observable<InvoiceDetail> {\r\n return this.falha(this.http.get<InvoiceDetail>(this.url(`/invoices/${encodeURIComponent(id)}`)));\r\n }\r\n\r\n refreshInvoicePix(id: string): Observable<RefreshedPayment> {\r\n return this.falha(this.http.post<RefreshedPayment>(this.url(`/invoices/${encodeURIComponent(id)}/refresh`), null));\r\n }\r\n\r\n private url(path: string): string {\r\n return `${this.config.basePath}${path}`;\r\n }\r\n\r\n private falha<T>(chamada: Observable<T>): Observable<T> {\r\n return chamada.pipe(\r\n catchError((error: HttpErrorResponse) =>\r\n throwError((): ProblemDetails => normalizeProblemDetails(error)),\r\n ),\r\n );\r\n }\r\n}\r\n","import { ProblemDetails } from '@orechio/platform-angular';\r\n\r\nexport interface MensagemDeErro {\r\n texto: string;\r\n /** Só para code desconhecido: é o que o suporte cruza com o log dos dois lados. */\r\n correlationId?: string;\r\n}\r\n\r\nconst ENDERECO_INCOMPLETO = 'Preencha o endereço completo.';\r\nconst DOCUMENTO_INVALIDO = 'CPF ou CNPJ inválido.';\r\nconst DOCUMENTO_TRAVADO =\r\n 'O documento e o nome já foram usados em uma cobrança e não podem mais ser alterados.';\r\nconst PLANO_INDISPONIVEL = 'O plano da assinatura não está disponível. Fale com o suporte.';\r\nconst SO_O_TITULAR = 'Só o titular da conta pode ver e pagar a cobrança.';\r\n\r\n/**\r\n * Os codes que as rotas de cobrança da Platform e do BFF emitem de fato\r\n * (`Commerce*Endpoints`, handlers de `Billing/`, value objects de perfil,\r\n * `PermissionAuthorization`, `Bff.Account`).\r\n */\r\nconst MENSAGENS: Record<string, string> = {\r\n 'session.missing': 'Sua sessão expirou. Entre novamente para continuar.',\r\n 'platform.unavailable':\r\n 'O serviço de cobrança está indisponível no momento. Tente novamente em instantes.',\r\n 'http.0': 'Sem conexão com o servidor. Verifique sua internet e tente novamente.',\r\n rate_limit_exceeded: 'Muitas tentativas. Aguarde alguns minutos e tente novamente.',\r\n\r\n 'access.platform_only': SO_O_TITULAR,\r\n 'permission.denied': SO_O_TITULAR,\r\n\r\n 'subscription.not_found': 'Nenhuma assinatura encontrada para esta conta.',\r\n\r\n 'billing_profile.not_found': 'Cadastre os dados de cobrança antes de pagar.',\r\n 'billing_profile.already_exists': 'Os dados de cobrança já estão cadastrados.',\r\n 'billing_profile.legal_name_required': 'Informe o nome completo ou a razão social.',\r\n 'billing_profile.billing_email_required': 'Informe o e-mail de cobrança.',\r\n 'billing_profile.billing_email_invalid': 'Informe um e-mail de cobrança válido.',\r\n 'billing_profile.mobile_phone_too_long': 'O celular informado é longo demais.',\r\n 'billing_profile.document_type_locked': DOCUMENTO_TRAVADO,\r\n 'billing_profile.document_number_locked': DOCUMENTO_TRAVADO,\r\n 'billing_profile.legal_name_locked': DOCUMENTO_TRAVADO,\r\n 'tax_document.value_required': 'Informe o CPF ou o CNPJ.',\r\n 'tax_document.length_invalid': DOCUMENTO_INVALIDO,\r\n 'tax_document.checksum_invalid': DOCUMENTO_INVALIDO,\r\n 'address.postal_code_invalid': 'CEP inválido.',\r\n 'address.state_invalid': 'UF inválida. Use a sigla do estado, como SP.',\r\n 'address.street_required': ENDERECO_INCOMPLETO,\r\n 'address.number_required': ENDERECO_INCOMPLETO,\r\n 'address.neighborhood_required': ENDERECO_INCOMPLETO,\r\n 'address.city_required': ENDERECO_INCOMPLETO,\r\n\r\n 'checkout.subscription_not_eligible':\r\n 'Não há pagamento de assinatura a fazer agora. Veja as faturas em aberto.',\r\n 'checkout.open_invoice_exists':\r\n 'Já existe uma fatura em aberto. Pague a fatura em aberto em vez de gerar uma nova cobrança.',\r\n 'checkout.pending_other_method':\r\n 'Já existe uma cobrança em aberto por outro meio de pagamento. Veja as faturas.',\r\n 'checkout.credit_card_not_supported': 'Pagamento por cartão ainda não está disponível.',\r\n 'checkout.invoice_in_inconsistent_state':\r\n 'A cobrança anterior ficou incompleta. Tente novamente em alguns minutos.',\r\n 'billing.cycle_invalid': 'Escolha um ciclo de cobrança válido.',\r\n 'pricing_preview.cycle_invalid': 'Escolha um ciclo de cobrança válido.',\r\n 'plan.not_found': PLANO_INDISPONIVEL,\r\n 'plan.not_available': PLANO_INDISPONIVEL,\r\n 'subscription.plan_inactive': PLANO_INDISPONIVEL,\r\n\r\n 'invoice.not_found': 'Fatura não encontrada.',\r\n 'invoice.payment_unavailable':\r\n 'Esta cobrança não está mais disponível para pagamento. Fale com o suporte.',\r\n 'invoice.refresh_not_allowed': 'Esta fatura não está mais em aberto.',\r\n 'invoice.refresh_pix_only': 'Só é possível gerar QR para faturas PIX.',\r\n 'invoice.no_external_payment':\r\n 'Esta fatura ainda não tem cobrança gerada. Tente novamente em alguns minutos.',\r\n};\r\n\r\nconst GENERICA =\r\n 'Não foi possível concluir a operação. Tente novamente; se continuar, informe o código de atendimento ao suporte.';\r\n\r\n/**\r\n * O texto de tela para um erro do BFF, escolhido por `code`. O corpo do erro (`message`) nunca\r\n * vira texto: ele é escrito para log, não para o cliente.\r\n */\r\nexport function mensagemDeErro(problema: ProblemDetails): MensagemDeErro {\r\n const conhecida = MENSAGENS[problema.code];\r\n if (conhecida) return { texto: conhecida };\r\n\r\n return problema.correlationId\r\n ? { texto: GENERICA, correlationId: problema.correlationId }\r\n : { texto: GENERICA };\r\n}\r\n","import { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';\r\nimport { ProblemDetails } from '@orechio/platform-angular';\r\nimport { mensagemDeErro } from './billing-errors';\r\n\r\n@Component({\r\n selector: 'orechio-billing-error',\r\n standalone: true,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n template: `\r\n <div class=\"orechio-billing__error\" role=\"alert\">\r\n <p>{{ mensagem().texto }}</p>\r\n @if (mensagem().correlationId; as id) {\r\n <p>Código de atendimento: <code>{{ id }}</code></p>\r\n }\r\n </div>\r\n `,\r\n styles: `.orechio-billing__error { color: var(--orechio-color-danger, #b00020); }`,\r\n})\r\nexport class BillingErrorComponent {\r\n readonly problema = input.required<ProblemDetails>();\r\n protected readonly mensagem = computed(() => mensagemDeErro(this.problema()));\r\n}\r\n","import { InvoiceStatus } from './billing-models';\r\n\r\nconst MOEDA = new Intl.NumberFormat('pt-BR', { style: 'currency', currency: 'BRL' });\r\n\r\nexport function moeda(valor: number): string {\r\n return MOEDA.format(valor);\r\n}\r\n\r\nexport function data(iso: string | null | undefined): string {\r\n return iso ? new Date(iso).toLocaleDateString('pt-BR') : '';\r\n}\r\n\r\n/**\r\n * O vencimento para a tela: \"02/10/2026\". É uma DATA, sem hora: a Platform a manda como meia-noite\r\n * UTC (\"2026-10-02T00:00:00Z\") ou \"2026-10-02\", e passar por `new Date()` a deslocaria para o dia\r\n * anterior no fuso do navegador (Manaus, Brasília). Lê os 10 primeiros caracteres AAAA-MM-DD e\r\n * formata por PARTES DO TEXTO. Formato inesperado volta como veio.\r\n */\r\nexport function dataDoVencimento(valor: string): string {\r\n const partes = /^(\\d{4})-(\\d{2})-(\\d{2})(?![\\d])/.exec(valor.trim());\r\n if (!partes) return valor;\r\n const [, ano, mes, dia] = partes;\r\n return `${dia}/${mes}/${ano}`;\r\n}\r\n\r\nconst FUSO_DE_BRASILIA = 'America/Sao_Paulo';\r\nconst SUFIXO_DE_BRASILIA = '(horário de Brasília)';\r\n\r\n/** \"AAAA-MM-DD HH:mm[:ss]\" — o `expirationDate` do ASAAS: sem fuso, já em horário de Brasília. */\r\nconst SEM_FUSO = /^(\\d{4})-(\\d{2})-(\\d{2})[ T](\\d{2}):(\\d{2})(?::(\\d{2})(?:\\.\\d+)?)?$/;\r\n/** O mesmo, com fuso explícito: `Z` ou `±hh:mm` / `±hhmm`. */\r\nconst COM_FUSO = /^\\d{4}-\\d{2}-\\d{2}[ T]\\d{2}:\\d{2}(?::\\d{2}(?:\\.\\d+)?)?(?:Z|[+-]\\d{2}:?\\d{2})$/i;\r\n\r\n/**\r\n * A validade do PIX para a tela: \"02/10/2026 às 23:59 (horário de Brasília)\".\r\n *\r\n * O valor do ASAAS NÃO passa por `new Date()`: sem fuso, o construtor o leria no fuso do\r\n * navegador e a hora exibida se deslocaria. Ele é formatado por PARTES DO TEXTO. Só o valor\r\n * com fuso explícito é convertido (para America/Sao_Paulo). Formato não reconhecido volta como\r\n * veio — nunca \"Invalid Date\".\r\n */\r\nexport function validadeDoPix(valor: string): string {\r\n const texto = valor.trim();\r\n\r\n const semFuso = SEM_FUSO.exec(texto);\r\n if (semFuso) {\r\n const [, ano, mes, dia, hora, minuto] = semFuso;\r\n return dataValida(+ano, +mes, +dia, +hora, +minuto)\r\n ? `${dia}/${mes}/${ano} às ${hora}:${minuto} ${SUFIXO_DE_BRASILIA}`\r\n : valor;\r\n }\r\n\r\n if (COM_FUSO.test(texto)) {\r\n const instante = new Date(texto.replace(' ', 'T'));\r\n if (Number.isNaN(instante.getTime())) return valor;\r\n\r\n const partes = Object.fromEntries(\r\n new Intl.DateTimeFormat('pt-BR', {\r\n timeZone: FUSO_DE_BRASILIA,\r\n day: '2-digit',\r\n month: '2-digit',\r\n year: 'numeric',\r\n hour: '2-digit',\r\n minute: '2-digit',\r\n hourCycle: 'h23',\r\n })\r\n .formatToParts(instante)\r\n .map((parte) => [parte.type, parte.value]),\r\n );\r\n return `${partes['day']}/${partes['month']}/${partes['year']} às ${partes['hour']}:${partes['minute']} ${SUFIXO_DE_BRASILIA}`;\r\n }\r\n\r\n return valor;\r\n}\r\n\r\nfunction dataValida(ano: number, mes: number, dia: number, hora: number, minuto: number): boolean {\r\n if (mes < 1 || mes > 12 || hora > 23 || minuto > 59 || dia < 1) return false;\r\n return dia <= new Date(Date.UTC(ano, mes, 0)).getUTCDate();\r\n}\r\n\r\n// ── máscaras (perfil de cobrança) ────────────────────────────────────────────\r\n\r\nexport function soDigitos(valor: string): string {\r\n return valor.replace(/\\D/g, '');\r\n}\r\n\r\n/** Junta os grupos de dígitos com os separadores, parando no primeiro grupo vazio. */\r\nfunction agrupar(digitos: string, grupos: readonly number[], separadores: readonly string[]): string {\r\n let saida = '';\r\n let posicao = 0;\r\n for (let i = 0; i < grupos.length; i++) {\r\n const grupo = digitos.slice(posicao, posicao + grupos[i]);\r\n if (!grupo) break;\r\n saida += (i === 0 ? '' : separadores[i - 1]) + grupo;\r\n posicao += grupos[i];\r\n }\r\n return saida;\r\n}\r\n\r\n/** CPF `000.000.000-00`, progressivo enquanto se digita. */\r\nexport function mascararCpf(valor: string): string {\r\n return agrupar(soDigitos(valor).slice(0, 11), [3, 3, 3, 2], ['.', '.', '-']);\r\n}\r\n\r\n/** CNPJ `00.000.000/0000-00`, progressivo enquanto se digita. */\r\nexport function mascararCnpj(valor: string): string {\r\n return agrupar(soDigitos(valor).slice(0, 14), [2, 3, 3, 4, 2], ['.', '.', '/', '-']);\r\n}\r\n\r\n/** O documento conforme o tipo escolhido. */\r\nexport function mascararDocumento(tipo: 'Cpf' | 'Cnpj', valor: string): string {\r\n return tipo === 'Cpf' ? mascararCpf(valor) : mascararCnpj(valor);\r\n}\r\n\r\n/** CEP `00000-000`. */\r\nexport function mascararCep(valor: string): string {\r\n return agrupar(soDigitos(valor).slice(0, 8), [5, 3], ['-']);\r\n}\r\n\r\n/** Telefone `(00) 0000-0000` (10 dígitos) ou celular `(00) 00000-0000` (11). */\r\nexport function mascararTelefone(valor: string): string {\r\n const digitos = soDigitos(valor).slice(0, 11);\r\n if (!digitos) return '';\r\n\r\n const ddd = digitos.slice(0, 2);\r\n if (digitos.length <= 2) return `(${ddd}`;\r\n\r\n const numero = digitos.slice(2);\r\n const tamanhoDoPrimeiroBloco = digitos.length > 10 ? 5 : 4;\r\n const corpo =\r\n numero.length > tamanhoDoPrimeiroBloco\r\n ? `${numero.slice(0, tamanhoDoPrimeiroBloco)}-${numero.slice(tamanhoDoPrimeiroBloco)}`\r\n : numero;\r\n return `(${ddd}) ${corpo}`;\r\n}\r\n\r\nexport const STATUS_DA_FATURA: Record<InvoiceStatus, string> = {\r\n Created: 'Em emissão',\r\n Pending: 'Em aberto',\r\n Paid: 'Paga',\r\n Overdue: 'Vencida',\r\n Refunded: 'Estornada',\r\n Cancelled: 'Cancelada',\r\n};\r\n\r\nexport const ESTILO_BASE = `\r\n .orechio-billing {\r\n display: flex;\r\n flex-direction: column;\r\n gap: var(--orechio-space-3, 0.75rem);\r\n max-width: 40rem;\r\n margin: 0 auto;\r\n padding: var(--orechio-space-6, 1.5rem);\r\n color: var(--orechio-color-text, inherit);\r\n font-family: var(--orechio-font-body, inherit);\r\n }\r\n .orechio-billing form { display: flex; flex-direction: column; gap: var(--orechio-space-2, 0.5rem); }\r\n .orechio-billing__muted { color: var(--orechio-color-text-muted, #666); }\r\n .orechio-billing__error { color: var(--orechio-color-danger, #b00020); }\r\n .orechio-billing__acao {\r\n align-self: flex-start;\r\n display: inline-block;\r\n padding: var(--orechio-space-2, 0.5rem) var(--orechio-space-4, 1rem);\r\n border-radius: var(--orechio-radius, 0.375rem);\r\n background: var(--orechio-color-primary, #1a56db);\r\n color: var(--orechio-color-on-primary, #fff);\r\n font-weight: 600;\r\n text-decoration: none;\r\n }\r\n .orechio-billing__acao:hover,\r\n .orechio-billing__acao:focus-visible {\r\n background: var(--orechio-color-primary-hover, var(--orechio-color-primary, #1446b0));\r\n }\r\n`;\r\n"],"names":[],"mappings":"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"}
|
|
@@ -2,8 +2,8 @@ import * as i0 from '@angular/core';
|
|
|
2
2
|
import { inject, signal, ChangeDetectionStrategy, Component } from '@angular/core';
|
|
3
3
|
import { Router, ActivatedRoute, RouterLink } from '@angular/router';
|
|
4
4
|
import { forkJoin } from 'rxjs';
|
|
5
|
-
import { B as BillingClient, g as moeda, a as BillingErrorComponent } from './orechio-platform-angular-billing-billing-format-
|
|
6
|
-
import { P as PixPanelComponent } from './orechio-platform-angular-billing-pix-panel.component-
|
|
5
|
+
import { B as BillingClient, g as moeda, a as BillingErrorComponent } from './orechio-platform-angular-billing-billing-format-C8QPXulm.mjs';
|
|
6
|
+
import { P as PixPanelComponent } from './orechio-platform-angular-billing-pix-panel.component-DrfNi22S.mjs';
|
|
7
7
|
|
|
8
8
|
const STATUS_COM_CHECKOUT = ['Trial', 'PendingPayment', 'Grace', 'Suspended'];
|
|
9
9
|
const CICLOS = [
|
|
@@ -31,6 +31,10 @@ class CheckoutPage {
|
|
|
31
31
|
...(ngDevMode ? [{ debugName: "pix" }] : /* istanbul ignore next */ []));
|
|
32
32
|
invoiceUrl = signal(null, /* @ts-ignore */
|
|
33
33
|
...(ngDevMode ? [{ debugName: "invoiceUrl" }] : /* istanbul ignore next */ []));
|
|
34
|
+
valor = signal(null, /* @ts-ignore */
|
|
35
|
+
...(ngDevMode ? [{ debugName: "valor" }] : /* istanbul ignore next */ []));
|
|
36
|
+
vencimento = signal(null, /* @ts-ignore */
|
|
37
|
+
...(ngDevMode ? [{ debugName: "vencimento" }] : /* istanbul ignore next */ []));
|
|
34
38
|
erro = signal(null, /* @ts-ignore */
|
|
35
39
|
...(ngDevMode ? [{ debugName: "erro" }] : /* istanbul ignore next */ []));
|
|
36
40
|
rotulo(ciclo) {
|
|
@@ -70,6 +74,8 @@ class CheckoutPage {
|
|
|
70
74
|
next: (resultado) => {
|
|
71
75
|
this.enviando.set(false);
|
|
72
76
|
this.invoiceUrl.set(resultado.invoiceUrl ?? null);
|
|
77
|
+
this.valor.set(resultado.amount);
|
|
78
|
+
this.vencimento.set(resultado.dueDate);
|
|
73
79
|
if (resultado.pix)
|
|
74
80
|
this.pix.set(resultado.pix);
|
|
75
81
|
else
|
|
@@ -99,7 +105,7 @@ class CheckoutPage {
|
|
|
99
105
|
|
|
100
106
|
@if (pix(); as dados) {
|
|
101
107
|
<p>Pague o PIX abaixo. A assinatura é atualizada assim que o pagamento for confirmado.</p>
|
|
102
|
-
<orechio-billing-pix [pix]="dados" [invoiceUrl]="invoiceUrl()" />
|
|
108
|
+
<orechio-billing-pix [pix]="dados" [invoiceUrl]="invoiceUrl()" [valor]="valor()" [vencimento]="vencimento()" />
|
|
103
109
|
<a routerLink="..">Voltar para a assinatura</a>
|
|
104
110
|
} @else if (inelegivel()) {
|
|
105
111
|
<p>Não há pagamento de assinatura a fazer agora.</p>
|
|
@@ -134,7 +140,7 @@ class CheckoutPage {
|
|
|
134
140
|
<orechio-billing-error [problema]="problema" />
|
|
135
141
|
}
|
|
136
142
|
</section>
|
|
137
|
-
`, isInline: true, styles: [".orechio-billing{display:flex;flex-direction:column;gap:var(--orechio-space-3, .75rem);max-width:40rem;margin:0 auto;padding:var(--orechio-space-6, 1.5rem);color:var(--orechio-color-text, inherit);font-family:var(--orechio-font-body, inherit)}.orechio-billing form{display:flex;flex-direction:column;gap:var(--orechio-space-2, .5rem)}.orechio-billing__muted{color:var(--orechio-color-text-muted, #666)}.orechio-billing__error{color:var(--orechio-color-danger, #b00020)}.orechio-billing__acao{align-self:flex-start;display:inline-block;padding:var(--orechio-space-2, .5rem) var(--orechio-space-4, 1rem);border-radius:var(--orechio-radius, .375rem);background:var(--orechio-color-primary, #1a56db);color:var(--orechio-color-on-primary, #fff);font-weight:600;text-decoration:none}.orechio-billing__acao:hover,.orechio-billing__acao:focus-visible{background:var(--orechio-color-primary-hover, var(--orechio-color-primary, #1446b0))}\n"], dependencies: [{ kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "browserUrl", "routerLink"] }, { kind: "component", type: BillingErrorComponent, selector: "orechio-billing-error", inputs: ["problema"] }, { kind: "component", type: PixPanelComponent, selector: "orechio-billing-pix", inputs: ["pix", "invoiceUrl"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
143
|
+
`, isInline: true, styles: [".orechio-billing{display:flex;flex-direction:column;gap:var(--orechio-space-3, .75rem);max-width:40rem;margin:0 auto;padding:var(--orechio-space-6, 1.5rem);color:var(--orechio-color-text, inherit);font-family:var(--orechio-font-body, inherit)}.orechio-billing form{display:flex;flex-direction:column;gap:var(--orechio-space-2, .5rem)}.orechio-billing__muted{color:var(--orechio-color-text-muted, #666)}.orechio-billing__error{color:var(--orechio-color-danger, #b00020)}.orechio-billing__acao{align-self:flex-start;display:inline-block;padding:var(--orechio-space-2, .5rem) var(--orechio-space-4, 1rem);border-radius:var(--orechio-radius, .375rem);background:var(--orechio-color-primary, #1a56db);color:var(--orechio-color-on-primary, #fff);font-weight:600;text-decoration:none}.orechio-billing__acao:hover,.orechio-billing__acao:focus-visible{background:var(--orechio-color-primary-hover, var(--orechio-color-primary, #1446b0))}\n"], dependencies: [{ kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "browserUrl", "routerLink"] }, { kind: "component", type: BillingErrorComponent, selector: "orechio-billing-error", inputs: ["problema"] }, { kind: "component", type: PixPanelComponent, selector: "orechio-billing-pix", inputs: ["pix", "invoiceUrl", "valor", "vencimento"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
138
144
|
}
|
|
139
145
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: CheckoutPage, decorators: [{
|
|
140
146
|
type: Component,
|
|
@@ -144,7 +150,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImpor
|
|
|
144
150
|
|
|
145
151
|
@if (pix(); as dados) {
|
|
146
152
|
<p>Pague o PIX abaixo. A assinatura é atualizada assim que o pagamento for confirmado.</p>
|
|
147
|
-
<orechio-billing-pix [pix]="dados" [invoiceUrl]="invoiceUrl()" />
|
|
153
|
+
<orechio-billing-pix [pix]="dados" [invoiceUrl]="invoiceUrl()" [valor]="valor()" [vencimento]="vencimento()" />
|
|
148
154
|
<a routerLink="..">Voltar para a assinatura</a>
|
|
149
155
|
} @else if (inelegivel()) {
|
|
150
156
|
<p>Não há pagamento de assinatura a fazer agora.</p>
|
|
@@ -183,4 +189,4 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImpor
|
|
|
183
189
|
}] });
|
|
184
190
|
|
|
185
191
|
export { CheckoutPage };
|
|
186
|
-
//# sourceMappingURL=orechio-platform-angular-billing-checkout.page-
|
|
192
|
+
//# sourceMappingURL=orechio-platform-angular-billing-checkout.page-Dr6ZGDkd.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"orechio-platform-angular-billing-checkout.page-Dr6ZGDkd.mjs","sources":["../../../projects/platform-angular/billing/src/pages/checkout.page.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, OnInit, inject, signal } from '@angular/core';\r\nimport { ActivatedRoute, Router, RouterLink } from '@angular/router';\r\nimport { forkJoin } from 'rxjs';\r\nimport { ProblemDetails } from '@orechio/platform-angular';\r\nimport { BillingClient } from '../billing-client';\r\nimport { BillingErrorComponent } from '../billing-error.component';\r\nimport { ESTILO_BASE, moeda } from '../billing-format';\r\nimport { BillingCycle, PixData, PricingPreview, Subscription, SubscriptionStatus } from '../billing-models';\r\nimport { PixPanelComponent } from '../pix-panel.component';\r\n\r\nconst STATUS_COM_CHECKOUT: SubscriptionStatus[] = ['Trial', 'PendingPayment', 'Grace', 'Suspended'];\r\n\r\nconst CICLOS: { valor: BillingCycle; rotulo: string }[] = [\r\n { valor: 'Monthly', rotulo: 'Mensal' },\r\n { valor: 'Yearly', rotulo: 'Anual' },\r\n];\r\n\r\n/**\r\n * Pagar a assinatura (ADR-021 §3.5, item 3): o plano ATUAL — trocar de plano é v2 —, o preço\r\n * de cada ciclo vindo de `pricing/preview`, e o checkout PIX.\r\n */\r\n@Component({\r\n selector: 'orechio-billing-checkout',\r\n standalone: true,\r\n imports: [RouterLink, BillingErrorComponent, PixPanelComponent],\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n template: `\r\n <section class=\"orechio-billing\">\r\n <h1>Pagar assinatura</h1>\r\n\r\n @if (pix(); as dados) {\r\n <p>Pague o PIX abaixo. A assinatura é atualizada assim que o pagamento for confirmado.</p>\r\n <orechio-billing-pix [pix]=\"dados\" [invoiceUrl]=\"invoiceUrl()\" [valor]=\"valor()\" [vencimento]=\"vencimento()\" />\r\n <a routerLink=\"..\">Voltar para a assinatura</a>\r\n } @else if (inelegivel()) {\r\n <p>Não há pagamento de assinatura a fazer agora.</p>\r\n <a routerLink=\"../invoices\">Ver faturas</a>\r\n } @else if (previas().length) {\r\n <h2>{{ previas()[0].planName }}</h2>\r\n\r\n <fieldset>\r\n <legend>Ciclo de cobrança</legend>\r\n @for (previa of previas(); track previa.cycle) {\r\n <label>\r\n <input\r\n type=\"radio\"\r\n name=\"ciclo\"\r\n [value]=\"previa.cycle\"\r\n [checked]=\"ciclo() === previa.cycle\"\r\n (change)=\"ciclo.set(previa.cycle)\"\r\n />\r\n {{ rotulo(previa.cycle) }} — {{ moeda(previa.total) }}\r\n </label>\r\n }\r\n </fieldset>\r\n\r\n <button type=\"button\" data-gerar-pix (click)=\"pagar()\" [disabled]=\"enviando()\">\r\n {{ enviando() ? 'Gerando PIX…' : 'Gerar PIX' }}\r\n </button>\r\n } @else if (!erro()) {\r\n <p class=\"orechio-billing__muted\">Carregando…</p>\r\n }\r\n\r\n @if (erro(); as problema) {\r\n <orechio-billing-error [problema]=\"problema\" />\r\n }\r\n </section>\r\n `,\r\n styles: ESTILO_BASE,\r\n})\r\nexport class CheckoutPage implements OnInit {\r\n private readonly client = inject(BillingClient);\r\n private readonly router = inject(Router);\r\n private readonly route = inject(ActivatedRoute);\r\n\r\n protected readonly moeda = moeda;\r\n protected readonly previas = signal<PricingPreview[]>([]);\r\n protected readonly ciclo = signal<BillingCycle>('Monthly');\r\n protected readonly inelegivel = signal(false);\r\n protected readonly enviando = signal(false);\r\n protected readonly pix = signal<PixData | null>(null);\r\n protected readonly invoiceUrl = signal<string | null>(null);\r\n protected readonly valor = signal<number | null>(null);\r\n protected readonly vencimento = signal<string | null>(null);\r\n protected readonly erro = signal<ProblemDetails | null>(null);\r\n\r\n protected rotulo(ciclo: BillingCycle): string {\r\n return CICLOS.find((c) => c.valor === ciclo)?.rotulo ?? ciclo;\r\n }\r\n\r\n ngOnInit(): void {\r\n forkJoin({ assinatura: this.client.getSubscription(), perfil: this.client.getProfile() }).subscribe({\r\n next: ({ assinatura, perfil }) => {\r\n // O checkout serve Trial, PendingPayment, Grace e Suspended (`InitiateCheckoutCommand`,\r\n // passo 4; os dois últimos desde a BILLING-2b, e só sem fatura aberta — ver `pagar`).\r\n // Checado antes do perfil: não se pede cadastro para um pagamento que não existe.\r\n if (!STATUS_COM_CHECKOUT.includes(assinatura.status)) {\r\n this.inelegivel.set(true);\r\n return;\r\n }\r\n\r\n // O checkout recusa sem perfil (`billing_profile.not_found`). Mandar para o perfil\r\n // ANTES poupa o cliente de escolher o ciclo para receber um erro.\r\n if (!perfil.exists || !perfil.isComplete) {\r\n void this.router.navigate(['../profile'], {\r\n relativeTo: this.route,\r\n queryParams: { next: 'checkout' },\r\n });\r\n return;\r\n }\r\n\r\n this.ciclo.set(assinatura.cycle);\r\n this.carregarPrecos(assinatura);\r\n },\r\n error: (problema: ProblemDetails) => this.erro.set(problema),\r\n });\r\n }\r\n\r\n protected pagar(): void {\r\n if (this.enviando()) return;\r\n this.enviando.set(true);\r\n this.erro.set(null);\r\n\r\n this.client.checkout(this.ciclo()).subscribe({\r\n next: (resultado) => {\r\n this.enviando.set(false);\r\n this.invoiceUrl.set(resultado.invoiceUrl ?? null);\r\n this.valor.set(resultado.amount);\r\n this.vencimento.set(resultado.dueDate);\r\n if (resultado.pix) this.pix.set(resultado.pix);\r\n else this.erro.set({ code: 'checkout.pix_missing', status: 200 });\r\n },\r\n error: (problema: ProblemDetails) => {\r\n this.enviando.set(false);\r\n // Em Grace/Suspended com fatura aberta a Platform recusa: a aberta é a que se paga.\r\n if (problema.code === 'checkout.open_invoice_exists') {\r\n void this.router.navigate(['../invoices'], { relativeTo: this.route });\r\n return;\r\n }\r\n this.erro.set(problema);\r\n },\r\n });\r\n }\r\n\r\n private carregarPrecos(assinatura: Subscription): void {\r\n forkJoin(\r\n CICLOS.map(({ valor }) =>\r\n this.client.previewPricing(assinatura.planId, assinatura.branchCountSnapshot, valor),\r\n ),\r\n ).subscribe({\r\n next: (previas) => this.previas.set(previas),\r\n error: (problema: ProblemDetails) => this.erro.set(problema),\r\n });\r\n }\r\n}\r\n"],"names":[],"mappings":"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"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
2
|
import { inject, signal, computed, ChangeDetectionStrategy, Component } from '@angular/core';
|
|
3
3
|
import { ActivatedRoute, RouterLink } from '@angular/router';
|
|
4
|
-
import { B as BillingClient, S as STATUS_DA_FATURA, g as moeda, d as data, a as BillingErrorComponent } from './orechio-platform-angular-billing-billing-format-
|
|
5
|
-
import { P as PixPanelComponent } from './orechio-platform-angular-billing-pix-panel.component-
|
|
4
|
+
import { B as BillingClient, S as STATUS_DA_FATURA, g as moeda, d as data, h as dataDoVencimento, a as BillingErrorComponent } from './orechio-platform-angular-billing-billing-format-C8QPXulm.mjs';
|
|
5
|
+
import { P as PixPanelComponent } from './orechio-platform-angular-billing-pix-panel.component-DrfNi22S.mjs';
|
|
6
6
|
|
|
7
7
|
/**
|
|
8
8
|
* Detalhe de fatura. O detalhe da Platform não traz dado de pagamento (`InvoiceDetailDto`):
|
|
@@ -15,6 +15,7 @@ class InvoicePage {
|
|
|
15
15
|
status = STATUS_DA_FATURA;
|
|
16
16
|
moeda = moeda;
|
|
17
17
|
data = data;
|
|
18
|
+
dataDoVencimento = dataDoVencimento;
|
|
18
19
|
fatura = signal(null, /* @ts-ignore */
|
|
19
20
|
...(ngDevMode ? [{ debugName: "fatura" }] : /* istanbul ignore next */ []));
|
|
20
21
|
pix = signal(null, /* @ts-ignore */
|
|
@@ -61,7 +62,7 @@ class InvoicePage {
|
|
|
61
62
|
<section class="orechio-billing">
|
|
62
63
|
@if (fatura(); as f) {
|
|
63
64
|
<h1>Fatura {{ f.number }}</h1>
|
|
64
|
-
<p>{{ status[f.status] }} — {{ moeda(f.amount) }} — vence em {{
|
|
65
|
+
<p>{{ status[f.status] }} — {{ moeda(f.amount) }} — vence em {{ dataDoVencimento(f.dueDate) }}</p>
|
|
65
66
|
<p class="orechio-billing__muted">
|
|
66
67
|
{{ f.planNameAtTime }}, de {{ data(f.periodStartsAt) }} a {{ data(f.periodEndsAt) }}
|
|
67
68
|
</p>
|
|
@@ -79,7 +80,7 @@ class InvoicePage {
|
|
|
79
80
|
}
|
|
80
81
|
|
|
81
82
|
@if (pix(); as dados) {
|
|
82
|
-
<orechio-billing-pix [pix]="dados" [invoiceUrl]="invoiceUrl()" />
|
|
83
|
+
<orechio-billing-pix [pix]="dados" [invoiceUrl]="invoiceUrl()" [valor]="f.amount" [vencimento]="f.dueDate" />
|
|
83
84
|
}
|
|
84
85
|
} @else if (!erro()) {
|
|
85
86
|
<p class="orechio-billing__muted">Carregando…</p>
|
|
@@ -91,7 +92,7 @@ class InvoicePage {
|
|
|
91
92
|
|
|
92
93
|
<a routerLink="..">Voltar para as faturas</a>
|
|
93
94
|
</section>
|
|
94
|
-
`, isInline: true, styles: [".orechio-billing{display:flex;flex-direction:column;gap:var(--orechio-space-3, .75rem);max-width:40rem;margin:0 auto;padding:var(--orechio-space-6, 1.5rem);color:var(--orechio-color-text, inherit);font-family:var(--orechio-font-body, inherit)}.orechio-billing form{display:flex;flex-direction:column;gap:var(--orechio-space-2, .5rem)}.orechio-billing__muted{color:var(--orechio-color-text-muted, #666)}.orechio-billing__error{color:var(--orechio-color-danger, #b00020)}.orechio-billing__acao{align-self:flex-start;display:inline-block;padding:var(--orechio-space-2, .5rem) var(--orechio-space-4, 1rem);border-radius:var(--orechio-radius, .375rem);background:var(--orechio-color-primary, #1a56db);color:var(--orechio-color-on-primary, #fff);font-weight:600;text-decoration:none}.orechio-billing__acao:hover,.orechio-billing__acao:focus-visible{background:var(--orechio-color-primary-hover, var(--orechio-color-primary, #1446b0))}\n"], dependencies: [{ kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "browserUrl", "routerLink"] }, { kind: "component", type: BillingErrorComponent, selector: "orechio-billing-error", inputs: ["problema"] }, { kind: "component", type: PixPanelComponent, selector: "orechio-billing-pix", inputs: ["pix", "invoiceUrl"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
95
|
+
`, isInline: true, styles: [".orechio-billing{display:flex;flex-direction:column;gap:var(--orechio-space-3, .75rem);max-width:40rem;margin:0 auto;padding:var(--orechio-space-6, 1.5rem);color:var(--orechio-color-text, inherit);font-family:var(--orechio-font-body, inherit)}.orechio-billing form{display:flex;flex-direction:column;gap:var(--orechio-space-2, .5rem)}.orechio-billing__muted{color:var(--orechio-color-text-muted, #666)}.orechio-billing__error{color:var(--orechio-color-danger, #b00020)}.orechio-billing__acao{align-self:flex-start;display:inline-block;padding:var(--orechio-space-2, .5rem) var(--orechio-space-4, 1rem);border-radius:var(--orechio-radius, .375rem);background:var(--orechio-color-primary, #1a56db);color:var(--orechio-color-on-primary, #fff);font-weight:600;text-decoration:none}.orechio-billing__acao:hover,.orechio-billing__acao:focus-visible{background:var(--orechio-color-primary-hover, var(--orechio-color-primary, #1446b0))}\n"], dependencies: [{ kind: "directive", type: RouterLink, selector: "[routerLink]", inputs: ["target", "queryParams", "fragment", "queryParamsHandling", "state", "info", "relativeTo", "preserveFragment", "skipLocationChange", "replaceUrl", "browserUrl", "routerLink"] }, { kind: "component", type: BillingErrorComponent, selector: "orechio-billing-error", inputs: ["problema"] }, { kind: "component", type: PixPanelComponent, selector: "orechio-billing-pix", inputs: ["pix", "invoiceUrl", "valor", "vencimento"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
95
96
|
}
|
|
96
97
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: InvoicePage, decorators: [{
|
|
97
98
|
type: Component,
|
|
@@ -99,7 +100,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImpor
|
|
|
99
100
|
<section class="orechio-billing">
|
|
100
101
|
@if (fatura(); as f) {
|
|
101
102
|
<h1>Fatura {{ f.number }}</h1>
|
|
102
|
-
<p>{{ status[f.status] }} — {{ moeda(f.amount) }} — vence em {{
|
|
103
|
+
<p>{{ status[f.status] }} — {{ moeda(f.amount) }} — vence em {{ dataDoVencimento(f.dueDate) }}</p>
|
|
103
104
|
<p class="orechio-billing__muted">
|
|
104
105
|
{{ f.planNameAtTime }}, de {{ data(f.periodStartsAt) }} a {{ data(f.periodEndsAt) }}
|
|
105
106
|
</p>
|
|
@@ -117,7 +118,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImpor
|
|
|
117
118
|
}
|
|
118
119
|
|
|
119
120
|
@if (pix(); as dados) {
|
|
120
|
-
<orechio-billing-pix [pix]="dados" [invoiceUrl]="invoiceUrl()" />
|
|
121
|
+
<orechio-billing-pix [pix]="dados" [invoiceUrl]="invoiceUrl()" [valor]="f.amount" [vencimento]="f.dueDate" />
|
|
121
122
|
}
|
|
122
123
|
} @else if (!erro()) {
|
|
123
124
|
<p class="orechio-billing__muted">Carregando…</p>
|
|
@@ -133,4 +134,4 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImpor
|
|
|
133
134
|
}] });
|
|
134
135
|
|
|
135
136
|
export { InvoicePage };
|
|
136
|
-
//# sourceMappingURL=orechio-platform-angular-billing-invoice.page-
|
|
137
|
+
//# sourceMappingURL=orechio-platform-angular-billing-invoice.page-CWThsx-_.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"orechio-platform-angular-billing-invoice.page-CWThsx-_.mjs","sources":["../../../projects/platform-angular/billing/src/pages/invoice.page.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, OnInit, computed, inject, signal } from '@angular/core';\r\nimport { ActivatedRoute, RouterLink } from '@angular/router';\r\nimport { ProblemDetails } from '@orechio/platform-angular';\r\nimport { BillingClient } from '../billing-client';\r\nimport { BillingErrorComponent } from '../billing-error.component';\r\nimport { ESTILO_BASE, STATUS_DA_FATURA, data, dataDoVencimento, moeda } from '../billing-format';\r\nimport { InvoiceDetail, PixData } from '../billing-models';\r\nimport { PixPanelComponent } from '../pix-panel.component';\r\n\r\n/**\r\n * Detalhe de fatura. O detalhe da Platform não traz dado de pagamento (`InvoiceDetailDto`):\r\n * o QR vem do refresh, que a Platform aceita para fatura PIX `Pending` ou `Overdue`\r\n * (`RefreshPaymentDataCommand`, passo 2) — o que inclui a fatura de renovação.\r\n */\r\n@Component({\r\n selector: 'orechio-billing-invoice',\r\n standalone: true,\r\n imports: [RouterLink, BillingErrorComponent, PixPanelComponent],\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n template: `\r\n <section class=\"orechio-billing\">\r\n @if (fatura(); as f) {\r\n <h1>Fatura {{ f.number }}</h1>\r\n <p>{{ status[f.status] }} — {{ moeda(f.amount) }} — vence em {{ dataDoVencimento(f.dueDate) }}</p>\r\n <p class=\"orechio-billing__muted\">\r\n {{ f.planNameAtTime }}, de {{ data(f.periodStartsAt) }} a {{ data(f.periodEndsAt) }}\r\n </p>\r\n\r\n <ul>\r\n @for (item of f.items; track item.order) {\r\n <li>{{ item.description }} — {{ item.quantity }} × {{ moeda(item.unitAmount) }} = {{ moeda(item.totalAmount) }}</li>\r\n }\r\n </ul>\r\n\r\n @if (podeGerarQr()) {\r\n <button type=\"button\" data-gerar-qr (click)=\"gerarQr()\" [disabled]=\"gerando()\">\r\n {{ gerando() ? 'Gerando…' : 'Gerar novo QR' }}\r\n </button>\r\n }\r\n\r\n @if (pix(); as dados) {\r\n <orechio-billing-pix [pix]=\"dados\" [invoiceUrl]=\"invoiceUrl()\" [valor]=\"f.amount\" [vencimento]=\"f.dueDate\" />\r\n }\r\n } @else if (!erro()) {\r\n <p class=\"orechio-billing__muted\">Carregando…</p>\r\n }\r\n\r\n @if (erro(); as problema) {\r\n <orechio-billing-error [problema]=\"problema\" />\r\n }\r\n\r\n <a routerLink=\"..\">Voltar para as faturas</a>\r\n </section>\r\n `,\r\n styles: ESTILO_BASE,\r\n})\r\nexport class InvoicePage implements OnInit {\r\n private readonly client = inject(BillingClient);\r\n private readonly route = inject(ActivatedRoute);\r\n\r\n protected readonly status = STATUS_DA_FATURA;\r\n protected readonly moeda = moeda;\r\n protected readonly data = data;\r\n protected readonly dataDoVencimento = dataDoVencimento;\r\n protected readonly fatura = signal<InvoiceDetail | null>(null);\r\n protected readonly pix = signal<PixData | null>(null);\r\n protected readonly invoiceUrl = signal<string | null>(null);\r\n protected readonly gerando = signal(false);\r\n protected readonly erro = signal<ProblemDetails | null>(null);\r\n protected readonly podeGerarQr = computed(() => {\r\n const f = this.fatura();\r\n return !!f && f.paymentMethod === 'Pix' && (f.status === 'Pending' || f.status === 'Overdue');\r\n });\r\n\r\n ngOnInit(): void {\r\n const id = this.route.snapshot.paramMap.get('id') ?? '';\r\n this.client.getInvoice(id).subscribe({\r\n next: (fatura) => this.fatura.set(fatura),\r\n error: (problema: ProblemDetails) => this.erro.set(problema),\r\n });\r\n }\r\n\r\n protected gerarQr(): void {\r\n const f = this.fatura();\r\n if (!f || this.gerando()) return;\r\n\r\n this.gerando.set(true);\r\n this.erro.set(null);\r\n this.client.refreshInvoicePix(f.id).subscribe({\r\n next: (pix) => {\r\n this.gerando.set(false);\r\n // O refresh traz o PIX e o `invoiceUrl` juntos: o link acompanha o QR que está na tela.\r\n this.invoiceUrl.set(pix.invoiceUrl ?? null);\r\n this.pix.set(pix);\r\n },\r\n error: (problema: ProblemDetails) => {\r\n this.gerando.set(false);\r\n this.erro.set(problema);\r\n },\r\n });\r\n }\r\n}\r\n"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
2
|
import { inject, signal, computed, ChangeDetectionStrategy, Component } from '@angular/core';
|
|
3
3
|
import { RouterLink } from '@angular/router';
|
|
4
|
-
import { B as BillingClient, S as STATUS_DA_FATURA, g as moeda, d as data, a as BillingErrorComponent } from './orechio-platform-angular-billing-billing-format-
|
|
4
|
+
import { B as BillingClient, S as STATUS_DA_FATURA, g as moeda, d as data, h as dataDoVencimento, a as BillingErrorComponent } from './orechio-platform-angular-billing-billing-format-C8QPXulm.mjs';
|
|
5
5
|
|
|
6
6
|
const TAMANHO_DA_PAGINA = 20;
|
|
7
7
|
class InvoicesPage {
|
|
@@ -9,6 +9,7 @@ class InvoicesPage {
|
|
|
9
9
|
status = STATUS_DA_FATURA;
|
|
10
10
|
moeda = moeda;
|
|
11
11
|
data = data;
|
|
12
|
+
dataDoVencimento = dataDoVencimento;
|
|
12
13
|
pagina = signal(null, /* @ts-ignore */
|
|
13
14
|
...(ngDevMode ? [{ debugName: "pagina" }] : /* istanbul ignore next */ []));
|
|
14
15
|
erro = signal(null, /* @ts-ignore */
|
|
@@ -42,7 +43,7 @@ class InvoicesPage {
|
|
|
42
43
|
@for (fatura of p.invoices; track fatura.id) {
|
|
43
44
|
<li>
|
|
44
45
|
<a [routerLink]="fatura.id">Fatura {{ fatura.number }}</a>
|
|
45
|
-
— {{ status[fatura.status] }} — {{ moeda(fatura.amount) }} — vence em {{
|
|
46
|
+
— {{ status[fatura.status] }} — {{ moeda(fatura.amount) }} — vence em {{ dataDoVencimento(fatura.dueDate) }}
|
|
46
47
|
</li>
|
|
47
48
|
}
|
|
48
49
|
</ul>
|
|
@@ -79,7 +80,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImpor
|
|
|
79
80
|
@for (fatura of p.invoices; track fatura.id) {
|
|
80
81
|
<li>
|
|
81
82
|
<a [routerLink]="fatura.id">Fatura {{ fatura.number }}</a>
|
|
82
|
-
— {{ status[fatura.status] }} — {{ moeda(fatura.amount) }} — vence em {{
|
|
83
|
+
— {{ status[fatura.status] }} — {{ moeda(fatura.amount) }} — vence em {{ dataDoVencimento(fatura.dueDate) }}
|
|
83
84
|
</li>
|
|
84
85
|
}
|
|
85
86
|
</ul>
|
|
@@ -102,4 +103,4 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImpor
|
|
|
102
103
|
}] });
|
|
103
104
|
|
|
104
105
|
export { InvoicesPage };
|
|
105
|
-
//# sourceMappingURL=orechio-platform-angular-billing-invoices.page-
|
|
106
|
+
//# sourceMappingURL=orechio-platform-angular-billing-invoices.page-DytE1Lcz.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"orechio-platform-angular-billing-invoices.page-DytE1Lcz.mjs","sources":["../../../projects/platform-angular/billing/src/pages/invoices.page.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, OnInit, computed, inject, signal } from '@angular/core';\r\nimport { RouterLink } from '@angular/router';\r\nimport { ProblemDetails } from '@orechio/platform-angular';\r\nimport { BillingClient } from '../billing-client';\r\nimport { BillingErrorComponent } from '../billing-error.component';\r\nimport { ESTILO_BASE, STATUS_DA_FATURA, data, dataDoVencimento, moeda } from '../billing-format';\r\nimport { InvoicesPage as PaginaDeFaturas } from '../billing-models';\r\n\r\nconst TAMANHO_DA_PAGINA = 20;\r\n\r\n@Component({\r\n selector: 'orechio-billing-invoices',\r\n standalone: true,\r\n imports: [RouterLink, BillingErrorComponent],\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n template: `\r\n <section class=\"orechio-billing\">\r\n <h1>Faturas</h1>\r\n\r\n @if (erro(); as problema) {\r\n <orechio-billing-error [problema]=\"problema\" />\r\n } @else if (pagina(); as p) {\r\n @if (p.invoices.length === 0) {\r\n <p class=\"orechio-billing__muted\">Nenhuma fatura ainda.</p>\r\n } @else {\r\n <ul>\r\n @for (fatura of p.invoices; track fatura.id) {\r\n <li>\r\n <a [routerLink]=\"fatura.id\">Fatura {{ fatura.number }}</a>\r\n — {{ status[fatura.status] }} — {{ moeda(fatura.amount) }} — vence em {{ dataDoVencimento(fatura.dueDate) }}\r\n </li>\r\n }\r\n </ul>\r\n\r\n <nav>\r\n @if (p.page > 1) {\r\n <button type=\"button\" (click)=\"carregar(p.page - 1)\">Anteriores</button>\r\n }\r\n @if (temProxima()) {\r\n <button type=\"button\" (click)=\"carregar(p.page + 1)\">Próximas</button>\r\n }\r\n </nav>\r\n }\r\n <a routerLink=\"..\">Voltar para a assinatura</a>\r\n } @else {\r\n <p class=\"orechio-billing__muted\">Carregando…</p>\r\n }\r\n </section>\r\n `,\r\n styles: ESTILO_BASE,\r\n})\r\nexport class InvoicesPage implements OnInit {\r\n private readonly client = inject(BillingClient);\r\n\r\n protected readonly status = STATUS_DA_FATURA;\r\n protected readonly moeda = moeda;\r\n protected readonly data = data;\r\n protected readonly dataDoVencimento = dataDoVencimento;\r\n protected readonly pagina = signal<PaginaDeFaturas | null>(null);\r\n protected readonly erro = signal<ProblemDetails | null>(null);\r\n protected readonly temProxima = computed(() => {\r\n const p = this.pagina();\r\n return !!p && p.page * p.pageSize < p.total;\r\n });\r\n\r\n ngOnInit(): void {\r\n this.carregar(1);\r\n }\r\n\r\n protected carregar(pagina: number): void {\r\n this.client.listInvoices(pagina, TAMANHO_DA_PAGINA).subscribe({\r\n next: (resultado) => this.pagina.set(resultado),\r\n error: (problema: ProblemDetails) => this.erro.set(problema),\r\n });\r\n }\r\n}\r\n"],"names":[],"mappings":";;;;;AAQA,MAAM,iBAAiB,GAAG,EAAE;MA2Cf,YAAY,CAAA;AACN,IAAA,MAAM,GAAG,MAAM,CAAC,aAAa,CAAC;IAE5B,MAAM,GAAG,gBAAgB;IACzB,KAAK,GAAG,KAAK;IACb,IAAI,GAAG,IAAI;IACX,gBAAgB,GAAG,gBAAgB;IACnC,MAAM,GAAG,MAAM,CAAyB,IAAI;+EAAC;IAC7C,IAAI,GAAG,MAAM,CAAwB,IAAI;6EAAC;AAC1C,IAAA,UAAU,GAAG,QAAQ,CAAC,MAAK;AAC5C,QAAA,MAAM,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE;AACvB,QAAA,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,KAAK;IAC7C,CAAC;mFAAC;IAEF,QAAQ,GAAA;AACN,QAAA,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;IAClB;AAEU,IAAA,QAAQ,CAAC,MAAc,EAAA;QAC/B,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC,SAAS,CAAC;AAC5D,YAAA,IAAI,EAAE,CAAC,SAAS,KAAK,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC;AAC/C,YAAA,KAAK,EAAE,CAAC,QAAwB,KAAK,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC;AAC7D,SAAA,CAAC;IACJ;uGAvBW,YAAY,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAZ,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,YAAY,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,0BAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EApCb,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCT,EAAA,QAAA,EAAA,IAAA,EAAA,MAAA,EAAA,CAAA,o6BAAA,CAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAnCS,UAAU,kPAAE,qBAAqB,EAAA,QAAA,EAAA,uBAAA,EAAA,MAAA,EAAA,CAAA,UAAA,CAAA,EAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;2FAsChC,YAAY,EAAA,UAAA,EAAA,CAAA;kBAzCxB,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,0BAA0B,EAAA,UAAA,EACxB,IAAI,EAAA,OAAA,EACP,CAAC,UAAU,EAAE,qBAAqB,CAAC,EAAA,eAAA,EAC3B,uBAAuB,CAAC,MAAM,EAAA,QAAA,EACrC,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiCT,EAAA,CAAA,EAAA,MAAA,EAAA,CAAA,o6BAAA,CAAA,EAAA;;;;;"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
2
|
import { input, signal, ChangeDetectionStrategy, Component } from '@angular/core';
|
|
3
|
-
import { v as validadeDoPix } from './orechio-platform-angular-billing-billing-format-
|
|
3
|
+
import { g as moeda, h as dataDoVencimento, v as validadeDoPix } from './orechio-platform-angular-billing-billing-format-C8QPXulm.mjs';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* O QR e o copia-e-cola, como a Platform os devolveu. O `encodedImage` é PNG em base64
|
|
@@ -12,6 +12,14 @@ class PixPanelComponent {
|
|
|
12
12
|
/** Página de pagamento do ASAAS, aberta em nova aba; sem ela, o link não aparece. */
|
|
13
13
|
invoiceUrl = input(null, /* @ts-ignore */
|
|
14
14
|
...(ngDevMode ? [{ debugName: "invoiceUrl" }] : /* istanbul ignore next */ []));
|
|
15
|
+
/** Valor da fatura; sem ele, a linha "Valor" não aparece. */
|
|
16
|
+
valor = input(null, /* @ts-ignore */
|
|
17
|
+
...(ngDevMode ? [{ debugName: "valor" }] : /* istanbul ignore next */ []));
|
|
18
|
+
/** Vencimento da fatura (data sem hora); sem ele, a linha "Vencimento" não aparece. */
|
|
19
|
+
vencimento = input(null, /* @ts-ignore */
|
|
20
|
+
...(ngDevMode ? [{ debugName: "vencimento" }] : /* istanbul ignore next */ []));
|
|
21
|
+
moeda = moeda;
|
|
22
|
+
dataDoVencimento = dataDoVencimento;
|
|
15
23
|
validadeDoPix = validadeDoPix;
|
|
16
24
|
copiado = signal(false, /* @ts-ignore */
|
|
17
25
|
...(ngDevMode ? [{ debugName: "copiado" }] : /* istanbul ignore next */ []));
|
|
@@ -26,8 +34,14 @@ class PixPanelComponent {
|
|
|
26
34
|
areaDeTransferencia.writeText(this.pix().payload).then(() => this.copiado.set(true), () => this.semAreaDeTransferencia.set(true));
|
|
27
35
|
}
|
|
28
36
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: PixPanelComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
29
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.2", type: PixPanelComponent, isStandalone: true, selector: "orechio-billing-pix", inputs: { pix: { classPropertyName: "pix", publicName: "pix", isSignal: true, isRequired: true, transformFunction: null }, invoiceUrl: { classPropertyName: "invoiceUrl", publicName: "invoiceUrl", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
|
|
37
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.2", type: PixPanelComponent, isStandalone: true, selector: "orechio-billing-pix", inputs: { pix: { classPropertyName: "pix", publicName: "pix", isSignal: true, isRequired: true, transformFunction: null }, invoiceUrl: { classPropertyName: "invoiceUrl", publicName: "invoiceUrl", isSignal: true, isRequired: false, transformFunction: null }, valor: { classPropertyName: "valor", publicName: "valor", isSignal: true, isRequired: false, transformFunction: null }, vencimento: { classPropertyName: "vencimento", publicName: "vencimento", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
|
|
30
38
|
<div class="orechio-billing-pix">
|
|
39
|
+
@if (valor() !== null && valor() !== undefined) {
|
|
40
|
+
<p data-valor>Valor: {{ moeda(valor()!) }}</p>
|
|
41
|
+
}
|
|
42
|
+
@if (vencimento(); as data) {
|
|
43
|
+
<p data-vencimento>Vencimento: {{ dataDoVencimento(data) }}</p>
|
|
44
|
+
}
|
|
31
45
|
<img data-qr [src]="'data:image/png;base64,' + pix().encodedImage" alt="QR Code PIX" width="240" height="240" />
|
|
32
46
|
|
|
33
47
|
<p>Ou copie o código PIX e cole no app do seu banco:</p>
|
|
@@ -40,7 +54,7 @@ class PixPanelComponent {
|
|
|
40
54
|
}
|
|
41
55
|
|
|
42
56
|
@if (pix().expirationDate; as validade) {
|
|
43
|
-
<p class="orechio-billing-pix__aviso">
|
|
57
|
+
<p class="orechio-billing-pix__aviso">QR válido até {{ validadeDoPix(validade) }}.</p>
|
|
44
58
|
}
|
|
45
59
|
|
|
46
60
|
@if (invoiceUrl(); as url) {
|
|
@@ -55,6 +69,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImpor
|
|
|
55
69
|
type: Component,
|
|
56
70
|
args: [{ selector: 'orechio-billing-pix', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
57
71
|
<div class="orechio-billing-pix">
|
|
72
|
+
@if (valor() !== null && valor() !== undefined) {
|
|
73
|
+
<p data-valor>Valor: {{ moeda(valor()!) }}</p>
|
|
74
|
+
}
|
|
75
|
+
@if (vencimento(); as data) {
|
|
76
|
+
<p data-vencimento>Vencimento: {{ dataDoVencimento(data) }}</p>
|
|
77
|
+
}
|
|
58
78
|
<img data-qr [src]="'data:image/png;base64,' + pix().encodedImage" alt="QR Code PIX" width="240" height="240" />
|
|
59
79
|
|
|
60
80
|
<p>Ou copie o código PIX e cole no app do seu banco:</p>
|
|
@@ -67,7 +87,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImpor
|
|
|
67
87
|
}
|
|
68
88
|
|
|
69
89
|
@if (pix().expirationDate; as validade) {
|
|
70
|
-
<p class="orechio-billing-pix__aviso">
|
|
90
|
+
<p class="orechio-billing-pix__aviso">QR válido até {{ validadeDoPix(validade) }}.</p>
|
|
71
91
|
}
|
|
72
92
|
|
|
73
93
|
@if (invoiceUrl(); as url) {
|
|
@@ -77,7 +97,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImpor
|
|
|
77
97
|
}
|
|
78
98
|
</div>
|
|
79
99
|
`, styles: [".orechio-billing-pix{display:flex;flex-direction:column;align-items:flex-start;gap:var(--orechio-space-2, .5rem)}.orechio-billing-pix__codigo{word-break:break-all}.orechio-billing-pix__aviso{color:var(--orechio-color-text-muted, #666)}\n"] }]
|
|
80
|
-
}], propDecorators: { pix: [{ type: i0.Input, args: [{ isSignal: true, alias: "pix", required: true }] }], invoiceUrl: [{ type: i0.Input, args: [{ isSignal: true, alias: "invoiceUrl", required: false }] }] } });
|
|
100
|
+
}], propDecorators: { pix: [{ type: i0.Input, args: [{ isSignal: true, alias: "pix", required: true }] }], invoiceUrl: [{ type: i0.Input, args: [{ isSignal: true, alias: "invoiceUrl", required: false }] }], valor: [{ type: i0.Input, args: [{ isSignal: true, alias: "valor", required: false }] }], vencimento: [{ type: i0.Input, args: [{ isSignal: true, alias: "vencimento", required: false }] }] } });
|
|
81
101
|
|
|
82
102
|
export { PixPanelComponent as P };
|
|
83
|
-
//# sourceMappingURL=orechio-platform-angular-billing-pix-panel.component-
|
|
103
|
+
//# sourceMappingURL=orechio-platform-angular-billing-pix-panel.component-DrfNi22S.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"orechio-platform-angular-billing-pix-panel.component-DrfNi22S.mjs","sources":["../../../projects/platform-angular/billing/src/pix-panel.component.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, input, signal } from '@angular/core';\r\nimport { dataDoVencimento, moeda, validadeDoPix } from './billing-format';\r\nimport { PixData } from './billing-models';\r\n\r\n/**\r\n * O QR e o copia-e-cola, como a Platform os devolveu. O `encodedImage` é PNG em base64\r\n * (ASAAS); o `payload` é o código que o app do banco aceita colado.\r\n */\r\n@Component({\r\n selector: 'orechio-billing-pix',\r\n standalone: true,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n template: `\r\n <div class=\"orechio-billing-pix\">\r\n @if (valor() !== null && valor() !== undefined) {\r\n <p data-valor>Valor: {{ moeda(valor()!) }}</p>\r\n }\r\n @if (vencimento(); as data) {\r\n <p data-vencimento>Vencimento: {{ dataDoVencimento(data) }}</p>\r\n }\r\n <img data-qr [src]=\"'data:image/png;base64,' + pix().encodedImage\" alt=\"QR Code PIX\" width=\"240\" height=\"240\" />\r\n\r\n <p>Ou copie o código PIX e cole no app do seu banco:</p>\r\n <code class=\"orechio-billing-pix__codigo\">{{ pix().payload }}</code>\r\n <button type=\"button\" data-copiar (click)=\"copiar()\">\r\n {{ copiado() ? 'Código copiado' : 'Copiar código' }}\r\n </button>\r\n @if (semAreaDeTransferencia()) {\r\n <p class=\"orechio-billing-pix__aviso\">Selecione o código acima e copie manualmente.</p>\r\n }\r\n\r\n @if (pix().expirationDate; as validade) {\r\n <p class=\"orechio-billing-pix__aviso\">QR válido até {{ validadeDoPix(validade) }}.</p>\r\n }\r\n\r\n @if (invoiceUrl(); as url) {\r\n <a data-invoice-url [href]=\"url\" target=\"_blank\" rel=\"noopener noreferrer\">\r\n Pagar pelo site do ASAAS\r\n </a>\r\n }\r\n </div>\r\n `,\r\n styles: `\r\n .orechio-billing-pix { display: flex; flex-direction: column; align-items: flex-start; gap: var(--orechio-space-2, 0.5rem); }\r\n .orechio-billing-pix__codigo { word-break: break-all; }\r\n .orechio-billing-pix__aviso { color: var(--orechio-color-text-muted, #666); }\r\n `,\r\n})\r\nexport class PixPanelComponent {\r\n readonly pix = input.required<PixData>();\r\n /** Página de pagamento do ASAAS, aberta em nova aba; sem ela, o link não aparece. */\r\n readonly invoiceUrl = input<string | null | undefined>(null);\r\n\r\n /** Valor da fatura; sem ele, a linha \"Valor\" não aparece. */\r\n readonly valor = input<number | null | undefined>(null);\r\n /** Vencimento da fatura (data sem hora); sem ele, a linha \"Vencimento\" não aparece. */\r\n readonly vencimento = input<string | null | undefined>(null);\r\n\r\n protected readonly moeda = moeda;\r\n protected readonly dataDoVencimento = dataDoVencimento;\r\n protected readonly validadeDoPix = validadeDoPix;\r\n protected readonly copiado = signal(false);\r\n protected readonly semAreaDeTransferencia = signal(false);\r\n\r\n protected copiar(): void {\r\n const areaDeTransferencia = globalThis.navigator?.clipboard;\r\n if (!areaDeTransferencia) {\r\n this.semAreaDeTransferencia.set(true);\r\n return;\r\n }\r\n\r\n areaDeTransferencia.writeText(this.pix().payload).then(\r\n () => this.copiado.set(true),\r\n () => this.semAreaDeTransferencia.set(true),\r\n );\r\n }\r\n}\r\n"],"names":[],"mappings":";;;;AAIA;;;AAGG;MAyCU,iBAAiB,CAAA;IACnB,GAAG,GAAG,KAAK,CAAC,QAAQ;4EAAW;;IAE/B,UAAU,GAAG,KAAK,CAA4B,IAAI;mFAAC;;IAGnD,KAAK,GAAG,KAAK,CAA4B,IAAI;8EAAC;;IAE9C,UAAU,GAAG,KAAK,CAA4B,IAAI;mFAAC;IAEzC,KAAK,GAAG,KAAK;IACb,gBAAgB,GAAG,gBAAgB;IACnC,aAAa,GAAG,aAAa;IAC7B,OAAO,GAAG,MAAM,CAAC,KAAK;gFAAC;IACvB,sBAAsB,GAAG,MAAM,CAAC,KAAK;+FAAC;IAE/C,MAAM,GAAA;AACd,QAAA,MAAM,mBAAmB,GAAG,UAAU,CAAC,SAAS,EAAE,SAAS;QAC3D,IAAI,CAAC,mBAAmB,EAAE;AACxB,YAAA,IAAI,CAAC,sBAAsB,CAAC,GAAG,CAAC,IAAI,CAAC;YACrC;QACF;AAEA,QAAA,mBAAmB,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,CAAC,IAAI,CACpD,MAAM,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,EAC5B,MAAM,IAAI,CAAC,sBAAsB,CAAC,GAAG,CAAC,IAAI,CAAC,CAC5C;IACH;uGA3BW,iBAAiB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAjB,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,iBAAiB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,qBAAA,EAAA,MAAA,EAAA,EAAA,GAAA,EAAA,EAAA,iBAAA,EAAA,KAAA,EAAA,UAAA,EAAA,KAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,UAAA,EAAA,EAAA,iBAAA,EAAA,YAAA,EAAA,UAAA,EAAA,YAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,KAAA,EAAA,EAAA,iBAAA,EAAA,OAAA,EAAA,UAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,UAAA,EAAA,EAAA,iBAAA,EAAA,YAAA,EAAA,UAAA,EAAA,YAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EApClB,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BT,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,MAAA,EAAA,CAAA,+OAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;2FAOU,iBAAiB,EAAA,UAAA,EAAA,CAAA;kBAxC7B,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,qBAAqB,cACnB,IAAI,EAAA,eAAA,EACC,uBAAuB,CAAC,MAAM,EAAA,QAAA,EACrC,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BT,EAAA,CAAA,EAAA,MAAA,EAAA,CAAA,+OAAA,CAAA,EAAA;;;;;"}
|
|
@@ -4,7 +4,7 @@ import { NgTemplateOutlet } from '@angular/common';
|
|
|
4
4
|
import * as i1 from '@angular/forms';
|
|
5
5
|
import { FormsModule } from '@angular/forms';
|
|
6
6
|
import { Router, ActivatedRoute, RouterLink } from '@angular/router';
|
|
7
|
-
import { m as mascararCep, b as mascararTelefone, c as mascararDocumento, B as BillingClient, e as mascararCpf, f as mascararCnpj, s as soDigitos, a as BillingErrorComponent } from './orechio-platform-angular-billing-billing-format-
|
|
7
|
+
import { m as mascararCep, b as mascararTelefone, c as mascararDocumento, B as BillingClient, e as mascararCpf, f as mascararCnpj, s as soDigitos, a as BillingErrorComponent } from './orechio-platform-angular-billing-billing-format-C8QPXulm.mjs';
|
|
8
8
|
|
|
9
9
|
function doPerfil(perfil) {
|
|
10
10
|
const tipo = perfil.documentType ?? 'Cnpj';
|
|
@@ -306,4 +306,4 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImpor
|
|
|
306
306
|
}] });
|
|
307
307
|
|
|
308
308
|
export { ProfilePage };
|
|
309
|
-
//# sourceMappingURL=orechio-platform-angular-billing-profile.page-
|
|
309
|
+
//# sourceMappingURL=orechio-platform-angular-billing-profile.page-DRkoYOZa.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"orechio-platform-angular-billing-profile.page-DHjsB3jJ.mjs","sources":["../../../projects/platform-angular/billing/src/pages/profile.page.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, OnInit, inject, signal } from '@angular/core';\r\nimport { NgTemplateOutlet } from '@angular/common';\r\nimport { FormsModule } from '@angular/forms';\r\nimport { ActivatedRoute, Router, RouterLink } from '@angular/router';\r\nimport { Observable } from 'rxjs';\r\nimport { ProblemDetails } from '@orechio/platform-angular';\r\nimport { BillingClient } from '../billing-client';\r\nimport { BillingErrorComponent } from '../billing-error.component';\r\nimport {\r\n ESTILO_BASE,\r\n mascararCep,\r\n mascararCpf,\r\n mascararCnpj,\r\n mascararDocumento,\r\n mascararTelefone,\r\n soDigitos,\r\n} from '../billing-format';\r\nimport {\r\n BillingAddressRequest,\r\n BillingContactRequest,\r\n BillingProfile,\r\n DocumentType,\r\n} from '../billing-models';\r\n\r\ninterface Formulario {\r\n documentType: DocumentType;\r\n documentNumber: string;\r\n legalName: string;\r\n tradeName: string;\r\n billingEmail: string;\r\n phone: string;\r\n mobilePhone: string;\r\n postalCode: string;\r\n street: string;\r\n number: string;\r\n complement: string;\r\n neighborhood: string;\r\n city: string;\r\n state: string;\r\n}\r\n\r\n/** Os campos que levam máscara, e qual máscara cada um leva (o documento depende do tipo). */\r\ntype CampoMascarado = 'documentNumber' | 'phone' | 'mobilePhone' | 'postalCode';\r\n\r\nfunction doPerfil(perfil: BillingProfile): Formulario {\r\n const tipo = perfil.documentType ?? 'Cnpj';\r\n return {\r\n documentType: tipo,\r\n documentNumber: mascararDocumento(tipo, perfil.documentNumber ?? ''),\r\n legalName: perfil.legalName ?? '',\r\n tradeName: perfil.tradeName ?? '',\r\n billingEmail: perfil.billingEmail ?? '',\r\n phone: mascararTelefone(perfil.phone ?? ''),\r\n mobilePhone: mascararTelefone(perfil.mobilePhone ?? ''),\r\n postalCode: mascararCep(perfil.postalCode ?? ''),\r\n street: perfil.street ?? '',\r\n number: perfil.number ?? '',\r\n complement: perfil.complement ?? '',\r\n neighborhood: perfil.neighborhood ?? '',\r\n city: perfil.city ?? '',\r\n state: perfil.state ?? '',\r\n };\r\n}\r\n\r\n/** Campo opcional vazio vai como `null`, que é o que a Platform espera nos `string?`. */\r\nfunction opcional(valor: string): string | null {\r\n const limpo = valor.trim();\r\n return limpo ? limpo : null;\r\n}\r\n\r\n/**\r\n * Perfil de cobrança (ADR-021 §3.5, item 2). Cria com `POST profile`; edita por partes, como a\r\n * Platform: `PATCH profile/contact` e `PATCH profile/address` — não existe PUT.\r\n */\r\n@Component({\r\n selector: 'orechio-billing-profile',\r\n standalone: true,\r\n imports: [FormsModule, NgTemplateOutlet, RouterLink, BillingErrorComponent],\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n template: `\r\n <section class=\"orechio-billing\">\r\n <h1>Dados de cobrança</h1>\r\n\r\n @if (perfil(); as p) {\r\n @if (!p.exists) {\r\n <p class=\"orechio-billing__muted\">\r\n Informe os dados que vão na cobrança. O CPF ou CNPJ é exigido pelo emissor do PIX.\r\n </p>\r\n <form data-criar (ngSubmit)=\"criar()\">\r\n <ng-container *ngTemplateOutlet=\"contato\" />\r\n <ng-container *ngTemplateOutlet=\"endereco\" />\r\n <button type=\"submit\" [disabled]=\"enviando()\">{{ enviando() ? 'Salvando…' : 'Salvar' }}</button>\r\n </form>\r\n } @else {\r\n <form data-contato (ngSubmit)=\"salvarContato()\">\r\n <h2>Contato</h2>\r\n <ng-container *ngTemplateOutlet=\"contato\" />\r\n <button type=\"submit\" [disabled]=\"enviando()\">Salvar contato</button>\r\n </form>\r\n\r\n <form data-endereco (ngSubmit)=\"salvarEndereco()\">\r\n <h2>Endereço</h2>\r\n <ng-container *ngTemplateOutlet=\"endereco\" />\r\n <button type=\"submit\" [disabled]=\"enviando()\">Salvar endereço</button>\r\n </form>\r\n }\r\n\r\n @if (salvo()) {\r\n <p role=\"status\">Dados salvos.</p>\r\n }\r\n <a routerLink=\"..\">Voltar para a assinatura</a>\r\n } @else if (!erro()) {\r\n <p class=\"orechio-billing__muted\">Carregando…</p>\r\n }\r\n\r\n @if (erro(); as problema) {\r\n <orechio-billing-error [problema]=\"problema\" />\r\n }\r\n </section>\r\n\r\n <ng-template #contato>\r\n <label>Tipo de documento\r\n <select name=\"documentType\" [ngModel]=\"form.documentType\" (ngModelChange)=\"trocarTipo($event)\" [disabled]=\"travado()\">\r\n <option value=\"Cpf\">CPF</option>\r\n <option value=\"Cnpj\">CNPJ</option>\r\n </select>\r\n </label>\r\n <label>{{ form.documentType === 'Cpf' ? 'CPF' : 'CNPJ' }}\r\n <input name=\"documentNumber\" inputmode=\"numeric\" [value]=\"form.documentNumber\" (input)=\"mascarar('documentNumber', $event)\" [disabled]=\"travado()\" required />\r\n </label>\r\n <label>{{ form.documentType === 'Cpf' ? 'Nome completo' : 'Razão social' }}\r\n <input name=\"legalName\" [(ngModel)]=\"form.legalName\" [disabled]=\"travado()\" required />\r\n </label>\r\n <label>Nome fantasia (opcional)\r\n <input name=\"tradeName\" [(ngModel)]=\"form.tradeName\" />\r\n </label>\r\n <label>E-mail de cobrança\r\n <input name=\"billingEmail\" type=\"email\" autocomplete=\"email\" [(ngModel)]=\"form.billingEmail\" required />\r\n </label>\r\n <label>Telefone (opcional)\r\n <input name=\"phone\" type=\"tel\" [value]=\"form.phone\" (input)=\"mascarar('phone', $event)\" />\r\n </label>\r\n <label>Celular (opcional)\r\n <input name=\"mobilePhone\" type=\"tel\" [value]=\"form.mobilePhone\" (input)=\"mascarar('mobilePhone', $event)\" />\r\n </label>\r\n </ng-template>\r\n\r\n <ng-template #endereco>\r\n <label>CEP <input name=\"postalCode\" inputmode=\"numeric\" autocomplete=\"postal-code\" [value]=\"form.postalCode\" (input)=\"mascarar('postalCode', $event)\" required /></label>\r\n <label>Rua <input name=\"street\" autocomplete=\"address-line1\" [(ngModel)]=\"form.street\" required /></label>\r\n <label>Número <input name=\"number\" [(ngModel)]=\"form.number\" required /></label>\r\n <label>Complemento (opcional) <input name=\"complement\" autocomplete=\"address-line2\" [(ngModel)]=\"form.complement\" /></label>\r\n <label>Bairro <input name=\"neighborhood\" [(ngModel)]=\"form.neighborhood\" required /></label>\r\n <label>Cidade <input name=\"city\" autocomplete=\"address-level2\" [(ngModel)]=\"form.city\" required /></label>\r\n <label>UF <input name=\"state\" maxlength=\"2\" autocomplete=\"address-level1\" [(ngModel)]=\"form.state\" required /></label>\r\n </ng-template>\r\n `,\r\n styles: ESTILO_BASE,\r\n})\r\nexport class ProfilePage implements OnInit {\r\n private readonly client = inject(BillingClient);\r\n private readonly router = inject(Router);\r\n private readonly route = inject(ActivatedRoute);\r\n\r\n protected form: Formulario = doPerfil({} as BillingProfile);\r\n protected readonly perfil = signal<BillingProfile | null>(null);\r\n protected readonly travado = signal(false);\r\n protected readonly enviando = signal(false);\r\n protected readonly salvo = signal(false);\r\n protected readonly erro = signal<ProblemDetails | null>(null);\r\n\r\n ngOnInit(): void {\r\n this.carregar();\r\n }\r\n\r\n /**\r\n * Máscara ao digitar: o campo é reescrito pela máscara e o formulário guarda o texto\r\n * mascarado. Sem `ngModel` de propósito — com ele, digitar um caractere que a máscara\r\n * descarta deixaria o valor do modelo igual ao anterior, e o campo continuaria mostrando\r\n * o caractere digitado.\r\n */\r\n protected mascarar(campo: CampoMascarado, evento: Event): void {\r\n const entrada = evento.target as HTMLInputElement;\r\n const mascarado = this.mascara(campo)(entrada.value);\r\n entrada.value = mascarado;\r\n this.form[campo] = mascarado;\r\n }\r\n\r\n protected trocarTipo(tipo: DocumentType): void {\r\n this.form.documentType = tipo;\r\n this.form.documentNumber = mascararDocumento(tipo, this.form.documentNumber);\r\n }\r\n\r\n private mascara(campo: CampoMascarado): (valor: string) => string {\r\n switch (campo) {\r\n case 'documentNumber':\r\n return this.form.documentType === 'Cpf' ? mascararCpf : mascararCnpj;\r\n case 'postalCode':\r\n return mascararCep;\r\n default:\r\n return mascararTelefone;\r\n }\r\n }\r\n\r\n protected criar(): void {\r\n this.enviar(this.client.createProfile({ ...this.contato(), ...this.endereco() }), () => {\r\n // Quem veio do pagamento volta para ele: foi o checkout que mandou para cá.\r\n if (this.route.snapshot.queryParamMap.get('next') === 'checkout') {\r\n void this.router.navigate(['../checkout'], { relativeTo: this.route });\r\n return;\r\n }\r\n this.carregar();\r\n });\r\n }\r\n\r\n protected salvarContato(): void {\r\n this.enviar(this.client.updateProfileContact(this.contato()), () => this.salvo.set(true));\r\n }\r\n\r\n protected salvarEndereco(): void {\r\n this.enviar(this.client.updateProfileAddress(this.endereco()), () => this.salvo.set(true));\r\n }\r\n\r\n private carregar(): void {\r\n this.client.getProfile().subscribe({\r\n next: (perfil) => {\r\n this.form = doPerfil(perfil);\r\n this.travado.set(perfil.isDocumentLocked === true);\r\n this.perfil.set(perfil);\r\n },\r\n error: (problema: ProblemDetails) => this.erro.set(problema),\r\n });\r\n }\r\n\r\n private enviar(chamada: Observable<unknown>, depois: () => void): void {\r\n if (this.enviando()) return;\r\n this.enviando.set(true);\r\n this.salvo.set(false);\r\n this.erro.set(null);\r\n\r\n chamada.subscribe({\r\n next: () => {\r\n this.enviando.set(false);\r\n depois();\r\n },\r\n error: (problema: ProblemDetails) => {\r\n this.enviando.set(false);\r\n this.erro.set(problema);\r\n },\r\n });\r\n }\r\n\r\n private contato(): BillingContactRequest {\r\n return {\r\n documentType: this.form.documentType,\r\n documentNumber: soDigitos(this.form.documentNumber),\r\n legalName: this.form.legalName.trim(),\r\n tradeName: opcional(this.form.tradeName),\r\n billingEmail: this.form.billingEmail.trim(),\r\n phone: opcional(soDigitos(this.form.phone)),\r\n mobilePhone: opcional(soDigitos(this.form.mobilePhone)),\r\n };\r\n }\r\n\r\n private endereco(): BillingAddressRequest {\r\n return {\r\n postalCode: soDigitos(this.form.postalCode),\r\n street: this.form.street.trim(),\r\n number: this.form.number.trim(),\r\n complement: opcional(this.form.complement),\r\n neighborhood: this.form.neighborhood.trim(),\r\n city: this.form.city.trim(),\r\n state: this.form.state.trim().toUpperCase(),\r\n };\r\n }\r\n}\r\n"],"names":[],"mappings":";;;;;;;;AA4CA,SAAS,QAAQ,CAAC,MAAsB,EAAA;AACtC,IAAA,MAAM,IAAI,GAAG,MAAM,CAAC,YAAY,IAAI,MAAM;IAC1C,OAAO;AACL,QAAA,YAAY,EAAE,IAAI;QAClB,cAAc,EAAE,iBAAiB,CAAC,IAAI,EAAE,MAAM,CAAC,cAAc,IAAI,EAAE,CAAC;AACpE,QAAA,SAAS,EAAE,MAAM,CAAC,SAAS,IAAI,EAAE;AACjC,QAAA,SAAS,EAAE,MAAM,CAAC,SAAS,IAAI,EAAE;AACjC,QAAA,YAAY,EAAE,MAAM,CAAC,YAAY,IAAI,EAAE;QACvC,KAAK,EAAE,gBAAgB,CAAC,MAAM,CAAC,KAAK,IAAI,EAAE,CAAC;QAC3C,WAAW,EAAE,gBAAgB,CAAC,MAAM,CAAC,WAAW,IAAI,EAAE,CAAC;QACvD,UAAU,EAAE,WAAW,CAAC,MAAM,CAAC,UAAU,IAAI,EAAE,CAAC;AAChD,QAAA,MAAM,EAAE,MAAM,CAAC,MAAM,IAAI,EAAE;AAC3B,QAAA,MAAM,EAAE,MAAM,CAAC,MAAM,IAAI,EAAE;AAC3B,QAAA,UAAU,EAAE,MAAM,CAAC,UAAU,IAAI,EAAE;AACnC,QAAA,YAAY,EAAE,MAAM,CAAC,YAAY,IAAI,EAAE;AACvC,QAAA,IAAI,EAAE,MAAM,CAAC,IAAI,IAAI,EAAE;AACvB,QAAA,KAAK,EAAE,MAAM,CAAC,KAAK,IAAI,EAAE;KAC1B;AACH;AAEA;AACA,SAAS,QAAQ,CAAC,KAAa,EAAA;AAC7B,IAAA,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,EAAE;IAC1B,OAAO,KAAK,GAAG,KAAK,GAAG,IAAI;AAC7B;AAEA;;;AAGG;MAsFU,WAAW,CAAA;AACL,IAAA,MAAM,GAAG,MAAM,CAAC,aAAa,CAAC;AAC9B,IAAA,MAAM,GAAG,MAAM,CAAC,MAAM,CAAC;AACvB,IAAA,KAAK,GAAG,MAAM,CAAC,cAAc,CAAC;AAErC,IAAA,IAAI,GAAe,QAAQ,CAAC,EAAoB,CAAC;IACxC,MAAM,GAAG,MAAM,CAAwB,IAAI;+EAAC;IAC5C,OAAO,GAAG,MAAM,CAAC,KAAK;gFAAC;IACvB,QAAQ,GAAG,MAAM,CAAC,KAAK;iFAAC;IACxB,KAAK,GAAG,MAAM,CAAC,KAAK;8EAAC;IACrB,IAAI,GAAG,MAAM,CAAwB,IAAI;6EAAC;IAE7D,QAAQ,GAAA;QACN,IAAI,CAAC,QAAQ,EAAE;IACjB;AAEA;;;;;AAKG;IACO,QAAQ,CAAC,KAAqB,EAAE,MAAa,EAAA;AACrD,QAAA,MAAM,OAAO,GAAG,MAAM,CAAC,MAA0B;AACjD,QAAA,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC;AACpD,QAAA,OAAO,CAAC,KAAK,GAAG,SAAS;AACzB,QAAA,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,SAAS;IAC9B;AAEU,IAAA,UAAU,CAAC,IAAkB,EAAA;AACrC,QAAA,IAAI,CAAC,IAAI,CAAC,YAAY,GAAG,IAAI;AAC7B,QAAA,IAAI,CAAC,IAAI,CAAC,cAAc,GAAG,iBAAiB,CAAC,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC;IAC9E;AAEQ,IAAA,OAAO,CAAC,KAAqB,EAAA;QACnC,QAAQ,KAAK;AACX,YAAA,KAAK,gBAAgB;AACnB,gBAAA,OAAO,IAAI,CAAC,IAAI,CAAC,YAAY,KAAK,KAAK,GAAG,WAAW,GAAG,YAAY;AACtE,YAAA,KAAK,YAAY;AACf,gBAAA,OAAO,WAAW;AACpB,YAAA;AACE,gBAAA,OAAO,gBAAgB;;IAE7B;IAEU,KAAK,GAAA;QACb,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,aAAa,CAAC,EAAE,GAAG,IAAI,CAAC,OAAO,EAAE,EAAE,GAAG,IAAI,CAAC,QAAQ,EAAE,EAAE,CAAC,EAAE,MAAK;;AAErF,YAAA,IAAI,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,aAAa,CAAC,GAAG,CAAC,MAAM,CAAC,KAAK,UAAU,EAAE;AAChE,gBAAA,KAAK,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,aAAa,CAAC,EAAE,EAAE,UAAU,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC;gBACtE;YACF;YACA,IAAI,CAAC,QAAQ,EAAE;AACjB,QAAA,CAAC,CAAC;IACJ;IAEU,aAAa,GAAA;QACrB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,oBAAoB,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,EAAE,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC3F;IAEU,cAAc,GAAA;QACtB,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,CAAC,oBAAoB,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC,EAAE,MAAM,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IAC5F;IAEQ,QAAQ,GAAA;AACd,QAAA,IAAI,CAAC,MAAM,CAAC,UAAU,EAAE,CAAC,SAAS,CAAC;AACjC,YAAA,IAAI,EAAE,CAAC,MAAM,KAAI;AACf,gBAAA,IAAI,CAAC,IAAI,GAAG,QAAQ,CAAC,MAAM,CAAC;gBAC5B,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,MAAM,CAAC,gBAAgB,KAAK,IAAI,CAAC;AAClD,gBAAA,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC;YACzB,CAAC;AACD,YAAA,KAAK,EAAE,CAAC,QAAwB,KAAK,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC;AAC7D,SAAA,CAAC;IACJ;IAEQ,MAAM,CAAC,OAA4B,EAAE,MAAkB,EAAA;QAC7D,IAAI,IAAI,CAAC,QAAQ,EAAE;YAAE;AACrB,QAAA,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC;AACvB,QAAA,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC;AACrB,QAAA,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC;QAEnB,OAAO,CAAC,SAAS,CAAC;YAChB,IAAI,EAAE,MAAK;AACT,gBAAA,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC;AACxB,gBAAA,MAAM,EAAE;YACV,CAAC;AACD,YAAA,KAAK,EAAE,CAAC,QAAwB,KAAI;AAClC,gBAAA,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,KAAK,CAAC;AACxB,gBAAA,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC;YACzB,CAAC;AACF,SAAA,CAAC;IACJ;IAEQ,OAAO,GAAA;QACb,OAAO;AACL,YAAA,YAAY,EAAE,IAAI,CAAC,IAAI,CAAC,YAAY;YACpC,cAAc,EAAE,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,cAAc,CAAC;YACnD,SAAS,EAAE,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE;YACrC,SAAS,EAAE,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC;YACxC,YAAY,EAAE,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE;YAC3C,KAAK,EAAE,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YAC3C,WAAW,EAAE,QAAQ,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;SACxD;IACH;IAEQ,QAAQ,GAAA;QACd,OAAO;YACL,UAAU,EAAE,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC;YAC3C,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE;YAC/B,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,EAAE;YAC/B,UAAU,EAAE,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,UAAU,CAAC;YAC1C,YAAY,EAAE,IAAI,CAAC,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE;YAC3C,IAAI,EAAE,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE;YAC3B,KAAK,EAAE,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE;SAC5C;IACH;uGAnHW,WAAW,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAX,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,WAAW,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,yBAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EAhFZ,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6ET,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,MAAA,EAAA,CAAA,o6BAAA,CAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EA/ES,WAAW,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,aAAA,EAAA,QAAA,EAAA,8CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,cAAA,EAAA,QAAA,EAAA,QAAA,EAAA,MAAA,EAAA,CAAA,SAAA,EAAA,OAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,uBAAA,EAAA,QAAA,EAAA,QAAA,EAAA,MAAA,EAAA,CAAA,SAAA,EAAA,OAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,oBAAA,EAAA,QAAA,EAAA,wSAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,0BAAA,EAAA,QAAA,EAAA,0JAAA,EAAA,MAAA,EAAA,CAAA,aAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,eAAA,EAAA,QAAA,EAAA,2CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,oBAAA,EAAA,QAAA,EAAA,sGAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,iBAAA,EAAA,QAAA,EAAA,wIAAA,EAAA,MAAA,EAAA,CAAA,UAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,kBAAA,EAAA,QAAA,EAAA,4EAAA,EAAA,MAAA,EAAA,CAAA,WAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,OAAA,EAAA,QAAA,EAAA,qDAAA,EAAA,MAAA,EAAA,CAAA,MAAA,EAAA,UAAA,EAAA,SAAA,EAAA,gBAAA,CAAA,EAAA,OAAA,EAAA,CAAA,eAAA,CAAA,EAAA,QAAA,EAAA,CAAA,SAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAA,EAAA,CAAA,MAAA,EAAA,QAAA,EAAA,yEAAA,EAAA,MAAA,EAAA,CAAA,eAAA,CAAA,EAAA,OAAA,EAAA,CAAA,UAAA,CAAA,EAAA,QAAA,EAAA,CAAA,QAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAE,gBAAgB,EAAA,QAAA,EAAA,oBAAA,EAAA,MAAA,EAAA,CAAA,yBAAA,EAAA,kBAAA,EAAA,0BAAA,CAAA,EAAA,EAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAAE,UAAU,kPAAE,qBAAqB,EAAA,QAAA,EAAA,uBAAA,EAAA,MAAA,EAAA,CAAA,UAAA,CAAA,EAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;2FAkF/D,WAAW,EAAA,UAAA,EAAA,CAAA;kBArFvB,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,yBAAyB,cACvB,IAAI,EAAA,OAAA,EACP,CAAC,WAAW,EAAE,gBAAgB,EAAE,UAAU,EAAE,qBAAqB,CAAC,EAAA,eAAA,EAC1D,uBAAuB,CAAC,MAAM,EAAA,QAAA,EACrC,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6ET,EAAA,CAAA,EAAA,MAAA,EAAA,CAAA,o6BAAA,CAAA,EAAA;;;;;"}
|
|
1
|
+
{"version":3,"file":"orechio-platform-angular-billing-profile.page-DRkoYOZa.mjs","sources":["../../../projects/platform-angular/billing/src/pages/profile.page.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, OnInit, inject, signal } from '@angular/core';\r\nimport { NgTemplateOutlet } from '@angular/common';\r\nimport { FormsModule } from '@angular/forms';\r\nimport { ActivatedRoute, Router, RouterLink } from '@angular/router';\r\nimport { Observable } from 'rxjs';\r\nimport { ProblemDetails } from '@orechio/platform-angular';\r\nimport { BillingClient } from '../billing-client';\r\nimport { BillingErrorComponent } from '../billing-error.component';\r\nimport {\r\n ESTILO_BASE,\r\n mascararCep,\r\n mascararCpf,\r\n mascararCnpj,\r\n mascararDocumento,\r\n mascararTelefone,\r\n soDigitos,\r\n} from '../billing-format';\r\nimport {\r\n BillingAddressRequest,\r\n BillingContactRequest,\r\n BillingProfile,\r\n DocumentType,\r\n} from '../billing-models';\r\n\r\ninterface Formulario {\r\n documentType: DocumentType;\r\n documentNumber: string;\r\n legalName: string;\r\n tradeName: string;\r\n billingEmail: string;\r\n phone: string;\r\n mobilePhone: string;\r\n postalCode: string;\r\n street: string;\r\n number: string;\r\n complement: string;\r\n neighborhood: string;\r\n city: string;\r\n state: string;\r\n}\r\n\r\n/** Os campos que levam máscara, e qual máscara cada um leva (o documento depende do tipo). */\r\ntype CampoMascarado = 'documentNumber' | 'phone' | 'mobilePhone' | 'postalCode';\r\n\r\nfunction doPerfil(perfil: BillingProfile): Formulario {\r\n const tipo = perfil.documentType ?? 'Cnpj';\r\n return {\r\n documentType: tipo,\r\n documentNumber: mascararDocumento(tipo, perfil.documentNumber ?? ''),\r\n legalName: perfil.legalName ?? '',\r\n tradeName: perfil.tradeName ?? '',\r\n billingEmail: perfil.billingEmail ?? '',\r\n phone: mascararTelefone(perfil.phone ?? ''),\r\n mobilePhone: mascararTelefone(perfil.mobilePhone ?? ''),\r\n postalCode: mascararCep(perfil.postalCode ?? ''),\r\n street: perfil.street ?? '',\r\n number: perfil.number ?? '',\r\n complement: perfil.complement ?? '',\r\n neighborhood: perfil.neighborhood ?? '',\r\n city: perfil.city ?? '',\r\n state: perfil.state ?? '',\r\n };\r\n}\r\n\r\n/** Campo opcional vazio vai como `null`, que é o que a Platform espera nos `string?`. */\r\nfunction opcional(valor: string): string | null {\r\n const limpo = valor.trim();\r\n return limpo ? limpo : null;\r\n}\r\n\r\n/**\r\n * Perfil de cobrança (ADR-021 §3.5, item 2). Cria com `POST profile`; edita por partes, como a\r\n * Platform: `PATCH profile/contact` e `PATCH profile/address` — não existe PUT.\r\n */\r\n@Component({\r\n selector: 'orechio-billing-profile',\r\n standalone: true,\r\n imports: [FormsModule, NgTemplateOutlet, RouterLink, BillingErrorComponent],\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n template: `\r\n <section class=\"orechio-billing\">\r\n <h1>Dados de cobrança</h1>\r\n\r\n @if (perfil(); as p) {\r\n @if (!p.exists) {\r\n <p class=\"orechio-billing__muted\">\r\n Informe os dados que vão na cobrança. O CPF ou CNPJ é exigido pelo emissor do PIX.\r\n </p>\r\n <form data-criar (ngSubmit)=\"criar()\">\r\n <ng-container *ngTemplateOutlet=\"contato\" />\r\n <ng-container *ngTemplateOutlet=\"endereco\" />\r\n <button type=\"submit\" [disabled]=\"enviando()\">{{ enviando() ? 'Salvando…' : 'Salvar' }}</button>\r\n </form>\r\n } @else {\r\n <form data-contato (ngSubmit)=\"salvarContato()\">\r\n <h2>Contato</h2>\r\n <ng-container *ngTemplateOutlet=\"contato\" />\r\n <button type=\"submit\" [disabled]=\"enviando()\">Salvar contato</button>\r\n </form>\r\n\r\n <form data-endereco (ngSubmit)=\"salvarEndereco()\">\r\n <h2>Endereço</h2>\r\n <ng-container *ngTemplateOutlet=\"endereco\" />\r\n <button type=\"submit\" [disabled]=\"enviando()\">Salvar endereço</button>\r\n </form>\r\n }\r\n\r\n @if (salvo()) {\r\n <p role=\"status\">Dados salvos.</p>\r\n }\r\n <a routerLink=\"..\">Voltar para a assinatura</a>\r\n } @else if (!erro()) {\r\n <p class=\"orechio-billing__muted\">Carregando…</p>\r\n }\r\n\r\n @if (erro(); as problema) {\r\n <orechio-billing-error [problema]=\"problema\" />\r\n }\r\n </section>\r\n\r\n <ng-template #contato>\r\n <label>Tipo de documento\r\n <select name=\"documentType\" [ngModel]=\"form.documentType\" (ngModelChange)=\"trocarTipo($event)\" [disabled]=\"travado()\">\r\n <option value=\"Cpf\">CPF</option>\r\n <option value=\"Cnpj\">CNPJ</option>\r\n </select>\r\n </label>\r\n <label>{{ form.documentType === 'Cpf' ? 'CPF' : 'CNPJ' }}\r\n <input name=\"documentNumber\" inputmode=\"numeric\" [value]=\"form.documentNumber\" (input)=\"mascarar('documentNumber', $event)\" [disabled]=\"travado()\" required />\r\n </label>\r\n <label>{{ form.documentType === 'Cpf' ? 'Nome completo' : 'Razão social' }}\r\n <input name=\"legalName\" [(ngModel)]=\"form.legalName\" [disabled]=\"travado()\" required />\r\n </label>\r\n <label>Nome fantasia (opcional)\r\n <input name=\"tradeName\" [(ngModel)]=\"form.tradeName\" />\r\n </label>\r\n <label>E-mail de cobrança\r\n <input name=\"billingEmail\" type=\"email\" autocomplete=\"email\" [(ngModel)]=\"form.billingEmail\" required />\r\n </label>\r\n <label>Telefone (opcional)\r\n <input name=\"phone\" type=\"tel\" [value]=\"form.phone\" (input)=\"mascarar('phone', $event)\" />\r\n </label>\r\n <label>Celular (opcional)\r\n <input name=\"mobilePhone\" type=\"tel\" [value]=\"form.mobilePhone\" (input)=\"mascarar('mobilePhone', $event)\" />\r\n </label>\r\n </ng-template>\r\n\r\n <ng-template #endereco>\r\n <label>CEP <input name=\"postalCode\" inputmode=\"numeric\" autocomplete=\"postal-code\" [value]=\"form.postalCode\" (input)=\"mascarar('postalCode', $event)\" required /></label>\r\n <label>Rua <input name=\"street\" autocomplete=\"address-line1\" [(ngModel)]=\"form.street\" required /></label>\r\n <label>Número <input name=\"number\" [(ngModel)]=\"form.number\" required /></label>\r\n <label>Complemento (opcional) <input name=\"complement\" autocomplete=\"address-line2\" [(ngModel)]=\"form.complement\" /></label>\r\n <label>Bairro <input name=\"neighborhood\" [(ngModel)]=\"form.neighborhood\" required /></label>\r\n <label>Cidade <input name=\"city\" autocomplete=\"address-level2\" [(ngModel)]=\"form.city\" required /></label>\r\n <label>UF <input name=\"state\" maxlength=\"2\" autocomplete=\"address-level1\" [(ngModel)]=\"form.state\" required /></label>\r\n </ng-template>\r\n `,\r\n styles: ESTILO_BASE,\r\n})\r\nexport class ProfilePage implements OnInit {\r\n private readonly client = inject(BillingClient);\r\n private readonly router = inject(Router);\r\n private readonly route = inject(ActivatedRoute);\r\n\r\n protected form: Formulario = doPerfil({} as BillingProfile);\r\n protected readonly perfil = signal<BillingProfile | null>(null);\r\n protected readonly travado = signal(false);\r\n protected readonly enviando = signal(false);\r\n protected readonly salvo = signal(false);\r\n protected readonly erro = signal<ProblemDetails | null>(null);\r\n\r\n ngOnInit(): void {\r\n this.carregar();\r\n }\r\n\r\n /**\r\n * Máscara ao digitar: o campo é reescrito pela máscara e o formulário guarda o texto\r\n * mascarado. Sem `ngModel` de propósito — com ele, digitar um caractere que a máscara\r\n * descarta deixaria o valor do modelo igual ao anterior, e o campo continuaria mostrando\r\n * o caractere digitado.\r\n */\r\n protected mascarar(campo: CampoMascarado, evento: Event): void {\r\n const entrada = evento.target as HTMLInputElement;\r\n const mascarado = this.mascara(campo)(entrada.value);\r\n entrada.value = mascarado;\r\n this.form[campo] = mascarado;\r\n }\r\n\r\n protected trocarTipo(tipo: DocumentType): void {\r\n this.form.documentType = tipo;\r\n this.form.documentNumber = mascararDocumento(tipo, this.form.documentNumber);\r\n }\r\n\r\n private mascara(campo: CampoMascarado): (valor: string) => string {\r\n switch (campo) {\r\n case 'documentNumber':\r\n return this.form.documentType === 'Cpf' ? mascararCpf : mascararCnpj;\r\n case 'postalCode':\r\n return mascararCep;\r\n default:\r\n return mascararTelefone;\r\n }\r\n }\r\n\r\n protected criar(): void {\r\n this.enviar(this.client.createProfile({ ...this.contato(), ...this.endereco() }), () => {\r\n // Quem veio do pagamento volta para ele: foi o checkout que mandou para cá.\r\n if (this.route.snapshot.queryParamMap.get('next') === 'checkout') {\r\n void this.router.navigate(['../checkout'], { relativeTo: this.route });\r\n return;\r\n }\r\n this.carregar();\r\n });\r\n }\r\n\r\n protected salvarContato(): void {\r\n this.enviar(this.client.updateProfileContact(this.contato()), () => this.salvo.set(true));\r\n }\r\n\r\n protected salvarEndereco(): void {\r\n this.enviar(this.client.updateProfileAddress(this.endereco()), () => this.salvo.set(true));\r\n }\r\n\r\n private carregar(): void {\r\n this.client.getProfile().subscribe({\r\n next: (perfil) => {\r\n this.form = doPerfil(perfil);\r\n this.travado.set(perfil.isDocumentLocked === true);\r\n this.perfil.set(perfil);\r\n },\r\n error: (problema: ProblemDetails) => this.erro.set(problema),\r\n });\r\n }\r\n\r\n private enviar(chamada: Observable<unknown>, depois: () => void): void {\r\n if (this.enviando()) return;\r\n this.enviando.set(true);\r\n this.salvo.set(false);\r\n this.erro.set(null);\r\n\r\n chamada.subscribe({\r\n next: () => {\r\n this.enviando.set(false);\r\n depois();\r\n },\r\n error: (problema: ProblemDetails) => {\r\n this.enviando.set(false);\r\n this.erro.set(problema);\r\n },\r\n });\r\n }\r\n\r\n private contato(): BillingContactRequest {\r\n return {\r\n documentType: this.form.documentType,\r\n documentNumber: soDigitos(this.form.documentNumber),\r\n legalName: this.form.legalName.trim(),\r\n tradeName: opcional(this.form.tradeName),\r\n billingEmail: this.form.billingEmail.trim(),\r\n phone: opcional(soDigitos(this.form.phone)),\r\n mobilePhone: opcional(soDigitos(this.form.mobilePhone)),\r\n };\r\n }\r\n\r\n private endereco(): BillingAddressRequest {\r\n return {\r\n postalCode: soDigitos(this.form.postalCode),\r\n street: this.form.street.trim(),\r\n number: this.form.number.trim(),\r\n complement: opcional(this.form.complement),\r\n neighborhood: this.form.neighborhood.trim(),\r\n city: this.form.city.trim(),\r\n state: this.form.state.trim().toUpperCase(),\r\n };\r\n }\r\n}\r\n"],"names":[],"mappings":"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"}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
2
|
import { inject, signal, computed, ChangeDetectionStrategy, Component } from '@angular/core';
|
|
3
3
|
import { RouterLink } from '@angular/router';
|
|
4
|
-
import { d as data, B as BillingClient, a as BillingErrorComponent } from './orechio-platform-angular-billing-billing-format-
|
|
4
|
+
import { d as data, B as BillingClient, a as BillingErrorComponent } from './orechio-platform-angular-billing-billing-format-C8QPXulm.mjs';
|
|
5
5
|
|
|
6
6
|
const DIA = 24 * 60 * 60 * 1000;
|
|
7
7
|
const EM_ABERTO = ['Created', 'Pending', 'Overdue'];
|
|
@@ -157,4 +157,4 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImpor
|
|
|
157
157
|
}] });
|
|
158
158
|
|
|
159
159
|
export { SubscriptionPage };
|
|
160
|
-
//# sourceMappingURL=orechio-platform-angular-billing-subscription.page-
|
|
160
|
+
//# sourceMappingURL=orechio-platform-angular-billing-subscription.page-Dbk-Znvg.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"orechio-platform-angular-billing-subscription.page-Cotupt22.mjs","sources":["../../../projects/platform-angular/billing/src/pages/subscription.page.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, OnInit, computed, inject, signal } from '@angular/core';\r\nimport { RouterLink } from '@angular/router';\r\nimport { ProblemDetails } from '@orechio/platform-angular';\r\nimport { BillingClient } from '../billing-client';\r\nimport { BillingErrorComponent } from '../billing-error.component';\r\nimport { ESTILO_BASE, data } from '../billing-format';\r\nimport { InvoiceListItem, InvoiceStatus, Subscription } from '../billing-models';\r\n\r\nconst DIA = 24 * 60 * 60 * 1000;\r\nconst EM_ABERTO: InvoiceStatus[] = ['Created', 'Pending', 'Overdue'];\r\n\r\nconst emAtraso = (assinatura: Subscription) =>\r\n assinatura.status === 'Grace' || assinatura.status === 'Suspended';\r\n\r\ninterface Situacao {\r\n titulo: string;\r\n detalhe: string;\r\n acao: { texto: string; link: string };\r\n}\r\n\r\n/**\r\n * A ação de cada estado segue o que a Platform aceita: o checkout serve `Trial`,\r\n * `PendingPayment` e, desde a BILLING-2b, `Grace` e `Suspended` — mas só sem fatura aberta\r\n * (`Created|Pending|Overdue`; com ela, `checkout.open_invoice_exists`). Em carência e suspensão\r\n * a ação depende disso: fatura aberta → o detalhe dela; nenhuma → o checkout. Cancelada e\r\n * expirada não têm ação de pagamento nesta versão: reativar é v2 (ADR-021 §3.5).\r\n */\r\nfunction situacao(assinatura: Subscription, agora: number, emAberto: InvoiceListItem | null): Situacao {\r\n const pagar = { texto: 'Pagar agora', link: 'checkout' };\r\n const faturas = { texto: 'Ver faturas', link: 'invoices' };\r\n const acaoDeAtraso = emAberto\r\n ? { texto: 'Pagar fatura em aberto', link: `invoices/${emAberto.id}` }\r\n : { texto: 'Pagar', link: 'checkout' };\r\n const orientacao = emAberto ? 'Pague a fatura em aberto' : 'Faça o pagamento';\r\n\r\n switch (assinatura.status) {\r\n case 'Trial': {\r\n const dias = Math.max(0, Math.ceil((Date.parse(assinatura.trialEndsAt) - agora) / DIA));\r\n const restante =\r\n dias === 0 ? 'O período de teste termina hoje.'\r\n : dias === 1 ? 'Falta 1 dia para o fim do período de teste.'\r\n : `Faltam ${dias} dias para o fim do período de teste.`;\r\n return { titulo: 'Período de teste', detalhe: restante, acao: { texto: 'Assinar agora', link: 'checkout' } };\r\n }\r\n case 'PendingPayment':\r\n return {\r\n titulo: 'Pagamento pendente',\r\n detalhe: 'A assinatura começa assim que o primeiro pagamento for confirmado.',\r\n acao: pagar,\r\n };\r\n case 'Active':\r\n return {\r\n titulo: 'Assinatura ativa',\r\n detalhe: assinatura.currentPeriodEndsAt\r\n ? `Próxima renovação em ${data(assinatura.currentPeriodEndsAt)}.`\r\n : 'Tudo em dia.',\r\n acao: faturas,\r\n };\r\n case 'Grace':\r\n return {\r\n titulo: 'Pagamento em atraso',\r\n detalhe: assinatura.graceEndsAt\r\n ? `O acesso continua até ${data(assinatura.graceEndsAt)}. ${orientacao} para não ter o acesso suspenso.`\r\n : `${orientacao} para não ter o acesso suspenso.`,\r\n acao: acaoDeAtraso,\r\n };\r\n case 'Suspended':\r\n return {\r\n titulo: 'Assinatura suspensa',\r\n detalhe: `O acesso foi suspenso por falta de pagamento. ${orientacao} para voltar a usar.`,\r\n acao: acaoDeAtraso,\r\n };\r\n case 'Cancelled':\r\n return {\r\n titulo: 'Assinatura cancelada',\r\n detalhe: assinatura.cancelEffectiveAt\r\n ? `O cancelamento vale a partir de ${data(assinatura.cancelEffectiveAt)}. Para reativar, fale com o suporte.`\r\n : 'Para reativar, fale com o suporte.',\r\n acao: faturas,\r\n };\r\n case 'Expired':\r\n return {\r\n titulo: 'Assinatura expirada',\r\n detalhe: 'Para voltar a assinar, fale com o suporte.',\r\n acao: faturas,\r\n };\r\n }\r\n}\r\n\r\n@Component({\r\n selector: 'orechio-billing-subscription',\r\n standalone: true,\r\n imports: [RouterLink, BillingErrorComponent],\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n template: `\r\n <section class=\"orechio-billing\">\r\n <h1>Assinatura</h1>\r\n\r\n @if (erro(); as problema) {\r\n <orechio-billing-error [problema]=\"problema\" />\r\n } @else if (atual(); as s) {\r\n <h2>{{ s.titulo }}</h2>\r\n <p>{{ s.detalhe }}</p>\r\n <a data-acao class=\"orechio-billing__acao\" [routerLink]=\"s.acao.link\">{{ s.acao.texto }}</a>\r\n\r\n <nav class=\"orechio-billing__muted\">\r\n <a routerLink=\"profile\">Dados de cobrança</a>\r\n ·\r\n <a routerLink=\"invoices\">Faturas</a>\r\n </nav>\r\n } @else {\r\n <p class=\"orechio-billing__muted\">Carregando…</p>\r\n }\r\n </section>\r\n `,\r\n styles: ESTILO_BASE,\r\n})\r\nexport class SubscriptionPage implements OnInit {\r\n private readonly client = inject(BillingClient);\r\n\r\n private readonly assinatura = signal<Subscription | null>(null);\r\n /** Só preenchido em Grace/Suspended: `undefined` = ainda não respondeu; `null` = nenhuma aberta. */\r\n private readonly emAberto = signal<InvoiceListItem | null | undefined>(undefined);\r\n protected readonly erro = signal<ProblemDetails | null>(null);\r\n protected readonly atual = computed(() => {\r\n const assinatura = this.assinatura();\r\n if (!assinatura) return null;\r\n\r\n const aberta = this.emAberto();\r\n // Sem a resposta das faturas não se sabe o destino da ação: não se adivinha.\r\n if (emAtraso(assinatura) && aberta === undefined) return null;\r\n\r\n return situacao(assinatura, Date.now(), aberta ?? null);\r\n });\r\n\r\n ngOnInit(): void {\r\n this.client.getSubscription().subscribe({\r\n next: (assinatura) => {\r\n this.assinatura.set(assinatura);\r\n if (emAtraso(assinatura)) this.buscarFaturaEmAberto();\r\n },\r\n error: (problema: ProblemDetails) => this.erro.set(problema),\r\n });\r\n }\r\n\r\n private buscarFaturaEmAberto(): void {\r\n this.client.listInvoices().subscribe({\r\n next: (pagina) => this.emAberto.set(pagina.invoices.find((f) => EM_ABERTO.includes(f.status)) ?? null),\r\n error: (problema: ProblemDetails) => this.erro.set(problema),\r\n });\r\n }\r\n}\r\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"orechio-platform-angular-billing-subscription.page-Dbk-Znvg.mjs","sources":["../../../projects/platform-angular/billing/src/pages/subscription.page.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, OnInit, computed, inject, signal } from '@angular/core';\r\nimport { RouterLink } from '@angular/router';\r\nimport { ProblemDetails } from '@orechio/platform-angular';\r\nimport { BillingClient } from '../billing-client';\r\nimport { BillingErrorComponent } from '../billing-error.component';\r\nimport { ESTILO_BASE, data } from '../billing-format';\r\nimport { InvoiceListItem, InvoiceStatus, Subscription } from '../billing-models';\r\n\r\nconst DIA = 24 * 60 * 60 * 1000;\r\nconst EM_ABERTO: InvoiceStatus[] = ['Created', 'Pending', 'Overdue'];\r\n\r\nconst emAtraso = (assinatura: Subscription) =>\r\n assinatura.status === 'Grace' || assinatura.status === 'Suspended';\r\n\r\ninterface Situacao {\r\n titulo: string;\r\n detalhe: string;\r\n acao: { texto: string; link: string };\r\n}\r\n\r\n/**\r\n * A ação de cada estado segue o que a Platform aceita: o checkout serve `Trial`,\r\n * `PendingPayment` e, desde a BILLING-2b, `Grace` e `Suspended` — mas só sem fatura aberta\r\n * (`Created|Pending|Overdue`; com ela, `checkout.open_invoice_exists`). Em carência e suspensão\r\n * a ação depende disso: fatura aberta → o detalhe dela; nenhuma → o checkout. Cancelada e\r\n * expirada não têm ação de pagamento nesta versão: reativar é v2 (ADR-021 §3.5).\r\n */\r\nfunction situacao(assinatura: Subscription, agora: number, emAberto: InvoiceListItem | null): Situacao {\r\n const pagar = { texto: 'Pagar agora', link: 'checkout' };\r\n const faturas = { texto: 'Ver faturas', link: 'invoices' };\r\n const acaoDeAtraso = emAberto\r\n ? { texto: 'Pagar fatura em aberto', link: `invoices/${emAberto.id}` }\r\n : { texto: 'Pagar', link: 'checkout' };\r\n const orientacao = emAberto ? 'Pague a fatura em aberto' : 'Faça o pagamento';\r\n\r\n switch (assinatura.status) {\r\n case 'Trial': {\r\n const dias = Math.max(0, Math.ceil((Date.parse(assinatura.trialEndsAt) - agora) / DIA));\r\n const restante =\r\n dias === 0 ? 'O período de teste termina hoje.'\r\n : dias === 1 ? 'Falta 1 dia para o fim do período de teste.'\r\n : `Faltam ${dias} dias para o fim do período de teste.`;\r\n return { titulo: 'Período de teste', detalhe: restante, acao: { texto: 'Assinar agora', link: 'checkout' } };\r\n }\r\n case 'PendingPayment':\r\n return {\r\n titulo: 'Pagamento pendente',\r\n detalhe: 'A assinatura começa assim que o primeiro pagamento for confirmado.',\r\n acao: pagar,\r\n };\r\n case 'Active':\r\n return {\r\n titulo: 'Assinatura ativa',\r\n detalhe: assinatura.currentPeriodEndsAt\r\n ? `Próxima renovação em ${data(assinatura.currentPeriodEndsAt)}.`\r\n : 'Tudo em dia.',\r\n acao: faturas,\r\n };\r\n case 'Grace':\r\n return {\r\n titulo: 'Pagamento em atraso',\r\n detalhe: assinatura.graceEndsAt\r\n ? `O acesso continua até ${data(assinatura.graceEndsAt)}. ${orientacao} para não ter o acesso suspenso.`\r\n : `${orientacao} para não ter o acesso suspenso.`,\r\n acao: acaoDeAtraso,\r\n };\r\n case 'Suspended':\r\n return {\r\n titulo: 'Assinatura suspensa',\r\n detalhe: `O acesso foi suspenso por falta de pagamento. ${orientacao} para voltar a usar.`,\r\n acao: acaoDeAtraso,\r\n };\r\n case 'Cancelled':\r\n return {\r\n titulo: 'Assinatura cancelada',\r\n detalhe: assinatura.cancelEffectiveAt\r\n ? `O cancelamento vale a partir de ${data(assinatura.cancelEffectiveAt)}. Para reativar, fale com o suporte.`\r\n : 'Para reativar, fale com o suporte.',\r\n acao: faturas,\r\n };\r\n case 'Expired':\r\n return {\r\n titulo: 'Assinatura expirada',\r\n detalhe: 'Para voltar a assinar, fale com o suporte.',\r\n acao: faturas,\r\n };\r\n }\r\n}\r\n\r\n@Component({\r\n selector: 'orechio-billing-subscription',\r\n standalone: true,\r\n imports: [RouterLink, BillingErrorComponent],\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n template: `\r\n <section class=\"orechio-billing\">\r\n <h1>Assinatura</h1>\r\n\r\n @if (erro(); as problema) {\r\n <orechio-billing-error [problema]=\"problema\" />\r\n } @else if (atual(); as s) {\r\n <h2>{{ s.titulo }}</h2>\r\n <p>{{ s.detalhe }}</p>\r\n <a data-acao class=\"orechio-billing__acao\" [routerLink]=\"s.acao.link\">{{ s.acao.texto }}</a>\r\n\r\n <nav class=\"orechio-billing__muted\">\r\n <a routerLink=\"profile\">Dados de cobrança</a>\r\n ·\r\n <a routerLink=\"invoices\">Faturas</a>\r\n </nav>\r\n } @else {\r\n <p class=\"orechio-billing__muted\">Carregando…</p>\r\n }\r\n </section>\r\n `,\r\n styles: ESTILO_BASE,\r\n})\r\nexport class SubscriptionPage implements OnInit {\r\n private readonly client = inject(BillingClient);\r\n\r\n private readonly assinatura = signal<Subscription | null>(null);\r\n /** Só preenchido em Grace/Suspended: `undefined` = ainda não respondeu; `null` = nenhuma aberta. */\r\n private readonly emAberto = signal<InvoiceListItem | null | undefined>(undefined);\r\n protected readonly erro = signal<ProblemDetails | null>(null);\r\n protected readonly atual = computed(() => {\r\n const assinatura = this.assinatura();\r\n if (!assinatura) return null;\r\n\r\n const aberta = this.emAberto();\r\n // Sem a resposta das faturas não se sabe o destino da ação: não se adivinha.\r\n if (emAtraso(assinatura) && aberta === undefined) return null;\r\n\r\n return situacao(assinatura, Date.now(), aberta ?? null);\r\n });\r\n\r\n ngOnInit(): void {\r\n this.client.getSubscription().subscribe({\r\n next: (assinatura) => {\r\n this.assinatura.set(assinatura);\r\n if (emAtraso(assinatura)) this.buscarFaturaEmAberto();\r\n },\r\n error: (problema: ProblemDetails) => this.erro.set(problema),\r\n });\r\n }\r\n\r\n private buscarFaturaEmAberto(): void {\r\n this.client.listInvoices().subscribe({\r\n next: (pagina) => this.emAberto.set(pagina.invoices.find((f) => EM_ABERTO.includes(f.status)) ?? null),\r\n error: (problema: ProblemDetails) => this.erro.set(problema),\r\n });\r\n }\r\n}\r\n"],"names":[],"mappings":"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"}
|
|
@@ -29,27 +29,27 @@ const ORECHIO_BILLING_ROUTES = [
|
|
|
29
29
|
path: '',
|
|
30
30
|
pathMatch: 'full',
|
|
31
31
|
title: 'Assinatura',
|
|
32
|
-
loadComponent: () => import('./orechio-platform-angular-billing-subscription.page-
|
|
32
|
+
loadComponent: () => import('./orechio-platform-angular-billing-subscription.page-Dbk-Znvg.mjs').then((m) => m.SubscriptionPage),
|
|
33
33
|
},
|
|
34
34
|
{
|
|
35
35
|
path: 'profile',
|
|
36
36
|
title: 'Dados de cobrança',
|
|
37
|
-
loadComponent: () => import('./orechio-platform-angular-billing-profile.page-
|
|
37
|
+
loadComponent: () => import('./orechio-platform-angular-billing-profile.page-DRkoYOZa.mjs').then((m) => m.ProfilePage),
|
|
38
38
|
},
|
|
39
39
|
{
|
|
40
40
|
path: 'checkout',
|
|
41
41
|
title: 'Pagar assinatura',
|
|
42
|
-
loadComponent: () => import('./orechio-platform-angular-billing-checkout.page-
|
|
42
|
+
loadComponent: () => import('./orechio-platform-angular-billing-checkout.page-Dr6ZGDkd.mjs').then((m) => m.CheckoutPage),
|
|
43
43
|
},
|
|
44
44
|
{
|
|
45
45
|
path: 'invoices',
|
|
46
46
|
title: 'Faturas',
|
|
47
|
-
loadComponent: () => import('./orechio-platform-angular-billing-invoices.page-
|
|
47
|
+
loadComponent: () => import('./orechio-platform-angular-billing-invoices.page-DytE1Lcz.mjs').then((m) => m.InvoicesPage),
|
|
48
48
|
},
|
|
49
49
|
{
|
|
50
50
|
path: 'invoices/:id',
|
|
51
51
|
title: 'Fatura',
|
|
52
|
-
loadComponent: () => import('./orechio-platform-angular-billing-invoice.page-
|
|
52
|
+
loadComponent: () => import('./orechio-platform-angular-billing-invoice.page-CWThsx-_.mjs').then((m) => m.InvoicePage),
|
|
53
53
|
},
|
|
54
54
|
];
|
|
55
55
|
|
|
@@ -193,6 +193,9 @@ function normalizeEntries(raw) {
|
|
|
193
193
|
.filter((branch) => branch !== null);
|
|
194
194
|
}
|
|
195
195
|
|
|
196
|
+
function normalizeName(name) {
|
|
197
|
+
return (name ?? '').trim().toLowerCase();
|
|
198
|
+
}
|
|
196
199
|
/**
|
|
197
200
|
* Explicit branch picker for the multi-branch login step.
|
|
198
201
|
*
|
|
@@ -249,7 +252,19 @@ class BranchSelector {
|
|
|
249
252
|
* why the name is preferred whenever it exists.
|
|
250
253
|
*/
|
|
251
254
|
label(branch, index) {
|
|
252
|
-
return branch.name ? branch.name : `Filial ${index + 1}`;
|
|
255
|
+
return branch.name?.trim() ? branch.name : `Filial ${index + 1}`;
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* The fragment exists only to tell apart branches that would otherwise read the
|
|
259
|
+
* same, so it is rendered when another option carries the same name (trimmed,
|
|
260
|
+
* case-insensitive) — or when there is no name at all, where the positional
|
|
261
|
+
* "Filial N" fallback gives the user nothing else to go on.
|
|
262
|
+
*/
|
|
263
|
+
showFragment(branch) {
|
|
264
|
+
const key = normalizeName(branch.name);
|
|
265
|
+
if (!key)
|
|
266
|
+
return true;
|
|
267
|
+
return this.branches().some((other) => other !== branch && normalizeName(other.name) === key);
|
|
253
268
|
}
|
|
254
269
|
/** Fragment of the id, shown beside the label to disambiguate repeated names. */
|
|
255
270
|
fragment(branch) {
|
|
@@ -273,7 +288,9 @@ class BranchSelector {
|
|
|
273
288
|
(change)="choose(branch.id)"
|
|
274
289
|
/>
|
|
275
290
|
<span class="orechio-branch-selector__label">{{ label(branch, index) }}</span>
|
|
276
|
-
|
|
291
|
+
@if (showFragment(branch)) {
|
|
292
|
+
<span class="orechio-branch-selector__hint">{{ ' · ' + fragment(branch) }}</span>
|
|
293
|
+
}
|
|
277
294
|
</label>
|
|
278
295
|
}
|
|
279
296
|
|
|
@@ -287,14 +304,11 @@ class BranchSelector {
|
|
|
287
304
|
</button>
|
|
288
305
|
</fieldset>
|
|
289
306
|
}
|
|
290
|
-
`, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
307
|
+
`, isInline: true, styles: [".orechio-branch-selector__option{display:flex;align-items:center;margin:.25rem 0}.orechio-branch-selector__option input[type=radio]{width:auto;height:auto;margin:0 .5rem 0 0}.orechio-branch-selector__hint{font-size:.85em;opacity:.7;white-space:pre}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
291
308
|
}
|
|
292
309
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: BranchSelector, decorators: [{
|
|
293
310
|
type: Component,
|
|
294
|
-
args: [{
|
|
295
|
-
selector: 'orechio-branch-selector',
|
|
296
|
-
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
297
|
-
template: `
|
|
311
|
+
args: [{ selector: 'orechio-branch-selector', changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
298
312
|
@if (branches().length === 0) {
|
|
299
313
|
<p class="orechio-branch-selector__empty" role="status">{{ emptyLabel() }}</p>
|
|
300
314
|
} @else {
|
|
@@ -311,7 +325,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImpor
|
|
|
311
325
|
(change)="choose(branch.id)"
|
|
312
326
|
/>
|
|
313
327
|
<span class="orechio-branch-selector__label">{{ label(branch, index) }}</span>
|
|
314
|
-
|
|
328
|
+
@if (showFragment(branch)) {
|
|
329
|
+
<span class="orechio-branch-selector__hint">{{ ' · ' + fragment(branch) }}</span>
|
|
330
|
+
}
|
|
315
331
|
</label>
|
|
316
332
|
}
|
|
317
333
|
|
|
@@ -325,8 +341,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImpor
|
|
|
325
341
|
</button>
|
|
326
342
|
</fieldset>
|
|
327
343
|
}
|
|
328
|
-
`,
|
|
329
|
-
}]
|
|
344
|
+
`, styles: [".orechio-branch-selector__option{display:flex;align-items:center;margin:.25rem 0}.orechio-branch-selector__option input[type=radio]{width:auto;height:auto;margin:0 .5rem 0 0}.orechio-branch-selector__hint{font-size:.85em;opacity:.7;white-space:pre}\n"] }]
|
|
330
345
|
}], propDecorators: { branches: [{ type: i0.Input, args: [{ isSignal: true, alias: "branches", required: true }] }], busy: [{ type: i0.Input, args: [{ isSignal: true, alias: "busy", required: false }] }], legend: [{ type: i0.Input, args: [{ isSignal: true, alias: "legend", required: false }] }], confirmLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "confirmLabel", required: false }] }], emptyLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyLabel", required: false }] }], selected: [{ type: i0.Output, args: ["selected"] }] } });
|
|
331
346
|
|
|
332
347
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"orechio-platform-angular.mjs","sources":["../../../projects/platform-angular/src/lib/config/platform-angular-config.ts","../../../projects/platform-angular/src/lib/session/session-store.ts","../../../projects/platform-angular/src/lib/auth/auth-client.ts","../../../projects/platform-angular/src/lib/auth/branch-selection.model.ts","../../../projects/platform-angular/src/lib/auth/branch-selector.ts","../../../projects/platform-angular/src/lib/auth/refresh-coordinator.ts","../../../projects/platform-angular/src/lib/http/provide-platform-angular-http.ts","../../../projects/platform-angular/src/lib/capabilities/capability.model.ts","../../../projects/platform-angular/src/lib/capabilities/capability.directive.ts","../../../projects/platform-angular/src/lib/problem-details/problem-details.model.ts","../../../projects/platform-angular/src/public-api.ts","../../../projects/platform-angular/src/orechio-platform-angular.ts"],"sourcesContent":["import { InjectionToken, Provider } from '@angular/core';\r\n\r\n/**\r\n * BFF endpoint paths for the auth contract. All paths are relative to `apiBaseUrl`.\r\n * `me` is optional: some BFFs bootstrap the session via `refresh` alone (no dedicated\r\n * session-read endpoint) instead of a separate `me` call.\r\n */\r\nexport interface PlatformAngularAuthPaths {\r\n login: string;\r\n refresh: string;\r\n logout: string;\r\n me?: string;\r\n /**\r\n * Branch switch for an already-authenticated session. Optional: only BFFs that\r\n * expose an authenticated switch endpoint set it, and `AuthClient.selectBranch`\r\n * fails fast when it is absent rather than guessing a URL.\r\n */\r\n selectBranch?: string;\r\n /**\r\n * Unwraps the raw auth-endpoint response body into TUser. Defaults to the\r\n * identity function (response body IS the user object), matching Store's\r\n * proven wire format. Portal's BFF wraps the user in an envelope\r\n * (`{ user: UserInfo }`) — Portal supplies `(body) => body.user` here\r\n * rather than the package assuming either shape.\r\n */\r\n unwrapUser?: (body: unknown) => unknown;\r\n}\r\n\r\nexport interface PlatformAngularConfig {\r\n /** Same-origin API base path, e.g. '/api'. Never an absolute cross-origin URL — the\r\n * BFF/cookie-session pattern this library assumes requires same-origin requests. */\r\n apiBaseUrl: string;\r\n auth: PlatformAngularAuthPaths;\r\n /** XSRF cookie/header names. Defaults match Angular's own defaults and both proven\r\n * consumers (Portal, Store): cookie 'XSRF-TOKEN', header 'X-XSRF-TOKEN'. */\r\n xsrf?: {\r\n cookieName?: string;\r\n headerName?: string;\r\n };\r\n /** 401 handling strategy. 'redirect' clears the session and redirects immediately\r\n * (Store's proven behavior). 'refresh-then-redirect' attempts one silent refresh\r\n * before giving up (Portal's proven behavior). Defaults to 'redirect' — the simpler,\r\n * more conservative behavior — when omitted. */\r\n unauthorizedStrategy?: 'redirect' | 'refresh-then-redirect';\r\n /** Route to navigate to after a session is cleared. Defaults to '/login'. */\r\n loginRoute?: string;\r\n}\r\n\r\nexport const PLATFORM_ANGULAR_CONFIG = new InjectionToken<PlatformAngularConfig>(\r\n 'PLATFORM_ANGULAR_CONFIG',\r\n);\r\n\r\nexport function providePlatformAngularConfig(config: PlatformAngularConfig): Provider {\r\n return { provide: PLATFORM_ANGULAR_CONFIG, useValue: config };\r\n}\r\n","import { Injectable, Signal, computed, signal } from '@angular/core';\nimport { SessionUser } from './session-user.model';\n\n/**\n * Generic signal-based session store. Mirrors the pattern already proven\n * independently by both consumers (Portal's AuthService.user, Store's\n * EstadoDaSessao) — deliberately NOT a richer state machine (unknown /\n * loading / error states): neither consumer needed one, and ADR-010 §2.2\n * (\"projete para extração, não extraia por antecipação\") argues against\n * building one speculatively.\n *\n * Parameterized over TUser so each product can extend SessionUser with its\n * own fields without needing a package release.\n */\n@Injectable({ providedIn: 'root' })\nexport class SessionStore<TUser extends SessionUser = SessionUser> {\n private readonly state = signal<TUser | null>(null);\n\n readonly user: Signal<TUser | null> = this.state.asReadonly();\n readonly isAuthenticated: Signal<boolean> = computed(() => this.state() !== null);\n\n set(user: TUser): void {\n this.state.set(user);\n }\n\n clear(): void {\n this.state.set(null);\n }\n}\n","import { HttpClient } from '@angular/common/http';\r\nimport { Injectable, inject } from '@angular/core';\r\nimport { Observable, finalize, map, tap, throwError } from 'rxjs';\r\nimport { PLATFORM_ANGULAR_CONFIG } from '../config/platform-angular-config';\r\nimport { SessionStore } from '../session/session-store';\r\nimport { SessionUser } from '../session/session-user.model';\r\n\r\n/**\r\n * Thin BFF client for the auth contract: login / refresh / logout, and an\r\n * optional session-read (\"me\") endpoint. Deliberately does NOT decode JWTs,\r\n * store bearer tokens, or treat itself as a security authority — per\r\n * ADR-010 §12 and this package's explicit non-goal (no JWT client SDK): the\r\n * BFF/backend is the only place tokens live, this class only reflects\r\n * whatever session-shaped JSON the BFF hands back.\r\n *\r\n * Endpoint paths are entirely config-driven (PLATFORM_ANGULAR_CONFIG) — this\r\n * class contains zero hardcoded URLs, so it works unmodified against\r\n * Portal's /api/auth/{login,me,refresh,logout} (envelope-wrapped\r\n * `{ user: UserInfo }` responses, unwrapped via config.auth.unwrapUser) and\r\n * the cookie-session hosts' /api/auth/{login,session,refresh,logout,select-branch}\r\n * (unwrapped responses; `session` is configured as `me`) alike. Those hosts are\r\n * Store, Moveis and any host mounting Orechio.Platform.Bff.Account 0.3.0\r\n * (ADR-022), which serves exactly this contract.\r\n */\r\n@Injectable({ providedIn: 'root' })\r\nexport class AuthClient<TUser extends SessionUser = SessionUser> {\r\n private readonly http = inject(HttpClient);\r\n private readonly config = inject(PLATFORM_ANGULAR_CONFIG);\r\n private readonly session = inject(SessionStore) as SessionStore<TUser>;\r\n\r\n private url(path: string): string {\r\n return `${this.config.apiBaseUrl}${path}`;\r\n }\r\n\r\n private unwrap(body: unknown): TUser {\r\n const unwrapUser = this.config.auth.unwrapUser;\r\n return (unwrapUser ? unwrapUser(body) : body) as TUser;\r\n }\r\n\r\n login(credentials: unknown): Observable<TUser> {\r\n return this.http.post<unknown>(this.url(this.config.auth.login), credentials).pipe(\r\n map((body) => this.unwrap(body)),\r\n tap((user) => this.session.set(user)),\r\n );\r\n }\r\n\r\n /**\r\n * Reads the current session. Uses the configured `me` endpoint (GET) when\r\n * present (Portal; Store and Moveis via `/api/auth/session`; and the\r\n * Orechio.Platform.Bff.Account 0.3.0 session routes, ADR-022) — a read that\r\n * neither rotates the session nor its cookies. Falls back to `refresh` (POST)\r\n * only for a host that configures no `me`, which then bootstraps via refresh.\r\n */\r\n initSession(): Observable<TUser> {\r\n if (this.config.auth.me) {\r\n return this.http.get<unknown>(this.url(this.config.auth.me)).pipe(\r\n map((body) => this.unwrap(body)),\r\n tap((user) => this.session.set(user)),\r\n );\r\n }\r\n return this.refresh();\r\n }\r\n\r\n /**\r\n * Asks the BFF to make `branchId` the current branch of the session that is\r\n * already authenticated. No credentials are involved and none are re-sent: the\r\n * browser proves nothing here beyond the session it already holds.\r\n *\r\n * `branchId` is a **requested target, never an authorization**. The BFF forwards\r\n * it to the Platform, which re-reads the user's grants and refuses what is not\r\n * granted — a rejection arrives as a failed request, and the session store is\r\n * left exactly as it was, still on the previous branch.\r\n *\r\n * The store is updated only from the session the server returns, never\r\n * optimistically from the requested id: the whole point of an explicit switch is\r\n * that the server, not the client, decides which branch the user ends up in.\r\n */\r\n selectBranch(branchId: string): Observable<TUser> {\r\n const path = this.config.auth.selectBranch;\r\n if (!path) {\r\n return throwError(\r\n () =>\r\n new Error(\r\n 'AuthClient.selectBranch requires config.auth.selectBranch. Configure the ' +\r\n 'BFF path that performs an authenticated branch switch.',\r\n ),\r\n );\r\n }\r\n\r\n return this.http.post<unknown>(this.url(path), { branchId }).pipe(\r\n map((body) => this.unwrap(body)),\r\n tap((user) => this.session.set(user)),\r\n );\r\n }\r\n\r\n refresh(): Observable<TUser> {\r\n return this.http.post<unknown>(this.url(this.config.auth.refresh), {}).pipe(\r\n map((body) => this.unwrap(body)),\r\n tap((user) => this.session.set(user)),\r\n );\r\n }\r\n\r\n /**\r\n * Clears the local session unconditionally — on success, on error, and on\r\n * unsubscribe (`finalize`, not `tap`) — matching the behavior both\r\n * consumers already converged on independently (Portal swallows the\r\n * error before clearing; Store clears via `finalize` while still letting\r\n * the error propagate). This does NOT swallow errors: callers that want\r\n * Portal's \"always succeed\" UX compose their own `catchError` on top.\r\n */\r\n logout(): Observable<void> {\r\n return this.http\r\n .post<void>(this.url(this.config.auth.logout), {})\r\n .pipe(finalize(() => this.session.clear()));\r\n }\r\n}\r\n","import { HttpErrorResponse } from '@angular/common/http';\r\n\r\n/**\r\n * Control-flow code the Platform's auth contract returns (HTTP 409) when the\r\n * authenticated user has access to more than one branch and none was chosen.\r\n *\r\n * It is deliberately NOT a failure: no session is created, no cookie is set,\r\n * and the caller is expected to ask the user which branch to enter. Treating\r\n * it as a generic error is the bug this constant exists to prevent.\r\n */\r\nexport const BRANCH_SELECTION_REQUIRED_CODE = 'auth.branch_selection_required';\r\n\r\n/**\r\n * One selectable branch.\r\n *\r\n * `name` is optional because the current backend contract carries branch ids\r\n * only — see `readBranchSelectionRequired`. Modelling it as optional now means\r\n * the day the backend starts sending names, consumers get them without a\r\n * breaking change to this type.\r\n */\r\nexport interface BranchOption {\r\n readonly id: string;\r\n readonly name?: string | null;\r\n}\r\n\r\n/** Payload of a branch-selection-required response, normalized. */\r\nexport interface BranchSelectionRequired {\r\n readonly branches: readonly BranchOption[];\r\n}\r\n\r\n/**\r\n * Recognizes a branch-selection-required response and normalizes its payload,\r\n * or returns `null` for anything else — so callers branch on a value instead of\r\n * re-deriving the contract from status codes and body shapes.\r\n *\r\n * Reads three wire shapes, in this order of preference:\r\n *\r\n * 1. `branchOptions: [{ id, name }]` — the display metadata the Platform sends\r\n * alongside the ids. Preferred when present, because it is the only shape\r\n * that lets an operator tell the branches apart.\r\n * 2. `branches: [\"<guid>\"]` — the original contract. Still authoritative for\r\n * selection, and still enough on its own.\r\n * 3. `branches: [{ id, name }]` — tolerated since 0.3.0.\r\n *\r\n * Reading all three is what makes the rollout order irrelevant: a frontend on\r\n * this version works against a backend that has the metadata and against one\r\n * that does not, and neither side has to ship first.\r\n *\r\n * Entries without a usable id are dropped rather than rendered as blanks: an\r\n * unselectable option in a selection list is worse than a shorter list.\r\n */\r\nexport function readBranchSelectionRequired(error: unknown): BranchSelectionRequired | null {\r\n if (!(error instanceof HttpErrorResponse) || error.status !== 409) return null;\r\n\r\n const body = error.error as\r\n | { code?: unknown; branches?: unknown; branchOptions?: unknown }\r\n | null;\r\n if (!body || body.code !== BRANCH_SELECTION_REQUIRED_CODE) return null;\r\n\r\n // `branchOptions` só substitui os ids quando traz alguma opção utilizável —\r\n // uma lista vazia ou ilegível não pode apagar a lista de ids que funciona.\r\n const comMetadata = normalizeEntries(body.branchOptions);\r\n if (comMetadata.length > 0) return { branches: comMetadata };\r\n\r\n if (!Array.isArray(body.branches)) return null;\r\n return { branches: normalizeEntries(body.branches) };\r\n}\r\n\r\nfunction normalizeEntries(raw: unknown): BranchOption[] {\r\n if (!Array.isArray(raw)) return [];\r\n\r\n return raw\r\n .map((entry): BranchOption | null => {\r\n if (typeof entry === 'string') return entry ? { id: entry } : null;\r\n if (entry && typeof entry === 'object') {\r\n const { id, name } = entry as { id?: unknown; name?: unknown };\r\n if (typeof id === 'string' && id) {\r\n return { id, name: typeof name === 'string' ? name : null };\r\n }\r\n }\r\n return null;\r\n })\r\n .filter((branch): branch is BranchOption => branch !== null);\r\n}\r\n","import { ChangeDetectionStrategy, Component, input, output } from '@angular/core';\r\nimport { BranchOption } from './branch-selection.model';\r\n\r\n/**\r\n * Explicit branch picker for the multi-branch login step.\r\n *\r\n * Deliberately narrow: it receives options, renders them, and emits the id the\r\n * user actually chose. It does not call any backend, hold credentials, read\r\n * tokens, touch storage, or know which product it is rendering inside — the\r\n * flow around it belongs to the consumer, and the copy around it belongs to the\r\n * consumer's login page.\r\n *\r\n * **It never picks for the user.** When the backend answers\r\n * `auth.branch_selection_required` it has already refused to choose (no session\r\n * is created); a selector that quietly defaulted to the first option would put\r\n * the arbitrary choice back one layer up, in the place hardest to notice.\r\n *\r\n * Rendered as a radio group so the browser gives keyboard navigation, focus\r\n * management, and screen-reader semantics for free — a list of buttons would\r\n * need all three re-implemented, worse.\r\n */\r\n@Component({\r\n selector: 'orechio-branch-selector',\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n template: `\r\n @if (branches().length === 0) {\r\n <p class=\"orechio-branch-selector__empty\" role=\"status\">{{ emptyLabel() }}</p>\r\n } @else {\r\n <fieldset class=\"orechio-branch-selector\" [disabled]=\"busy()\">\r\n <legend>{{ legend() }}</legend>\r\n\r\n @for (branch of branches(); track branch.id; let index = $index) {\r\n <label class=\"orechio-branch-selector__option\">\r\n <input\r\n type=\"radio\"\r\n name=\"orechio-branch\"\r\n [value]=\"branch.id\"\r\n [checked]=\"chosen === branch.id\"\r\n (change)=\"choose(branch.id)\"\r\n />\r\n <span class=\"orechio-branch-selector__label\">{{ label(branch, index) }}</span>\r\n <span class=\"orechio-branch-selector__hint\">{{ fragment(branch) }}</span>\r\n </label>\r\n }\r\n\r\n <button\r\n type=\"button\"\r\n class=\"orechio-branch-selector__confirm\"\r\n [disabled]=\"!chosen || busy()\"\r\n (click)=\"confirm()\"\r\n >\r\n {{ confirmLabel() }}\r\n </button>\r\n </fieldset>\r\n }\r\n `,\r\n})\r\nexport class BranchSelector {\r\n /** Options to choose from. An empty list renders the empty state, never a silent no-op. */\r\n readonly branches = input.required<readonly BranchOption[]>();\r\n\r\n /** Blocks input while the consumer is completing the sign-in round-trip. */\r\n readonly busy = input(false);\r\n\r\n // Copy is injectable because wording belongs to the product, not to the\r\n // platform package — the defaults exist so a consumer can drop the component\r\n // in and iterate on the words later.\r\n readonly legend = input('Selecione a filial');\r\n readonly confirmLabel = input('Continuar');\r\n readonly emptyLabel = input('Nenhuma filial disponível para este usuário.');\r\n\r\n /** Emits only in response to a user action. */\r\n readonly selected = output<string>();\r\n\r\n protected chosen: string | null = null;\r\n\r\n protected choose(id: string): void {\r\n this.chosen = id;\r\n }\r\n\r\n protected confirm(): void {\r\n if (this.chosen) this.selected.emit(this.chosen);\r\n }\r\n\r\n /**\r\n * The name is the label whenever the contract carries one. The id fragment stays\r\n * as secondary text rather than disappearing: the domain allows two branches to\r\n * share a name, and when that happens the fragment is the only thing telling\r\n * them apart.\r\n *\r\n * Without a name — an older backend, or metadata the server could not compose —\r\n * the positional fallback keeps the list selectable. Two raw identifiers side by\r\n * side are technically a choice and practically a coin flip, which is exactly\r\n * why the name is preferred whenever it exists.\r\n */\r\n protected label(branch: BranchOption, index: number): string {\r\n return branch.name ? branch.name : `Filial ${index + 1}`;\r\n }\r\n\r\n /** Fragment of the id, shown beside the label to disambiguate repeated names. */\r\n protected fragment(branch: BranchOption): string {\r\n return branch.id.slice(0, 8);\r\n }\r\n}\r\n","import { inject, Injectable } from '@angular/core';\r\nimport { Router } from '@angular/router';\r\nimport { Observable, ReplaySubject, catchError, finalize, share, throwError } from 'rxjs';\r\nimport { PLATFORM_ANGULAR_CONFIG } from '../config/platform-angular-config';\r\nimport { SessionStore } from '../session/session-store';\r\nimport { SessionUser } from '../session/session-user.model';\r\nimport { AuthClient } from './auth-client';\r\n\r\n/**\r\n * INTERNAL. Not exported from public-api.ts — consumers never construct or configure this\r\n * directly; it exists purely to back `sessionExpiryInterceptor`'s `'refresh-then-redirect'`\r\n * strategy (WEB-0.2, ADR-016 §\"WEB-0.2 note\").\r\n *\r\n * Deduplicates concurrent `AuthClient.refresh()` calls that originate from the SAME Angular\r\n * application context (one injector, i.e. one running app/tab). Before this existed, every\r\n * 401'd request independently called `authClient.refresh()`, and since that method returns a\r\n * cold Observable (`http.post(...)`, no multicasting), N simultaneous 401s produced N\r\n * independent `POST /refresh` calls — proven by the characterization test in\r\n * `provide-platform-angular-http.spec.ts`.\r\n *\r\n * This is defense-in-depth ONLY. The Platform already tolerates concurrent legitimate refresh\r\n * rotations correctly and safely (PLATFORM-REFRESH-1/1A/1B, ADR-016's retry leeway) — this class\r\n * exists to cut needless network traffic, cryptographic work, and rate-limit pressure on the\r\n * Platform, not to be the thing that makes concurrent refreshes safe. It does not, and does not\r\n * need to, coordinate across browser tabs/windows or other Angular application instances — see\r\n * the \"Multi-tab\" note on `sessionExpiryInterceptor` for why that's explicitly out of scope here\r\n * and handled server-side instead.\r\n */\r\n@Injectable({ providedIn: 'root' })\r\nexport class RefreshCoordinator {\r\n private readonly authClient = inject(AuthClient);\r\n private readonly session = inject(SessionStore);\r\n private readonly router = inject(Router);\r\n private readonly config = inject(PLATFORM_ANGULAR_CONFIG);\r\n\r\n private inFlight$: Observable<SessionUser> | null = null;\r\n\r\n /**\r\n * Returns the single in-flight refresh operation if one is already running, or starts a new\r\n * one. Every caller — first or piggybacking — gets the SAME underlying HTTP call's outcome.\r\n *\r\n * Semantics (WEB-0.2):\r\n * - Single-flight, not a permanent cache: once the shared operation settles (success or\r\n * error), the reference is cleared (`finalize`, placed BEFORE `share()` so it fires exactly\r\n * once per underlying HTTP call, not once per subscriber — see the `share()` config note\r\n * below). The next call to `refresh()` after that always starts a genuinely new request.\r\n * - On failure, the session is cleared and the browser is navigated to the login route\r\n * exactly once, regardless of how many callers are waiting — not once per waiting caller.\r\n * Each caller still receives its own error notification (multicast by `share()`) and is\r\n * responsible for propagating whatever error IT needs to its own subscriber (see\r\n * `sessionExpiryInterceptor`, which re-throws the ORIGINAL 401 to the request that\r\n * triggered it, not this refresh failure).\r\n * - `resetOnRefCountZero: false` is load-bearing: if one waiting caller unsubscribes (e.g. a\r\n * cancelled navigation), the still-in-flight HTTP request for the OTHER callers must not be\r\n * aborted. Angular's `HttpClient` cancels the underlying network request when its Observable\r\n * loses its last subscriber — without this flag, one caller going away could silently\r\n * cancel the refresh everyone else is still waiting on.\r\n * - `resetOnError`/`resetOnComplete` are left `false` too: this class never reuses a settled\r\n * `inFlight$` reference across cycles (the field is always nulled and rebuilt from scratch\r\n * by `finalize`), so `share()` itself never needs to reset its own internal connector.\r\n */\r\n refresh(): Observable<SessionUser> {\r\n if (this.inFlight$) {\r\n return this.inFlight$;\r\n }\r\n\r\n this.inFlight$ = this.authClient.refresh().pipe(\r\n catchError((error: unknown) => {\r\n this.session.clear();\r\n void this.router.navigateByUrl(this.config.loginRoute ?? '/login');\r\n return throwError(() => error);\r\n }),\r\n finalize(() => {\r\n this.inFlight$ = null;\r\n }),\r\n share({\r\n connector: () => new ReplaySubject<SessionUser>(1),\r\n resetOnError: false,\r\n resetOnComplete: false,\r\n resetOnRefCountZero: false,\r\n }),\r\n );\r\n\r\n return this.inFlight$;\r\n }\r\n}\r\n","import {\r\n HttpErrorResponse,\r\n HttpInterceptorFn,\r\n provideHttpClient,\r\n withInterceptors,\r\n withXsrfConfiguration,\r\n HttpFeature,\r\n HttpFeatureKind,\r\n} from '@angular/common/http';\r\nimport { inject } from '@angular/core';\r\nimport { Router } from '@angular/router';\r\nimport { catchError, switchMap, throwError } from 'rxjs';\r\nimport { PLATFORM_ANGULAR_CONFIG, PlatformAngularAuthPaths } from '../config/platform-angular-config';\r\nimport { RefreshCoordinator } from '../auth/refresh-coordinator';\r\nimport { SessionStore } from '../session/session-store';\r\n\r\nfunction isApiRequest(url: string, apiBaseUrl: string): boolean {\r\n return url === apiBaseUrl || url.startsWith(`${apiBaseUrl}/`);\r\n}\r\n\r\nfunction isAuthEndpoint(url: string, apiBaseUrl: string, auth: PlatformAngularAuthPaths): boolean {\r\n const authPaths = [auth.login, auth.refresh, auth.logout, auth.me].filter(\r\n (p): p is string => !!p,\r\n );\r\n return authPaths.some((p) => url === `${apiBaseUrl}${p}`);\r\n}\r\n\r\n/**\r\n * Forces `withCredentials: true` on same-origin API requests, so the BFF's\r\n * HttpOnly session cookie is sent. Proven identically in both consumers.\r\n * XSRF header injection is intentionally NOT duplicated here — Angular's\r\n * own `withXsrfConfiguration` (registered by `providePlatformAngularHttp`)\r\n * already does that. Portal's local interceptor previously re-implemented\r\n * XSRF header injection manually alongside `withXsrfConfiguration`; that\r\n * was redundant, not a pattern to preserve.\r\n */\r\nexport const credentialsInterceptor: HttpInterceptorFn = (req, next) => {\r\n const { apiBaseUrl } = inject(PLATFORM_ANGULAR_CONFIG);\r\n if (!isApiRequest(req.url, apiBaseUrl)) {\r\n return next(req);\r\n }\r\n return next(req.clone({ withCredentials: true }));\r\n};\r\n\r\n/**\r\n * On 401 from a non-auth API request: 'redirect' (default) clears the\r\n * session and navigates to the login route immediately (Store's proven\r\n * behavior). 'refresh-then-redirect' attempts one silent refresh first and\r\n * retries the original request on success, falling back to redirect\r\n * (Portal's proven behavior). Configured via\r\n * PlatformAngularConfig.unauthorizedStrategy.\r\n *\r\n * WEB-0.2: the 'refresh-then-redirect' path routes through `RefreshCoordinator`\r\n * instead of calling `AuthClient.refresh()` directly — every 401 handled by\r\n * the SAME running app shares one in-flight refresh instead of each request\r\n * triggering its own. The 'redirect' strategy is untouched: it never calls\r\n * refresh at all, so there is nothing to coordinate (see the config note on\r\n * `PlatformAngularConfig.unauthorizedStrategy`). On refresh failure, the\r\n * session-clear/navigate side effect now happens once inside the coordinator\r\n * (shared across every waiting caller) rather than once per waiting caller;\r\n * each caller still receives the ORIGINAL 401 error, not a refresh-specific\r\n * one, exactly as before. Multi-tab/multi-window coordination is explicitly\r\n * NOT handled here — see `RefreshCoordinator`'s doc comment — the Platform's\r\n * own server-side retry leeway (ADR-016) is what makes concurrent refreshes\r\n * from independent contexts safe.\r\n */\r\nexport const sessionExpiryInterceptor: HttpInterceptorFn = (req, next) => {\r\n const config = inject(PLATFORM_ANGULAR_CONFIG);\r\n const session = inject(SessionStore);\r\n const router = inject(Router);\r\n const refreshCoordinator = inject(RefreshCoordinator);\r\n\r\n if (\r\n !isApiRequest(req.url, config.apiBaseUrl) ||\r\n isAuthEndpoint(req.url, config.apiBaseUrl, config.auth)\r\n ) {\r\n return next(req);\r\n }\r\n\r\n return next(req).pipe(\r\n catchError((error: unknown) => {\r\n if (!(error instanceof HttpErrorResponse) || error.status !== 401) {\r\n return throwError(() => error);\r\n }\r\n\r\n if (config.unauthorizedStrategy !== 'refresh-then-redirect') {\r\n session.clear();\r\n void router.navigateByUrl(config.loginRoute ?? '/login');\r\n return throwError(() => error);\r\n }\r\n\r\n return refreshCoordinator.refresh().pipe(\r\n switchMap(() => next(req)),\r\n // The coordinator already cleared the session and navigated once for\r\n // this shared failure (see RefreshCoordinator.refresh()) — this\r\n // request still needs to fail with its OWN original 401, not the\r\n // refresh's error, exactly as before WEB-0.2.\r\n catchError(() => throwError(() => error)),\r\n );\r\n }),\r\n );\r\n};\r\n\r\n/**\r\n * Bundles provideHttpClient with the XSRF configuration and interceptors\r\n * proven necessary by both consumers. Cookie/header names default to\r\n * 'XSRF-TOKEN'/'X-XSRF-TOKEN' (identical in both consumers today) but\r\n * remain overridable via config. Consumers add their own extra\r\n * provideHttpClient features (e.g. Portal's withXhr()) by calling\r\n * provideHttpClient(...) separately — this function only owns the concerns\r\n * proven common to both.\r\n */\r\nexport function providePlatformAngularHttp(...extraFeatures: HttpFeature<HttpFeatureKind>[]) {\r\n return provideHttpClient(\r\n withXsrfConfiguration({\r\n cookieName: 'XSRF-TOKEN',\r\n headerName: 'X-XSRF-TOKEN',\r\n }),\r\n withInterceptors([credentialsInterceptor, sessionExpiryInterceptor]),\r\n ...extraFeatures,\r\n );\r\n}\r\n","/**\n * Generic capability map: a flat set of boolean feature flags fetched once\n * from the BFF (Portal's proven pattern — GET .../capabilities returning a\n * flat boolean-keyed object). Deliberately a generic `Record<string,\n * boolean>` rather than a typed interface enumerating every field: Portal's\n * capability set already has 17 product-specific fields and Store will\n * grow its own, unrelated set. A typed interface would require a package\n * release for every new product capability (see ADR-010 §18 — evolution\n * cadence mismatch is a real argument against sharing). Each product\n * defines its own capability keys locally and reads them through this map.\n *\n * UX ONLY. This does not replace server-side authorization. Every consumer\n * MUST continue to handle 403 from the actual mutating/reading endpoint —\n * capability checks here are a consultive pre-check, exactly as already\n * documented in Portal's application.service.ts before this extraction.\n */\nexport type CapabilityMap = Readonly<Record<string, boolean>>;\n\nexport function hasCapability(capabilities: CapabilityMap | null | undefined, key: string): boolean {\n return capabilities?.[key] === true;\n}\n","import { Directive, Input, TemplateRef, ViewContainerRef, inject } from '@angular/core';\nimport { CapabilityMap, hasCapability } from './capability.model';\n\n/**\n * Structural directive gating template content on a capability key from a\n * consumer-supplied CapabilityMap. Net-new (neither consumer had factored\n * their proven inline `@if (capabilities()?.x)` pattern into a directive),\n * but grounded in Portal's validated usage — see capability.model.ts.\n *\n * UX ONLY — see capability.model.ts. Hiding an element here does not\n * authorize anything; the backend re-validates on every mutating call.\n *\n * Usage: `<button *orxCapability=\"capabilities(); has: 'manageTenants'\">`\n */\n@Directive({\n selector: '[orxCapability]',\n standalone: true,\n})\nexport class CapabilityDirective {\n private readonly templateRef = inject(TemplateRef<unknown>);\n private readonly viewContainer = inject(ViewContainerRef);\n\n private map: CapabilityMap | null | undefined;\n private key: string | undefined;\n private hasView = false;\n\n @Input()\n set orxCapability(map: CapabilityMap | null | undefined) {\n this.map = map;\n this.render();\n }\n\n @Input('orxCapabilityHas')\n set has(key: string) {\n this.key = key;\n this.render();\n }\n\n private render(): void {\n const shouldShow = !!this.key && hasCapability(this.map, this.key);\n if (shouldShow && !this.hasView) {\n this.viewContainer.createEmbeddedView(this.templateRef);\n this.hasView = true;\n } else if (!shouldShow && this.hasView) {\n this.viewContainer.clear();\n this.hasView = false;\n }\n }\n}\n","import { HttpErrorResponse } from '@angular/common/http';\r\n\r\n/**\r\n * Canonical error shape, generalized from Store's already-proven\r\n * ErroApresentavel/normalizarErroApi (the more mature of the two consumers'\r\n * error handling — Portal had no typed contract at all, just five\r\n * duplicated `error.error.code` reads). `message` is optional: Portal never\r\n * received a server-supplied message field, only `code`, and resolves\r\n * user-facing text locally — so this contract cannot require it.\r\n *\r\n * `correlation_id` (API-1, ADR-017 §3.3): added after confirming directly\r\n * from Orechio.Platform's `ProblemResponses.cs` that it IS present in the\r\n * JSON body of every error response the Platform host produces (not just\r\n * the `X-Correlation-ID` response header), and that Portal's BFF relays\r\n * that body byte-for-byte, unreshaped, for every typed endpoint. The\r\n * earlier NG-1-era absence was accurate for what it tested at the time but\r\n * incomplete relative to Platform's actual wire contract. Optional: not\r\n * every error path in every consumer emits this field today (Store's own\r\n * narrower auth/CSRF error shapes don't) — its absence must not be treated\r\n * as a parse failure.\r\n *\r\n * Domain-specific error codes (e.g. 'access.platform_only',\r\n * 'tenant.not_found', 'csrf.invalid') are NOT enumerated here — `code`\r\n * stays a plain string so any code survives normalization unmodified and\r\n * remains identifiable by callers, per Passo 28.\r\n */\r\nexport interface ProblemDetails {\r\n code: string;\r\n message?: string;\r\n status: number;\r\n required?: string;\r\n correlationId?: string;\r\n}\r\n\r\n/**\r\n * Normalizes an HttpErrorResponse into ProblemDetails. Mirrors Store's\r\n * normalizarErroApi exactly, generalized to make `message` optional and to\r\n * accept an HttpErrorResponse without any consumer-specific fallback\r\n * message map (each product keeps its own code→message copy locally —\r\n * that's product-specific text, not shared infrastructure).\r\n */\r\nexport function normalizeProblemDetails(error: HttpErrorResponse): ProblemDetails {\r\n const body = error.error as Record<string, unknown> | null | undefined;\r\n const code = typeof body?.['code'] === 'string' ? (body['code'] as string) : `http.${error.status || 0}`;\r\n const message = typeof body?.['message'] === 'string' ? (body['message'] as string) : undefined;\r\n const required = typeof body?.['required'] === 'string' ? (body['required'] as string) : undefined;\r\n // Wire field is `correlation_id` (snake_case, ADR-017 §3) — kept as-is on the wire,\r\n // exposed as `correlationId` here per TS/Angular convention (same server-side pattern:\r\n // OrechioProblemDetails.CorrelationId is [JsonPropertyName(\"correlation_id\")]).\r\n const correlationId =\r\n typeof body?.['correlation_id'] === 'string' ? (body['correlation_id'] as string) : undefined;\r\n\r\n return {\r\n code,\r\n message,\r\n status: error.status,\r\n ...(required ? { required } : {}),\r\n ...(correlationId ? { correlationId } : {}),\r\n };\r\n}\r\n","/*\r\n * Public API Surface of @orechio/platform-angular\r\n */\r\n\r\nexport type { PlatformAngularConfig, PlatformAngularAuthPaths } from './lib/config/platform-angular-config';\r\nexport { PLATFORM_ANGULAR_CONFIG, providePlatformAngularConfig } from './lib/config/platform-angular-config';\r\n\r\nexport type { SessionUser } from './lib/session/session-user.model';\r\nexport { SessionStore } from './lib/session/session-store';\r\n\r\nexport { AuthClient } from './lib/auth/auth-client';\r\n\r\nexport type { BranchOption, BranchSelectionRequired } from './lib/auth/branch-selection.model';\r\nexport {\r\n BRANCH_SELECTION_REQUIRED_CODE,\r\n readBranchSelectionRequired,\r\n} from './lib/auth/branch-selection.model';\r\nexport { BranchSelector } from './lib/auth/branch-selector';\r\n\r\nexport { credentialsInterceptor, sessionExpiryInterceptor, providePlatformAngularHttp } from './lib/http/provide-platform-angular-http';\r\n\r\nexport type { CapabilityMap } from './lib/capabilities/capability.model';\r\nexport { hasCapability } from './lib/capabilities/capability.model';\r\nexport { CapabilityDirective } from './lib/capabilities/capability.directive';\r\n\r\nexport type { ProblemDetails } from './lib/problem-details/problem-details.model';\r\nexport { normalizeProblemDetails } from './lib/problem-details/problem-details.model';\r\n\r\nexport type { PagedResult } from './lib/pagination/paged-result.model';\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"orechio-platform-angular.mjs","sources":["../../../projects/platform-angular/src/lib/config/platform-angular-config.ts","../../../projects/platform-angular/src/lib/session/session-store.ts","../../../projects/platform-angular/src/lib/auth/auth-client.ts","../../../projects/platform-angular/src/lib/auth/branch-selection.model.ts","../../../projects/platform-angular/src/lib/auth/branch-selector.ts","../../../projects/platform-angular/src/lib/auth/refresh-coordinator.ts","../../../projects/platform-angular/src/lib/http/provide-platform-angular-http.ts","../../../projects/platform-angular/src/lib/capabilities/capability.model.ts","../../../projects/platform-angular/src/lib/capabilities/capability.directive.ts","../../../projects/platform-angular/src/lib/problem-details/problem-details.model.ts","../../../projects/platform-angular/src/public-api.ts","../../../projects/platform-angular/src/orechio-platform-angular.ts"],"sourcesContent":["import { InjectionToken, Provider } from '@angular/core';\r\n\r\n/**\r\n * BFF endpoint paths for the auth contract. All paths are relative to `apiBaseUrl`.\r\n * `me` is optional: some BFFs bootstrap the session via `refresh` alone (no dedicated\r\n * session-read endpoint) instead of a separate `me` call.\r\n */\r\nexport interface PlatformAngularAuthPaths {\r\n login: string;\r\n refresh: string;\r\n logout: string;\r\n me?: string;\r\n /**\r\n * Branch switch for an already-authenticated session. Optional: only BFFs that\r\n * expose an authenticated switch endpoint set it, and `AuthClient.selectBranch`\r\n * fails fast when it is absent rather than guessing a URL.\r\n */\r\n selectBranch?: string;\r\n /**\r\n * Unwraps the raw auth-endpoint response body into TUser. Defaults to the\r\n * identity function (response body IS the user object), matching Store's\r\n * proven wire format. Portal's BFF wraps the user in an envelope\r\n * (`{ user: UserInfo }`) — Portal supplies `(body) => body.user` here\r\n * rather than the package assuming either shape.\r\n */\r\n unwrapUser?: (body: unknown) => unknown;\r\n}\r\n\r\nexport interface PlatformAngularConfig {\r\n /** Same-origin API base path, e.g. '/api'. Never an absolute cross-origin URL — the\r\n * BFF/cookie-session pattern this library assumes requires same-origin requests. */\r\n apiBaseUrl: string;\r\n auth: PlatformAngularAuthPaths;\r\n /** XSRF cookie/header names. Defaults match Angular's own defaults and both proven\r\n * consumers (Portal, Store): cookie 'XSRF-TOKEN', header 'X-XSRF-TOKEN'. */\r\n xsrf?: {\r\n cookieName?: string;\r\n headerName?: string;\r\n };\r\n /** 401 handling strategy. 'redirect' clears the session and redirects immediately\r\n * (Store's proven behavior). 'refresh-then-redirect' attempts one silent refresh\r\n * before giving up (Portal's proven behavior). Defaults to 'redirect' — the simpler,\r\n * more conservative behavior — when omitted. */\r\n unauthorizedStrategy?: 'redirect' | 'refresh-then-redirect';\r\n /** Route to navigate to after a session is cleared. Defaults to '/login'. */\r\n loginRoute?: string;\r\n}\r\n\r\nexport const PLATFORM_ANGULAR_CONFIG = new InjectionToken<PlatformAngularConfig>(\r\n 'PLATFORM_ANGULAR_CONFIG',\r\n);\r\n\r\nexport function providePlatformAngularConfig(config: PlatformAngularConfig): Provider {\r\n return { provide: PLATFORM_ANGULAR_CONFIG, useValue: config };\r\n}\r\n","import { Injectable, Signal, computed, signal } from '@angular/core';\nimport { SessionUser } from './session-user.model';\n\n/**\n * Generic signal-based session store. Mirrors the pattern already proven\n * independently by both consumers (Portal's AuthService.user, Store's\n * EstadoDaSessao) — deliberately NOT a richer state machine (unknown /\n * loading / error states): neither consumer needed one, and ADR-010 §2.2\n * (\"projete para extração, não extraia por antecipação\") argues against\n * building one speculatively.\n *\n * Parameterized over TUser so each product can extend SessionUser with its\n * own fields without needing a package release.\n */\n@Injectable({ providedIn: 'root' })\nexport class SessionStore<TUser extends SessionUser = SessionUser> {\n private readonly state = signal<TUser | null>(null);\n\n readonly user: Signal<TUser | null> = this.state.asReadonly();\n readonly isAuthenticated: Signal<boolean> = computed(() => this.state() !== null);\n\n set(user: TUser): void {\n this.state.set(user);\n }\n\n clear(): void {\n this.state.set(null);\n }\n}\n","import { HttpClient } from '@angular/common/http';\r\nimport { Injectable, inject } from '@angular/core';\r\nimport { Observable, finalize, map, tap, throwError } from 'rxjs';\r\nimport { PLATFORM_ANGULAR_CONFIG } from '../config/platform-angular-config';\r\nimport { SessionStore } from '../session/session-store';\r\nimport { SessionUser } from '../session/session-user.model';\r\n\r\n/**\r\n * Thin BFF client for the auth contract: login / refresh / logout, and an\r\n * optional session-read (\"me\") endpoint. Deliberately does NOT decode JWTs,\r\n * store bearer tokens, or treat itself as a security authority — per\r\n * ADR-010 §12 and this package's explicit non-goal (no JWT client SDK): the\r\n * BFF/backend is the only place tokens live, this class only reflects\r\n * whatever session-shaped JSON the BFF hands back.\r\n *\r\n * Endpoint paths are entirely config-driven (PLATFORM_ANGULAR_CONFIG) — this\r\n * class contains zero hardcoded URLs, so it works unmodified against\r\n * Portal's /api/auth/{login,me,refresh,logout} (envelope-wrapped\r\n * `{ user: UserInfo }` responses, unwrapped via config.auth.unwrapUser) and\r\n * the cookie-session hosts' /api/auth/{login,session,refresh,logout,select-branch}\r\n * (unwrapped responses; `session` is configured as `me`) alike. Those hosts are\r\n * Store, Moveis and any host mounting Orechio.Platform.Bff.Account 0.3.0\r\n * (ADR-022), which serves exactly this contract.\r\n */\r\n@Injectable({ providedIn: 'root' })\r\nexport class AuthClient<TUser extends SessionUser = SessionUser> {\r\n private readonly http = inject(HttpClient);\r\n private readonly config = inject(PLATFORM_ANGULAR_CONFIG);\r\n private readonly session = inject(SessionStore) as SessionStore<TUser>;\r\n\r\n private url(path: string): string {\r\n return `${this.config.apiBaseUrl}${path}`;\r\n }\r\n\r\n private unwrap(body: unknown): TUser {\r\n const unwrapUser = this.config.auth.unwrapUser;\r\n return (unwrapUser ? unwrapUser(body) : body) as TUser;\r\n }\r\n\r\n login(credentials: unknown): Observable<TUser> {\r\n return this.http.post<unknown>(this.url(this.config.auth.login), credentials).pipe(\r\n map((body) => this.unwrap(body)),\r\n tap((user) => this.session.set(user)),\r\n );\r\n }\r\n\r\n /**\r\n * Reads the current session. Uses the configured `me` endpoint (GET) when\r\n * present (Portal; Store and Moveis via `/api/auth/session`; and the\r\n * Orechio.Platform.Bff.Account 0.3.0 session routes, ADR-022) — a read that\r\n * neither rotates the session nor its cookies. Falls back to `refresh` (POST)\r\n * only for a host that configures no `me`, which then bootstraps via refresh.\r\n */\r\n initSession(): Observable<TUser> {\r\n if (this.config.auth.me) {\r\n return this.http.get<unknown>(this.url(this.config.auth.me)).pipe(\r\n map((body) => this.unwrap(body)),\r\n tap((user) => this.session.set(user)),\r\n );\r\n }\r\n return this.refresh();\r\n }\r\n\r\n /**\r\n * Asks the BFF to make `branchId` the current branch of the session that is\r\n * already authenticated. No credentials are involved and none are re-sent: the\r\n * browser proves nothing here beyond the session it already holds.\r\n *\r\n * `branchId` is a **requested target, never an authorization**. The BFF forwards\r\n * it to the Platform, which re-reads the user's grants and refuses what is not\r\n * granted — a rejection arrives as a failed request, and the session store is\r\n * left exactly as it was, still on the previous branch.\r\n *\r\n * The store is updated only from the session the server returns, never\r\n * optimistically from the requested id: the whole point of an explicit switch is\r\n * that the server, not the client, decides which branch the user ends up in.\r\n */\r\n selectBranch(branchId: string): Observable<TUser> {\r\n const path = this.config.auth.selectBranch;\r\n if (!path) {\r\n return throwError(\r\n () =>\r\n new Error(\r\n 'AuthClient.selectBranch requires config.auth.selectBranch. Configure the ' +\r\n 'BFF path that performs an authenticated branch switch.',\r\n ),\r\n );\r\n }\r\n\r\n return this.http.post<unknown>(this.url(path), { branchId }).pipe(\r\n map((body) => this.unwrap(body)),\r\n tap((user) => this.session.set(user)),\r\n );\r\n }\r\n\r\n refresh(): Observable<TUser> {\r\n return this.http.post<unknown>(this.url(this.config.auth.refresh), {}).pipe(\r\n map((body) => this.unwrap(body)),\r\n tap((user) => this.session.set(user)),\r\n );\r\n }\r\n\r\n /**\r\n * Clears the local session unconditionally — on success, on error, and on\r\n * unsubscribe (`finalize`, not `tap`) — matching the behavior both\r\n * consumers already converged on independently (Portal swallows the\r\n * error before clearing; Store clears via `finalize` while still letting\r\n * the error propagate). This does NOT swallow errors: callers that want\r\n * Portal's \"always succeed\" UX compose their own `catchError` on top.\r\n */\r\n logout(): Observable<void> {\r\n return this.http\r\n .post<void>(this.url(this.config.auth.logout), {})\r\n .pipe(finalize(() => this.session.clear()));\r\n }\r\n}\r\n","import { HttpErrorResponse } from '@angular/common/http';\r\n\r\n/**\r\n * Control-flow code the Platform's auth contract returns (HTTP 409) when the\r\n * authenticated user has access to more than one branch and none was chosen.\r\n *\r\n * It is deliberately NOT a failure: no session is created, no cookie is set,\r\n * and the caller is expected to ask the user which branch to enter. Treating\r\n * it as a generic error is the bug this constant exists to prevent.\r\n */\r\nexport const BRANCH_SELECTION_REQUIRED_CODE = 'auth.branch_selection_required';\r\n\r\n/**\r\n * One selectable branch.\r\n *\r\n * `name` is optional because the current backend contract carries branch ids\r\n * only — see `readBranchSelectionRequired`. Modelling it as optional now means\r\n * the day the backend starts sending names, consumers get them without a\r\n * breaking change to this type.\r\n */\r\nexport interface BranchOption {\r\n readonly id: string;\r\n readonly name?: string | null;\r\n}\r\n\r\n/** Payload of a branch-selection-required response, normalized. */\r\nexport interface BranchSelectionRequired {\r\n readonly branches: readonly BranchOption[];\r\n}\r\n\r\n/**\r\n * Recognizes a branch-selection-required response and normalizes its payload,\r\n * or returns `null` for anything else — so callers branch on a value instead of\r\n * re-deriving the contract from status codes and body shapes.\r\n *\r\n * Reads three wire shapes, in this order of preference:\r\n *\r\n * 1. `branchOptions: [{ id, name }]` — the display metadata the Platform sends\r\n * alongside the ids. Preferred when present, because it is the only shape\r\n * that lets an operator tell the branches apart.\r\n * 2. `branches: [\"<guid>\"]` — the original contract. Still authoritative for\r\n * selection, and still enough on its own.\r\n * 3. `branches: [{ id, name }]` — tolerated since 0.3.0.\r\n *\r\n * Reading all three is what makes the rollout order irrelevant: a frontend on\r\n * this version works against a backend that has the metadata and against one\r\n * that does not, and neither side has to ship first.\r\n *\r\n * Entries without a usable id are dropped rather than rendered as blanks: an\r\n * unselectable option in a selection list is worse than a shorter list.\r\n */\r\nexport function readBranchSelectionRequired(error: unknown): BranchSelectionRequired | null {\r\n if (!(error instanceof HttpErrorResponse) || error.status !== 409) return null;\r\n\r\n const body = error.error as\r\n | { code?: unknown; branches?: unknown; branchOptions?: unknown }\r\n | null;\r\n if (!body || body.code !== BRANCH_SELECTION_REQUIRED_CODE) return null;\r\n\r\n // `branchOptions` só substitui os ids quando traz alguma opção utilizável —\r\n // uma lista vazia ou ilegível não pode apagar a lista de ids que funciona.\r\n const comMetadata = normalizeEntries(body.branchOptions);\r\n if (comMetadata.length > 0) return { branches: comMetadata };\r\n\r\n if (!Array.isArray(body.branches)) return null;\r\n return { branches: normalizeEntries(body.branches) };\r\n}\r\n\r\nfunction normalizeEntries(raw: unknown): BranchOption[] {\r\n if (!Array.isArray(raw)) return [];\r\n\r\n return raw\r\n .map((entry): BranchOption | null => {\r\n if (typeof entry === 'string') return entry ? { id: entry } : null;\r\n if (entry && typeof entry === 'object') {\r\n const { id, name } = entry as { id?: unknown; name?: unknown };\r\n if (typeof id === 'string' && id) {\r\n return { id, name: typeof name === 'string' ? name : null };\r\n }\r\n }\r\n return null;\r\n })\r\n .filter((branch): branch is BranchOption => branch !== null);\r\n}\r\n","import { ChangeDetectionStrategy, Component, input, output } from '@angular/core';\r\nimport { BranchOption } from './branch-selection.model';\r\n\r\nfunction normalizeName(name: string | null | undefined): string {\r\n return (name ?? '').trim().toLowerCase();\r\n}\r\n\r\n/**\r\n * Explicit branch picker for the multi-branch login step.\r\n *\r\n * Deliberately narrow: it receives options, renders them, and emits the id the\r\n * user actually chose. It does not call any backend, hold credentials, read\r\n * tokens, touch storage, or know which product it is rendering inside — the\r\n * flow around it belongs to the consumer, and the copy around it belongs to the\r\n * consumer's login page.\r\n *\r\n * **It never picks for the user.** When the backend answers\r\n * `auth.branch_selection_required` it has already refused to choose (no session\r\n * is created); a selector that quietly defaulted to the first option would put\r\n * the arbitrary choice back one layer up, in the place hardest to notice.\r\n *\r\n * Rendered as a radio group so the browser gives keyboard navigation, focus\r\n * management, and screen-reader semantics for free — a list of buttons would\r\n * need all three re-implemented, worse.\r\n */\r\n@Component({\r\n selector: 'orechio-branch-selector',\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n template: `\r\n @if (branches().length === 0) {\r\n <p class=\"orechio-branch-selector__empty\" role=\"status\">{{ emptyLabel() }}</p>\r\n } @else {\r\n <fieldset class=\"orechio-branch-selector\" [disabled]=\"busy()\">\r\n <legend>{{ legend() }}</legend>\r\n\r\n @for (branch of branches(); track branch.id; let index = $index) {\r\n <label class=\"orechio-branch-selector__option\">\r\n <input\r\n type=\"radio\"\r\n name=\"orechio-branch\"\r\n [value]=\"branch.id\"\r\n [checked]=\"chosen === branch.id\"\r\n (change)=\"choose(branch.id)\"\r\n />\r\n <span class=\"orechio-branch-selector__label\">{{ label(branch, index) }}</span>\r\n @if (showFragment(branch)) {\r\n <span class=\"orechio-branch-selector__hint\">{{ ' · ' + fragment(branch) }}</span>\r\n }\r\n </label>\r\n }\r\n\r\n <button\r\n type=\"button\"\r\n class=\"orechio-branch-selector__confirm\"\r\n [disabled]=\"!chosen || busy()\"\r\n (click)=\"confirm()\"\r\n >\r\n {{ confirmLabel() }}\r\n </button>\r\n </fieldset>\r\n }\r\n `,\r\n // Minimal layout only, no brand colour: the host's global CSS (e.g.\r\n // `input { width: 100% }`) must not be able to stretch the radios, and the host\r\n // can still override any of this.\r\n styles: `\r\n .orechio-branch-selector__option {\r\n display: flex;\r\n align-items: center;\r\n margin: 0.25rem 0;\r\n }\r\n\r\n .orechio-branch-selector__option input[type='radio'] {\r\n width: auto;\r\n height: auto;\r\n margin: 0 0.5rem 0 0;\r\n }\r\n\r\n .orechio-branch-selector__hint {\r\n font-size: 0.85em;\r\n opacity: 0.7;\r\n white-space: pre;\r\n }\r\n `,\r\n})\r\nexport class BranchSelector {\r\n /** Options to choose from. An empty list renders the empty state, never a silent no-op. */\r\n readonly branches = input.required<readonly BranchOption[]>();\r\n\r\n /** Blocks input while the consumer is completing the sign-in round-trip. */\r\n readonly busy = input(false);\r\n\r\n // Copy is injectable because wording belongs to the product, not to the\r\n // platform package — the defaults exist so a consumer can drop the component\r\n // in and iterate on the words later.\r\n readonly legend = input('Selecione a filial');\r\n readonly confirmLabel = input('Continuar');\r\n readonly emptyLabel = input('Nenhuma filial disponível para este usuário.');\r\n\r\n /** Emits only in response to a user action. */\r\n readonly selected = output<string>();\r\n\r\n protected chosen: string | null = null;\r\n\r\n protected choose(id: string): void {\r\n this.chosen = id;\r\n }\r\n\r\n protected confirm(): void {\r\n if (this.chosen) this.selected.emit(this.chosen);\r\n }\r\n\r\n /**\r\n * The name is the label whenever the contract carries one. The id fragment stays\r\n * as secondary text rather than disappearing: the domain allows two branches to\r\n * share a name, and when that happens the fragment is the only thing telling\r\n * them apart.\r\n *\r\n * Without a name — an older backend, or metadata the server could not compose —\r\n * the positional fallback keeps the list selectable. Two raw identifiers side by\r\n * side are technically a choice and practically a coin flip, which is exactly\r\n * why the name is preferred whenever it exists.\r\n */\r\n protected label(branch: BranchOption, index: number): string {\r\n return branch.name?.trim() ? branch.name : `Filial ${index + 1}`;\r\n }\r\n\r\n /**\r\n * The fragment exists only to tell apart branches that would otherwise read the\r\n * same, so it is rendered when another option carries the same name (trimmed,\r\n * case-insensitive) — or when there is no name at all, where the positional\r\n * \"Filial N\" fallback gives the user nothing else to go on.\r\n */\r\n protected showFragment(branch: BranchOption): boolean {\r\n const key = normalizeName(branch.name);\r\n if (!key) return true;\r\n return this.branches().some((other) => other !== branch && normalizeName(other.name) === key);\r\n }\r\n\r\n /** Fragment of the id, shown beside the label to disambiguate repeated names. */\r\n protected fragment(branch: BranchOption): string {\r\n return branch.id.slice(0, 8);\r\n }\r\n}\r\n","import { inject, Injectable } from '@angular/core';\r\nimport { Router } from '@angular/router';\r\nimport { Observable, ReplaySubject, catchError, finalize, share, throwError } from 'rxjs';\r\nimport { PLATFORM_ANGULAR_CONFIG } from '../config/platform-angular-config';\r\nimport { SessionStore } from '../session/session-store';\r\nimport { SessionUser } from '../session/session-user.model';\r\nimport { AuthClient } from './auth-client';\r\n\r\n/**\r\n * INTERNAL. Not exported from public-api.ts — consumers never construct or configure this\r\n * directly; it exists purely to back `sessionExpiryInterceptor`'s `'refresh-then-redirect'`\r\n * strategy (WEB-0.2, ADR-016 §\"WEB-0.2 note\").\r\n *\r\n * Deduplicates concurrent `AuthClient.refresh()` calls that originate from the SAME Angular\r\n * application context (one injector, i.e. one running app/tab). Before this existed, every\r\n * 401'd request independently called `authClient.refresh()`, and since that method returns a\r\n * cold Observable (`http.post(...)`, no multicasting), N simultaneous 401s produced N\r\n * independent `POST /refresh` calls — proven by the characterization test in\r\n * `provide-platform-angular-http.spec.ts`.\r\n *\r\n * This is defense-in-depth ONLY. The Platform already tolerates concurrent legitimate refresh\r\n * rotations correctly and safely (PLATFORM-REFRESH-1/1A/1B, ADR-016's retry leeway) — this class\r\n * exists to cut needless network traffic, cryptographic work, and rate-limit pressure on the\r\n * Platform, not to be the thing that makes concurrent refreshes safe. It does not, and does not\r\n * need to, coordinate across browser tabs/windows or other Angular application instances — see\r\n * the \"Multi-tab\" note on `sessionExpiryInterceptor` for why that's explicitly out of scope here\r\n * and handled server-side instead.\r\n */\r\n@Injectable({ providedIn: 'root' })\r\nexport class RefreshCoordinator {\r\n private readonly authClient = inject(AuthClient);\r\n private readonly session = inject(SessionStore);\r\n private readonly router = inject(Router);\r\n private readonly config = inject(PLATFORM_ANGULAR_CONFIG);\r\n\r\n private inFlight$: Observable<SessionUser> | null = null;\r\n\r\n /**\r\n * Returns the single in-flight refresh operation if one is already running, or starts a new\r\n * one. Every caller — first or piggybacking — gets the SAME underlying HTTP call's outcome.\r\n *\r\n * Semantics (WEB-0.2):\r\n * - Single-flight, not a permanent cache: once the shared operation settles (success or\r\n * error), the reference is cleared (`finalize`, placed BEFORE `share()` so it fires exactly\r\n * once per underlying HTTP call, not once per subscriber — see the `share()` config note\r\n * below). The next call to `refresh()` after that always starts a genuinely new request.\r\n * - On failure, the session is cleared and the browser is navigated to the login route\r\n * exactly once, regardless of how many callers are waiting — not once per waiting caller.\r\n * Each caller still receives its own error notification (multicast by `share()`) and is\r\n * responsible for propagating whatever error IT needs to its own subscriber (see\r\n * `sessionExpiryInterceptor`, which re-throws the ORIGINAL 401 to the request that\r\n * triggered it, not this refresh failure).\r\n * - `resetOnRefCountZero: false` is load-bearing: if one waiting caller unsubscribes (e.g. a\r\n * cancelled navigation), the still-in-flight HTTP request for the OTHER callers must not be\r\n * aborted. Angular's `HttpClient` cancels the underlying network request when its Observable\r\n * loses its last subscriber — without this flag, one caller going away could silently\r\n * cancel the refresh everyone else is still waiting on.\r\n * - `resetOnError`/`resetOnComplete` are left `false` too: this class never reuses a settled\r\n * `inFlight$` reference across cycles (the field is always nulled and rebuilt from scratch\r\n * by `finalize`), so `share()` itself never needs to reset its own internal connector.\r\n */\r\n refresh(): Observable<SessionUser> {\r\n if (this.inFlight$) {\r\n return this.inFlight$;\r\n }\r\n\r\n this.inFlight$ = this.authClient.refresh().pipe(\r\n catchError((error: unknown) => {\r\n this.session.clear();\r\n void this.router.navigateByUrl(this.config.loginRoute ?? '/login');\r\n return throwError(() => error);\r\n }),\r\n finalize(() => {\r\n this.inFlight$ = null;\r\n }),\r\n share({\r\n connector: () => new ReplaySubject<SessionUser>(1),\r\n resetOnError: false,\r\n resetOnComplete: false,\r\n resetOnRefCountZero: false,\r\n }),\r\n );\r\n\r\n return this.inFlight$;\r\n }\r\n}\r\n","import {\r\n HttpErrorResponse,\r\n HttpInterceptorFn,\r\n provideHttpClient,\r\n withInterceptors,\r\n withXsrfConfiguration,\r\n HttpFeature,\r\n HttpFeatureKind,\r\n} from '@angular/common/http';\r\nimport { inject } from '@angular/core';\r\nimport { Router } from '@angular/router';\r\nimport { catchError, switchMap, throwError } from 'rxjs';\r\nimport { PLATFORM_ANGULAR_CONFIG, PlatformAngularAuthPaths } from '../config/platform-angular-config';\r\nimport { RefreshCoordinator } from '../auth/refresh-coordinator';\r\nimport { SessionStore } from '../session/session-store';\r\n\r\nfunction isApiRequest(url: string, apiBaseUrl: string): boolean {\r\n return url === apiBaseUrl || url.startsWith(`${apiBaseUrl}/`);\r\n}\r\n\r\nfunction isAuthEndpoint(url: string, apiBaseUrl: string, auth: PlatformAngularAuthPaths): boolean {\r\n const authPaths = [auth.login, auth.refresh, auth.logout, auth.me].filter(\r\n (p): p is string => !!p,\r\n );\r\n return authPaths.some((p) => url === `${apiBaseUrl}${p}`);\r\n}\r\n\r\n/**\r\n * Forces `withCredentials: true` on same-origin API requests, so the BFF's\r\n * HttpOnly session cookie is sent. Proven identically in both consumers.\r\n * XSRF header injection is intentionally NOT duplicated here — Angular's\r\n * own `withXsrfConfiguration` (registered by `providePlatformAngularHttp`)\r\n * already does that. Portal's local interceptor previously re-implemented\r\n * XSRF header injection manually alongside `withXsrfConfiguration`; that\r\n * was redundant, not a pattern to preserve.\r\n */\r\nexport const credentialsInterceptor: HttpInterceptorFn = (req, next) => {\r\n const { apiBaseUrl } = inject(PLATFORM_ANGULAR_CONFIG);\r\n if (!isApiRequest(req.url, apiBaseUrl)) {\r\n return next(req);\r\n }\r\n return next(req.clone({ withCredentials: true }));\r\n};\r\n\r\n/**\r\n * On 401 from a non-auth API request: 'redirect' (default) clears the\r\n * session and navigates to the login route immediately (Store's proven\r\n * behavior). 'refresh-then-redirect' attempts one silent refresh first and\r\n * retries the original request on success, falling back to redirect\r\n * (Portal's proven behavior). Configured via\r\n * PlatformAngularConfig.unauthorizedStrategy.\r\n *\r\n * WEB-0.2: the 'refresh-then-redirect' path routes through `RefreshCoordinator`\r\n * instead of calling `AuthClient.refresh()` directly — every 401 handled by\r\n * the SAME running app shares one in-flight refresh instead of each request\r\n * triggering its own. The 'redirect' strategy is untouched: it never calls\r\n * refresh at all, so there is nothing to coordinate (see the config note on\r\n * `PlatformAngularConfig.unauthorizedStrategy`). On refresh failure, the\r\n * session-clear/navigate side effect now happens once inside the coordinator\r\n * (shared across every waiting caller) rather than once per waiting caller;\r\n * each caller still receives the ORIGINAL 401 error, not a refresh-specific\r\n * one, exactly as before. Multi-tab/multi-window coordination is explicitly\r\n * NOT handled here — see `RefreshCoordinator`'s doc comment — the Platform's\r\n * own server-side retry leeway (ADR-016) is what makes concurrent refreshes\r\n * from independent contexts safe.\r\n */\r\nexport const sessionExpiryInterceptor: HttpInterceptorFn = (req, next) => {\r\n const config = inject(PLATFORM_ANGULAR_CONFIG);\r\n const session = inject(SessionStore);\r\n const router = inject(Router);\r\n const refreshCoordinator = inject(RefreshCoordinator);\r\n\r\n if (\r\n !isApiRequest(req.url, config.apiBaseUrl) ||\r\n isAuthEndpoint(req.url, config.apiBaseUrl, config.auth)\r\n ) {\r\n return next(req);\r\n }\r\n\r\n return next(req).pipe(\r\n catchError((error: unknown) => {\r\n if (!(error instanceof HttpErrorResponse) || error.status !== 401) {\r\n return throwError(() => error);\r\n }\r\n\r\n if (config.unauthorizedStrategy !== 'refresh-then-redirect') {\r\n session.clear();\r\n void router.navigateByUrl(config.loginRoute ?? '/login');\r\n return throwError(() => error);\r\n }\r\n\r\n return refreshCoordinator.refresh().pipe(\r\n switchMap(() => next(req)),\r\n // The coordinator already cleared the session and navigated once for\r\n // this shared failure (see RefreshCoordinator.refresh()) — this\r\n // request still needs to fail with its OWN original 401, not the\r\n // refresh's error, exactly as before WEB-0.2.\r\n catchError(() => throwError(() => error)),\r\n );\r\n }),\r\n );\r\n};\r\n\r\n/**\r\n * Bundles provideHttpClient with the XSRF configuration and interceptors\r\n * proven necessary by both consumers. Cookie/header names default to\r\n * 'XSRF-TOKEN'/'X-XSRF-TOKEN' (identical in both consumers today) but\r\n * remain overridable via config. Consumers add their own extra\r\n * provideHttpClient features (e.g. Portal's withXhr()) by calling\r\n * provideHttpClient(...) separately — this function only owns the concerns\r\n * proven common to both.\r\n */\r\nexport function providePlatformAngularHttp(...extraFeatures: HttpFeature<HttpFeatureKind>[]) {\r\n return provideHttpClient(\r\n withXsrfConfiguration({\r\n cookieName: 'XSRF-TOKEN',\r\n headerName: 'X-XSRF-TOKEN',\r\n }),\r\n withInterceptors([credentialsInterceptor, sessionExpiryInterceptor]),\r\n ...extraFeatures,\r\n );\r\n}\r\n","/**\n * Generic capability map: a flat set of boolean feature flags fetched once\n * from the BFF (Portal's proven pattern — GET .../capabilities returning a\n * flat boolean-keyed object). Deliberately a generic `Record<string,\n * boolean>` rather than a typed interface enumerating every field: Portal's\n * capability set already has 17 product-specific fields and Store will\n * grow its own, unrelated set. A typed interface would require a package\n * release for every new product capability (see ADR-010 §18 — evolution\n * cadence mismatch is a real argument against sharing). Each product\n * defines its own capability keys locally and reads them through this map.\n *\n * UX ONLY. This does not replace server-side authorization. Every consumer\n * MUST continue to handle 403 from the actual mutating/reading endpoint —\n * capability checks here are a consultive pre-check, exactly as already\n * documented in Portal's application.service.ts before this extraction.\n */\nexport type CapabilityMap = Readonly<Record<string, boolean>>;\n\nexport function hasCapability(capabilities: CapabilityMap | null | undefined, key: string): boolean {\n return capabilities?.[key] === true;\n}\n","import { Directive, Input, TemplateRef, ViewContainerRef, inject } from '@angular/core';\nimport { CapabilityMap, hasCapability } from './capability.model';\n\n/**\n * Structural directive gating template content on a capability key from a\n * consumer-supplied CapabilityMap. Net-new (neither consumer had factored\n * their proven inline `@if (capabilities()?.x)` pattern into a directive),\n * but grounded in Portal's validated usage — see capability.model.ts.\n *\n * UX ONLY — see capability.model.ts. Hiding an element here does not\n * authorize anything; the backend re-validates on every mutating call.\n *\n * Usage: `<button *orxCapability=\"capabilities(); has: 'manageTenants'\">`\n */\n@Directive({\n selector: '[orxCapability]',\n standalone: true,\n})\nexport class CapabilityDirective {\n private readonly templateRef = inject(TemplateRef<unknown>);\n private readonly viewContainer = inject(ViewContainerRef);\n\n private map: CapabilityMap | null | undefined;\n private key: string | undefined;\n private hasView = false;\n\n @Input()\n set orxCapability(map: CapabilityMap | null | undefined) {\n this.map = map;\n this.render();\n }\n\n @Input('orxCapabilityHas')\n set has(key: string) {\n this.key = key;\n this.render();\n }\n\n private render(): void {\n const shouldShow = !!this.key && hasCapability(this.map, this.key);\n if (shouldShow && !this.hasView) {\n this.viewContainer.createEmbeddedView(this.templateRef);\n this.hasView = true;\n } else if (!shouldShow && this.hasView) {\n this.viewContainer.clear();\n this.hasView = false;\n }\n }\n}\n","import { HttpErrorResponse } from '@angular/common/http';\r\n\r\n/**\r\n * Canonical error shape, generalized from Store's already-proven\r\n * ErroApresentavel/normalizarErroApi (the more mature of the two consumers'\r\n * error handling — Portal had no typed contract at all, just five\r\n * duplicated `error.error.code` reads). `message` is optional: Portal never\r\n * received a server-supplied message field, only `code`, and resolves\r\n * user-facing text locally — so this contract cannot require it.\r\n *\r\n * `correlation_id` (API-1, ADR-017 §3.3): added after confirming directly\r\n * from Orechio.Platform's `ProblemResponses.cs` that it IS present in the\r\n * JSON body of every error response the Platform host produces (not just\r\n * the `X-Correlation-ID` response header), and that Portal's BFF relays\r\n * that body byte-for-byte, unreshaped, for every typed endpoint. The\r\n * earlier NG-1-era absence was accurate for what it tested at the time but\r\n * incomplete relative to Platform's actual wire contract. Optional: not\r\n * every error path in every consumer emits this field today (Store's own\r\n * narrower auth/CSRF error shapes don't) — its absence must not be treated\r\n * as a parse failure.\r\n *\r\n * Domain-specific error codes (e.g. 'access.platform_only',\r\n * 'tenant.not_found', 'csrf.invalid') are NOT enumerated here — `code`\r\n * stays a plain string so any code survives normalization unmodified and\r\n * remains identifiable by callers, per Passo 28.\r\n */\r\nexport interface ProblemDetails {\r\n code: string;\r\n message?: string;\r\n status: number;\r\n required?: string;\r\n correlationId?: string;\r\n}\r\n\r\n/**\r\n * Normalizes an HttpErrorResponse into ProblemDetails. Mirrors Store's\r\n * normalizarErroApi exactly, generalized to make `message` optional and to\r\n * accept an HttpErrorResponse without any consumer-specific fallback\r\n * message map (each product keeps its own code→message copy locally —\r\n * that's product-specific text, not shared infrastructure).\r\n */\r\nexport function normalizeProblemDetails(error: HttpErrorResponse): ProblemDetails {\r\n const body = error.error as Record<string, unknown> | null | undefined;\r\n const code = typeof body?.['code'] === 'string' ? (body['code'] as string) : `http.${error.status || 0}`;\r\n const message = typeof body?.['message'] === 'string' ? (body['message'] as string) : undefined;\r\n const required = typeof body?.['required'] === 'string' ? (body['required'] as string) : undefined;\r\n // Wire field is `correlation_id` (snake_case, ADR-017 §3) — kept as-is on the wire,\r\n // exposed as `correlationId` here per TS/Angular convention (same server-side pattern:\r\n // OrechioProblemDetails.CorrelationId is [JsonPropertyName(\"correlation_id\")]).\r\n const correlationId =\r\n typeof body?.['correlation_id'] === 'string' ? (body['correlation_id'] as string) : undefined;\r\n\r\n return {\r\n code,\r\n message,\r\n status: error.status,\r\n ...(required ? { required } : {}),\r\n ...(correlationId ? { correlationId } : {}),\r\n };\r\n}\r\n","/*\r\n * Public API Surface of @orechio/platform-angular\r\n */\r\n\r\nexport type { PlatformAngularConfig, PlatformAngularAuthPaths } from './lib/config/platform-angular-config';\r\nexport { PLATFORM_ANGULAR_CONFIG, providePlatformAngularConfig } from './lib/config/platform-angular-config';\r\n\r\nexport type { SessionUser } from './lib/session/session-user.model';\r\nexport { SessionStore } from './lib/session/session-store';\r\n\r\nexport { AuthClient } from './lib/auth/auth-client';\r\n\r\nexport type { BranchOption, BranchSelectionRequired } from './lib/auth/branch-selection.model';\r\nexport {\r\n BRANCH_SELECTION_REQUIRED_CODE,\r\n readBranchSelectionRequired,\r\n} from './lib/auth/branch-selection.model';\r\nexport { BranchSelector } from './lib/auth/branch-selector';\r\n\r\nexport { credentialsInterceptor, sessionExpiryInterceptor, providePlatformAngularHttp } from './lib/http/provide-platform-angular-http';\r\n\r\nexport type { CapabilityMap } from './lib/capabilities/capability.model';\r\nexport { hasCapability } from './lib/capabilities/capability.model';\r\nexport { CapabilityDirective } from './lib/capabilities/capability.directive';\r\n\r\nexport type { ProblemDetails } from './lib/problem-details/problem-details.model';\r\nexport { normalizeProblemDetails } from './lib/problem-details/problem-details.model';\r\n\r\nexport type { PagedResult } from './lib/pagination/paged-result.model';\r\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -239,6 +239,13 @@ declare class BranchSelector {
|
|
|
239
239
|
* why the name is preferred whenever it exists.
|
|
240
240
|
*/
|
|
241
241
|
protected label(branch: BranchOption, index: number): string;
|
|
242
|
+
/**
|
|
243
|
+
* The fragment exists only to tell apart branches that would otherwise read the
|
|
244
|
+
* same, so it is rendered when another option carries the same name (trimmed,
|
|
245
|
+
* case-insensitive) — or when there is no name at all, where the positional
|
|
246
|
+
* "Filial N" fallback gives the user nothing else to go on.
|
|
247
|
+
*/
|
|
248
|
+
protected showFragment(branch: BranchOption): boolean;
|
|
242
249
|
/** Fragment of the id, shown beside the label to disambiguate repeated names. */
|
|
243
250
|
protected fragment(branch: BranchOption): string;
|
|
244
251
|
static ɵfac: i0.ɵɵFactoryDeclaration<BranchSelector, never>;
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"orechio-platform-angular-billing-billing-format-DFY2U19Y.mjs","sources":["../../../projects/platform-angular/billing/src/billing-client.ts","../../../projects/platform-angular/billing/src/billing-errors.ts","../../../projects/platform-angular/billing/src/billing-error.component.ts","../../../projects/platform-angular/billing/src/billing-format.ts"],"sourcesContent":["import { HttpClient, HttpErrorResponse, HttpParams } from '@angular/common/http';\r\nimport { Injectable, inject } from '@angular/core';\r\nimport { Observable, catchError, throwError } from 'rxjs';\r\nimport { ProblemDetails, normalizeProblemDetails } from '@orechio/platform-angular';\r\nimport { ORECHIO_BILLING_CONFIG } from './billing-config';\r\nimport {\r\n BillingAddressRequest,\r\n BillingContactRequest,\r\n BillingCycle,\r\n BillingProfile,\r\n CheckoutResult,\r\n CreateBillingProfileRequest,\r\n InvoiceDetail,\r\n InvoicesPage,\r\n Plan,\r\n PricingPreview,\r\n RefreshedPayment,\r\n Subscription,\r\n} from './billing-models';\r\n\r\n/**\r\n * O cliente do BFF de cobrança (`Orechio.Platform.Bff.Account` 0.2.0,\r\n * `MapOrechioBillingEndpoints`). Same-origin, só com o BFF do host — mesmo desenho do\r\n * `AccountClient`, e o erro sai pelo mesmo `normalizeProblemDetails`.\r\n */\r\n@Injectable({ providedIn: 'root' })\r\nexport class BillingClient {\r\n private readonly http = inject(HttpClient);\r\n private readonly config = inject(ORECHIO_BILLING_CONFIG);\r\n\r\n getSubscription(): Observable<Subscription> {\r\n return this.falha(this.http.get<Subscription>(this.url('/subscription')));\r\n }\r\n\r\n getProfile(): Observable<BillingProfile> {\r\n return this.falha(this.http.get<BillingProfile>(this.url('/profile')));\r\n }\r\n\r\n createProfile(request: CreateBillingProfileRequest): Observable<{ id: string }> {\r\n return this.falha(this.http.post<{ id: string }>(this.url('/profile'), request));\r\n }\r\n\r\n updateProfileContact(request: BillingContactRequest): Observable<void> {\r\n return this.falha(this.http.patch<void>(this.url('/profile/contact'), request));\r\n }\r\n\r\n updateProfileAddress(request: BillingAddressRequest): Observable<void> {\r\n return this.falha(this.http.patch<void>(this.url('/profile/address'), request));\r\n }\r\n\r\n listPlans(): Observable<Plan[]> {\r\n return this.falha(this.http.get<Plan[]>(this.url('/plans')));\r\n }\r\n\r\n previewPricing(planId: string, branchCount: number, cycle: BillingCycle): Observable<PricingPreview> {\r\n const params = new HttpParams({ fromObject: { planId, branchCount, cycle } });\r\n return this.falha(this.http.get<PricingPreview>(this.url('/pricing/preview'), { params }));\r\n }\r\n\r\n /**\r\n * Checkout PIX. O corpo é o `CheckoutRequest` da Platform (`cycle`, `method`), sem\r\n * `applicationId`: com o token de produto, a Platform usa o app do token.\r\n */\r\n checkout(cycle: BillingCycle): Observable<CheckoutResult> {\r\n return this.falha(this.http.post<CheckoutResult>(this.url('/checkout'), { cycle, method: 'pix' }));\r\n }\r\n\r\n listInvoices(page = 1, pageSize = 20): Observable<InvoicesPage> {\r\n const params = new HttpParams({ fromObject: { page, pageSize } });\r\n return this.falha(this.http.get<InvoicesPage>(this.url('/invoices'), { params }));\r\n }\r\n\r\n getInvoice(id: string): Observable<InvoiceDetail> {\r\n return this.falha(this.http.get<InvoiceDetail>(this.url(`/invoices/${encodeURIComponent(id)}`)));\r\n }\r\n\r\n refreshInvoicePix(id: string): Observable<RefreshedPayment> {\r\n return this.falha(this.http.post<RefreshedPayment>(this.url(`/invoices/${encodeURIComponent(id)}/refresh`), null));\r\n }\r\n\r\n private url(path: string): string {\r\n return `${this.config.basePath}${path}`;\r\n }\r\n\r\n private falha<T>(chamada: Observable<T>): Observable<T> {\r\n return chamada.pipe(\r\n catchError((error: HttpErrorResponse) =>\r\n throwError((): ProblemDetails => normalizeProblemDetails(error)),\r\n ),\r\n );\r\n }\r\n}\r\n","import { ProblemDetails } from '@orechio/platform-angular';\r\n\r\nexport interface MensagemDeErro {\r\n texto: string;\r\n /** Só para code desconhecido: é o que o suporte cruza com o log dos dois lados. */\r\n correlationId?: string;\r\n}\r\n\r\nconst ENDERECO_INCOMPLETO = 'Preencha o endereço completo.';\r\nconst DOCUMENTO_INVALIDO = 'CPF ou CNPJ inválido.';\r\nconst DOCUMENTO_TRAVADO =\r\n 'O documento e o nome já foram usados em uma cobrança e não podem mais ser alterados.';\r\nconst PLANO_INDISPONIVEL = 'O plano da assinatura não está disponível. Fale com o suporte.';\r\nconst SO_O_TITULAR = 'Só o titular da conta pode ver e pagar a cobrança.';\r\n\r\n/**\r\n * Os codes que as rotas de cobrança da Platform e do BFF emitem de fato\r\n * (`Commerce*Endpoints`, handlers de `Billing/`, value objects de perfil,\r\n * `PermissionAuthorization`, `Bff.Account`).\r\n */\r\nconst MENSAGENS: Record<string, string> = {\r\n 'session.missing': 'Sua sessão expirou. Entre novamente para continuar.',\r\n 'platform.unavailable':\r\n 'O serviço de cobrança está indisponível no momento. Tente novamente em instantes.',\r\n 'http.0': 'Sem conexão com o servidor. Verifique sua internet e tente novamente.',\r\n rate_limit_exceeded: 'Muitas tentativas. Aguarde alguns minutos e tente novamente.',\r\n\r\n 'access.platform_only': SO_O_TITULAR,\r\n 'permission.denied': SO_O_TITULAR,\r\n\r\n 'subscription.not_found': 'Nenhuma assinatura encontrada para esta conta.',\r\n\r\n 'billing_profile.not_found': 'Cadastre os dados de cobrança antes de pagar.',\r\n 'billing_profile.already_exists': 'Os dados de cobrança já estão cadastrados.',\r\n 'billing_profile.legal_name_required': 'Informe o nome completo ou a razão social.',\r\n 'billing_profile.billing_email_required': 'Informe o e-mail de cobrança.',\r\n 'billing_profile.billing_email_invalid': 'Informe um e-mail de cobrança válido.',\r\n 'billing_profile.mobile_phone_too_long': 'O celular informado é longo demais.',\r\n 'billing_profile.document_type_locked': DOCUMENTO_TRAVADO,\r\n 'billing_profile.document_number_locked': DOCUMENTO_TRAVADO,\r\n 'billing_profile.legal_name_locked': DOCUMENTO_TRAVADO,\r\n 'tax_document.value_required': 'Informe o CPF ou o CNPJ.',\r\n 'tax_document.length_invalid': DOCUMENTO_INVALIDO,\r\n 'tax_document.checksum_invalid': DOCUMENTO_INVALIDO,\r\n 'address.postal_code_invalid': 'CEP inválido.',\r\n 'address.state_invalid': 'UF inválida. Use a sigla do estado, como SP.',\r\n 'address.street_required': ENDERECO_INCOMPLETO,\r\n 'address.number_required': ENDERECO_INCOMPLETO,\r\n 'address.neighborhood_required': ENDERECO_INCOMPLETO,\r\n 'address.city_required': ENDERECO_INCOMPLETO,\r\n\r\n 'checkout.subscription_not_eligible':\r\n 'Não há pagamento de assinatura a fazer agora. Veja as faturas em aberto.',\r\n 'checkout.open_invoice_exists':\r\n 'Já existe uma fatura em aberto. Pague a fatura em aberto em vez de gerar uma nova cobrança.',\r\n 'checkout.pending_other_method':\r\n 'Já existe uma cobrança em aberto por outro meio de pagamento. Veja as faturas.',\r\n 'checkout.credit_card_not_supported': 'Pagamento por cartão ainda não está disponível.',\r\n 'checkout.invoice_in_inconsistent_state':\r\n 'A cobrança anterior ficou incompleta. Tente novamente em alguns minutos.',\r\n 'billing.cycle_invalid': 'Escolha um ciclo de cobrança válido.',\r\n 'pricing_preview.cycle_invalid': 'Escolha um ciclo de cobrança válido.',\r\n 'plan.not_found': PLANO_INDISPONIVEL,\r\n 'plan.not_available': PLANO_INDISPONIVEL,\r\n 'subscription.plan_inactive': PLANO_INDISPONIVEL,\r\n\r\n 'invoice.not_found': 'Fatura não encontrada.',\r\n 'invoice.payment_unavailable':\r\n 'Esta cobrança não está mais disponível para pagamento. Fale com o suporte.',\r\n 'invoice.refresh_not_allowed': 'Esta fatura não está mais em aberto.',\r\n 'invoice.refresh_pix_only': 'Só é possível gerar QR para faturas PIX.',\r\n 'invoice.no_external_payment':\r\n 'Esta fatura ainda não tem cobrança gerada. Tente novamente em alguns minutos.',\r\n};\r\n\r\nconst GENERICA =\r\n 'Não foi possível concluir a operação. Tente novamente; se continuar, informe o código de atendimento ao suporte.';\r\n\r\n/**\r\n * O texto de tela para um erro do BFF, escolhido por `code`. O corpo do erro (`message`) nunca\r\n * vira texto: ele é escrito para log, não para o cliente.\r\n */\r\nexport function mensagemDeErro(problema: ProblemDetails): MensagemDeErro {\r\n const conhecida = MENSAGENS[problema.code];\r\n if (conhecida) return { texto: conhecida };\r\n\r\n return problema.correlationId\r\n ? { texto: GENERICA, correlationId: problema.correlationId }\r\n : { texto: GENERICA };\r\n}\r\n","import { ChangeDetectionStrategy, Component, computed, input } from '@angular/core';\r\nimport { ProblemDetails } from '@orechio/platform-angular';\r\nimport { mensagemDeErro } from './billing-errors';\r\n\r\n@Component({\r\n selector: 'orechio-billing-error',\r\n standalone: true,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n template: `\r\n <div class=\"orechio-billing__error\" role=\"alert\">\r\n <p>{{ mensagem().texto }}</p>\r\n @if (mensagem().correlationId; as id) {\r\n <p>Código de atendimento: <code>{{ id }}</code></p>\r\n }\r\n </div>\r\n `,\r\n styles: `.orechio-billing__error { color: var(--orechio-color-danger, #b00020); }`,\r\n})\r\nexport class BillingErrorComponent {\r\n readonly problema = input.required<ProblemDetails>();\r\n protected readonly mensagem = computed(() => mensagemDeErro(this.problema()));\r\n}\r\n","import { InvoiceStatus } from './billing-models';\r\n\r\nconst MOEDA = new Intl.NumberFormat('pt-BR', { style: 'currency', currency: 'BRL' });\r\n\r\nexport function moeda(valor: number): string {\r\n return MOEDA.format(valor);\r\n}\r\n\r\nexport function data(iso: string | null | undefined): string {\r\n return iso ? new Date(iso).toLocaleDateString('pt-BR') : '';\r\n}\r\n\r\nconst FUSO_DE_BRASILIA = 'America/Sao_Paulo';\r\nconst SUFIXO_DE_BRASILIA = '(horário de Brasília)';\r\n\r\n/** \"AAAA-MM-DD HH:mm[:ss]\" — o `expirationDate` do ASAAS: sem fuso, já em horário de Brasília. */\r\nconst SEM_FUSO = /^(\\d{4})-(\\d{2})-(\\d{2})[ T](\\d{2}):(\\d{2})(?::(\\d{2})(?:\\.\\d+)?)?$/;\r\n/** O mesmo, com fuso explícito: `Z` ou `±hh:mm` / `±hhmm`. */\r\nconst COM_FUSO = /^\\d{4}-\\d{2}-\\d{2}[ T]\\d{2}:\\d{2}(?::\\d{2}(?:\\.\\d+)?)?(?:Z|[+-]\\d{2}:?\\d{2})$/i;\r\n\r\n/**\r\n * A validade do PIX para a tela: \"02/10/2026 às 23:59 (horário de Brasília)\".\r\n *\r\n * O valor do ASAAS NÃO passa por `new Date()`: sem fuso, o construtor o leria no fuso do\r\n * navegador e a hora exibida se deslocaria. Ele é formatado por PARTES DO TEXTO. Só o valor\r\n * com fuso explícito é convertido (para America/Sao_Paulo). Formato não reconhecido volta como\r\n * veio — nunca \"Invalid Date\".\r\n */\r\nexport function validadeDoPix(valor: string): string {\r\n const texto = valor.trim();\r\n\r\n const semFuso = SEM_FUSO.exec(texto);\r\n if (semFuso) {\r\n const [, ano, mes, dia, hora, minuto] = semFuso;\r\n return dataValida(+ano, +mes, +dia, +hora, +minuto)\r\n ? `${dia}/${mes}/${ano} às ${hora}:${minuto} ${SUFIXO_DE_BRASILIA}`\r\n : valor;\r\n }\r\n\r\n if (COM_FUSO.test(texto)) {\r\n const instante = new Date(texto.replace(' ', 'T'));\r\n if (Number.isNaN(instante.getTime())) return valor;\r\n\r\n const partes = Object.fromEntries(\r\n new Intl.DateTimeFormat('pt-BR', {\r\n timeZone: FUSO_DE_BRASILIA,\r\n day: '2-digit',\r\n month: '2-digit',\r\n year: 'numeric',\r\n hour: '2-digit',\r\n minute: '2-digit',\r\n hourCycle: 'h23',\r\n })\r\n .formatToParts(instante)\r\n .map((parte) => [parte.type, parte.value]),\r\n );\r\n return `${partes['day']}/${partes['month']}/${partes['year']} às ${partes['hour']}:${partes['minute']} ${SUFIXO_DE_BRASILIA}`;\r\n }\r\n\r\n return valor;\r\n}\r\n\r\nfunction dataValida(ano: number, mes: number, dia: number, hora: number, minuto: number): boolean {\r\n if (mes < 1 || mes > 12 || hora > 23 || minuto > 59 || dia < 1) return false;\r\n return dia <= new Date(Date.UTC(ano, mes, 0)).getUTCDate();\r\n}\r\n\r\n// ── máscaras (perfil de cobrança) ────────────────────────────────────────────\r\n\r\nexport function soDigitos(valor: string): string {\r\n return valor.replace(/\\D/g, '');\r\n}\r\n\r\n/** Junta os grupos de dígitos com os separadores, parando no primeiro grupo vazio. */\r\nfunction agrupar(digitos: string, grupos: readonly number[], separadores: readonly string[]): string {\r\n let saida = '';\r\n let posicao = 0;\r\n for (let i = 0; i < grupos.length; i++) {\r\n const grupo = digitos.slice(posicao, posicao + grupos[i]);\r\n if (!grupo) break;\r\n saida += (i === 0 ? '' : separadores[i - 1]) + grupo;\r\n posicao += grupos[i];\r\n }\r\n return saida;\r\n}\r\n\r\n/** CPF `000.000.000-00`, progressivo enquanto se digita. */\r\nexport function mascararCpf(valor: string): string {\r\n return agrupar(soDigitos(valor).slice(0, 11), [3, 3, 3, 2], ['.', '.', '-']);\r\n}\r\n\r\n/** CNPJ `00.000.000/0000-00`, progressivo enquanto se digita. */\r\nexport function mascararCnpj(valor: string): string {\r\n return agrupar(soDigitos(valor).slice(0, 14), [2, 3, 3, 4, 2], ['.', '.', '/', '-']);\r\n}\r\n\r\n/** O documento conforme o tipo escolhido. */\r\nexport function mascararDocumento(tipo: 'Cpf' | 'Cnpj', valor: string): string {\r\n return tipo === 'Cpf' ? mascararCpf(valor) : mascararCnpj(valor);\r\n}\r\n\r\n/** CEP `00000-000`. */\r\nexport function mascararCep(valor: string): string {\r\n return agrupar(soDigitos(valor).slice(0, 8), [5, 3], ['-']);\r\n}\r\n\r\n/** Telefone `(00) 0000-0000` (10 dígitos) ou celular `(00) 00000-0000` (11). */\r\nexport function mascararTelefone(valor: string): string {\r\n const digitos = soDigitos(valor).slice(0, 11);\r\n if (!digitos) return '';\r\n\r\n const ddd = digitos.slice(0, 2);\r\n if (digitos.length <= 2) return `(${ddd}`;\r\n\r\n const numero = digitos.slice(2);\r\n const tamanhoDoPrimeiroBloco = digitos.length > 10 ? 5 : 4;\r\n const corpo =\r\n numero.length > tamanhoDoPrimeiroBloco\r\n ? `${numero.slice(0, tamanhoDoPrimeiroBloco)}-${numero.slice(tamanhoDoPrimeiroBloco)}`\r\n : numero;\r\n return `(${ddd}) ${corpo}`;\r\n}\r\n\r\nexport const STATUS_DA_FATURA: Record<InvoiceStatus, string> = {\r\n Created: 'Em emissão',\r\n Pending: 'Em aberto',\r\n Paid: 'Paga',\r\n Overdue: 'Vencida',\r\n Refunded: 'Estornada',\r\n Cancelled: 'Cancelada',\r\n};\r\n\r\nexport const ESTILO_BASE = `\r\n .orechio-billing {\r\n display: flex;\r\n flex-direction: column;\r\n gap: var(--orechio-space-3, 0.75rem);\r\n max-width: 40rem;\r\n margin: 0 auto;\r\n padding: var(--orechio-space-6, 1.5rem);\r\n color: var(--orechio-color-text, inherit);\r\n font-family: var(--orechio-font-body, inherit);\r\n }\r\n .orechio-billing form { display: flex; flex-direction: column; gap: var(--orechio-space-2, 0.5rem); }\r\n .orechio-billing__muted { color: var(--orechio-color-text-muted, #666); }\r\n .orechio-billing__error { color: var(--orechio-color-danger, #b00020); }\r\n .orechio-billing__acao {\r\n align-self: flex-start;\r\n display: inline-block;\r\n padding: var(--orechio-space-2, 0.5rem) var(--orechio-space-4, 1rem);\r\n border-radius: var(--orechio-radius, 0.375rem);\r\n background: var(--orechio-color-primary, #1a56db);\r\n color: var(--orechio-color-on-primary, #fff);\r\n font-weight: 600;\r\n text-decoration: none;\r\n }\r\n .orechio-billing__acao:hover,\r\n .orechio-billing__acao:focus-visible {\r\n background: var(--orechio-color-primary-hover, var(--orechio-color-primary, #1446b0));\r\n }\r\n`;\r\n"],"names":[],"mappings":";;;;;;;AAoBA;;;;AAIG;MAEU,aAAa,CAAA;AACP,IAAA,IAAI,GAAG,MAAM,CAAC,UAAU,CAAC;AACzB,IAAA,MAAM,GAAG,MAAM,CAAC,sBAAsB,CAAC;IAExD,eAAe,GAAA;AACb,QAAA,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAe,IAAI,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC,CAAC;IAC3E;IAEA,UAAU,GAAA;AACR,QAAA,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAiB,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC;IACxE;AAEA,IAAA,aAAa,CAAC,OAAoC,EAAA;QAChD,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAiB,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC,CAAC;IAClF;AAEA,IAAA,oBAAoB,CAAC,OAA8B,EAAA;QACjD,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAO,IAAI,CAAC,GAAG,CAAC,kBAAkB,CAAC,EAAE,OAAO,CAAC,CAAC;IACjF;AAEA,IAAA,oBAAoB,CAAC,OAA8B,EAAA;QACjD,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAO,IAAI,CAAC,GAAG,CAAC,kBAAkB,CAAC,EAAE,OAAO,CAAC,CAAC;IACjF;IAEA,SAAS,GAAA;AACP,QAAA,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAS,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC,CAAC;IAC9D;AAEA,IAAA,cAAc,CAAC,MAAc,EAAE,WAAmB,EAAE,KAAmB,EAAA;AACrE,QAAA,MAAM,MAAM,GAAG,IAAI,UAAU,CAAC,EAAE,UAAU,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,KAAK,EAAE,EAAE,CAAC;QAC7E,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAiB,IAAI,CAAC,GAAG,CAAC,kBAAkB,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;IAC5F;AAEA;;;AAGG;AACH,IAAA,QAAQ,CAAC,KAAmB,EAAA;QAC1B,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAiB,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC,CAAC;IACpG;AAEA,IAAA,YAAY,CAAC,IAAI,GAAG,CAAC,EAAE,QAAQ,GAAG,EAAE,EAAA;AAClC,QAAA,MAAM,MAAM,GAAG,IAAI,UAAU,CAAC,EAAE,UAAU,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,CAAC;QACjE,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAe,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC;IACnF;AAEA,IAAA,UAAU,CAAC,EAAU,EAAA;QACnB,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAgB,IAAI,CAAC,GAAG,CAAC,CAAA,UAAA,EAAa,kBAAkB,CAAC,EAAE,CAAC,CAAA,CAAE,CAAC,CAAC,CAAC;IAClG;AAEA,IAAA,iBAAiB,CAAC,EAAU,EAAA;QAC1B,OAAO,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAmB,IAAI,CAAC,GAAG,CAAC,CAAA,UAAA,EAAa,kBAAkB,CAAC,EAAE,CAAC,CAAA,QAAA,CAAU,CAAC,EAAE,IAAI,CAAC,CAAC;IACpH;AAEQ,IAAA,GAAG,CAAC,IAAY,EAAA;QACtB,OAAO,CAAA,EAAG,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAA,EAAG,IAAI,CAAA,CAAE;IACzC;AAEQ,IAAA,KAAK,CAAI,OAAsB,EAAA;QACrC,OAAO,OAAO,CAAC,IAAI,CACjB,UAAU,CAAC,CAAC,KAAwB,KAClC,UAAU,CAAC,MAAsB,uBAAuB,CAAC,KAAK,CAAC,CAAC,CACjE,CACF;IACH;uGAhEW,aAAa,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,UAAA,EAAA,CAAA;AAAb,IAAA,OAAA,KAAA,GAAA,EAAA,CAAA,qBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,QAAA,EAAA,EAAA,EAAA,IAAA,EAAA,aAAa,cADA,MAAM,EAAA,CAAA;;2FACnB,aAAa,EAAA,UAAA,EAAA,CAAA;kBADzB,UAAU;mBAAC,EAAE,UAAU,EAAE,MAAM,EAAE;;;ACjBlC,MAAM,mBAAmB,GAAG,+BAA+B;AAC3D,MAAM,kBAAkB,GAAG,uBAAuB;AAClD,MAAM,iBAAiB,GACrB,sFAAsF;AACxF,MAAM,kBAAkB,GAAG,gEAAgE;AAC3F,MAAM,YAAY,GAAG,oDAAoD;AAEzE;;;;AAIG;AACH,MAAM,SAAS,GAA2B;AACxC,IAAA,iBAAiB,EAAE,qDAAqD;AACxE,IAAA,sBAAsB,EACpB,mFAAmF;AACrF,IAAA,QAAQ,EAAE,uEAAuE;AACjF,IAAA,mBAAmB,EAAE,8DAA8D;AAEnF,IAAA,sBAAsB,EAAE,YAAY;AACpC,IAAA,mBAAmB,EAAE,YAAY;AAEjC,IAAA,wBAAwB,EAAE,gDAAgD;AAE1E,IAAA,2BAA2B,EAAE,+CAA+C;AAC5E,IAAA,gCAAgC,EAAE,4CAA4C;AAC9E,IAAA,qCAAqC,EAAE,4CAA4C;AACnF,IAAA,wCAAwC,EAAE,+BAA+B;AACzE,IAAA,uCAAuC,EAAE,uCAAuC;AAChF,IAAA,uCAAuC,EAAE,qCAAqC;AAC9E,IAAA,sCAAsC,EAAE,iBAAiB;AACzD,IAAA,wCAAwC,EAAE,iBAAiB;AAC3D,IAAA,mCAAmC,EAAE,iBAAiB;AACtD,IAAA,6BAA6B,EAAE,0BAA0B;AACzD,IAAA,6BAA6B,EAAE,kBAAkB;AACjD,IAAA,+BAA+B,EAAE,kBAAkB;AACnD,IAAA,6BAA6B,EAAE,eAAe;AAC9C,IAAA,uBAAuB,EAAE,8CAA8C;AACvE,IAAA,yBAAyB,EAAE,mBAAmB;AAC9C,IAAA,yBAAyB,EAAE,mBAAmB;AAC9C,IAAA,+BAA+B,EAAE,mBAAmB;AACpD,IAAA,uBAAuB,EAAE,mBAAmB;AAE5C,IAAA,oCAAoC,EAClC,0EAA0E;AAC5E,IAAA,8BAA8B,EAC5B,6FAA6F;AAC/F,IAAA,+BAA+B,EAC7B,gFAAgF;AAClF,IAAA,oCAAoC,EAAE,iDAAiD;AACvF,IAAA,wCAAwC,EACtC,0EAA0E;AAC5E,IAAA,uBAAuB,EAAE,sCAAsC;AAC/D,IAAA,+BAA+B,EAAE,sCAAsC;AACvE,IAAA,gBAAgB,EAAE,kBAAkB;AACpC,IAAA,oBAAoB,EAAE,kBAAkB;AACxC,IAAA,4BAA4B,EAAE,kBAAkB;AAEhD,IAAA,mBAAmB,EAAE,wBAAwB;AAC7C,IAAA,6BAA6B,EAC3B,4EAA4E;AAC9E,IAAA,6BAA6B,EAAE,sCAAsC;AACrE,IAAA,0BAA0B,EAAE,0CAA0C;AACtE,IAAA,6BAA6B,EAC3B,+EAA+E;CAClF;AAED,MAAM,QAAQ,GACZ,kHAAkH;AAEpH;;;AAGG;AACG,SAAU,cAAc,CAAC,QAAwB,EAAA;IACrD,MAAM,SAAS,GAAG,SAAS,CAAC,QAAQ,CAAC,IAAI,CAAC;AAC1C,IAAA,IAAI,SAAS;AAAE,QAAA,OAAO,EAAE,KAAK,EAAE,SAAS,EAAE;IAE1C,OAAO,QAAQ,CAAC;UACZ,EAAE,KAAK,EAAE,QAAQ,EAAE,aAAa,EAAE,QAAQ,CAAC,aAAa;AAC1D,UAAE,EAAE,KAAK,EAAE,QAAQ,EAAE;AACzB;;MCvEa,qBAAqB,CAAA;IACvB,QAAQ,GAAG,KAAK,CAAC,QAAQ;iFAAkB;AACjC,IAAA,QAAQ,GAAG,QAAQ,CAAC,MAAM,cAAc,CAAC,IAAI,CAAC,QAAQ,EAAE,CAAC;iFAAC;uGAFlE,qBAAqB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAArB,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,qBAAqB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,uBAAA,EAAA,MAAA,EAAA,EAAA,QAAA,EAAA,EAAA,iBAAA,EAAA,UAAA,EAAA,UAAA,EAAA,UAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EAVtB,CAAA;;;;;;;AAOT,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,MAAA,EAAA,CAAA,uEAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;2FAGU,qBAAqB,EAAA,UAAA,EAAA,CAAA;kBAdjC,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,uBAAuB,cACrB,IAAI,EAAA,eAAA,EACC,uBAAuB,CAAC,MAAM,EAAA,QAAA,EACrC,CAAA;;;;;;;AAOT,EAAA,CAAA,EAAA,MAAA,EAAA,CAAA,uEAAA,CAAA,EAAA;;;ACbH,MAAM,KAAK,GAAG,IAAI,IAAI,CAAC,YAAY,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,KAAK,EAAE,CAAC;AAE9E,SAAU,KAAK,CAAC,KAAa,EAAA;AACjC,IAAA,OAAO,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC;AAC5B;AAEM,SAAU,IAAI,CAAC,GAA8B,EAAA;AACjD,IAAA,OAAO,GAAG,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,CAAC,kBAAkB,CAAC,OAAO,CAAC,GAAG,EAAE;AAC7D;AAEA,MAAM,gBAAgB,GAAG,mBAAmB;AAC5C,MAAM,kBAAkB,GAAG,uBAAuB;AAElD;AACA,MAAM,QAAQ,GAAG,qEAAqE;AACtF;AACA,MAAM,QAAQ,GAAG,gFAAgF;AAEjG;;;;;;;AAOG;AACG,SAAU,aAAa,CAAC,KAAa,EAAA;AACzC,IAAA,MAAM,KAAK,GAAG,KAAK,CAAC,IAAI,EAAE;IAE1B,MAAM,OAAO,GAAG,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC;IACpC,IAAI,OAAO,EAAE;AACX,QAAA,MAAM,GAAG,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,CAAC,GAAG,OAAO;AAC/C,QAAA,OAAO,UAAU,CAAC,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,EAAE,CAAC,IAAI,EAAE,CAAC,MAAM;AAChD,cAAE,CAAA,EAAG,GAAG,CAAA,CAAA,EAAI,GAAG,CAAA,CAAA,EAAI,GAAG,CAAA,IAAA,EAAO,IAAI,CAAA,CAAA,EAAI,MAAM,CAAA,CAAA,EAAI,kBAAkB,CAAA;cAC/D,KAAK;IACX;AAEA,IAAA,IAAI,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE;AACxB,QAAA,MAAM,QAAQ,GAAG,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE,GAAG,CAAC,CAAC;QAClD,IAAI,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,OAAO,EAAE,CAAC;AAAE,YAAA,OAAO,KAAK;AAElD,QAAA,MAAM,MAAM,GAAG,MAAM,CAAC,WAAW,CAC/B,IAAI,IAAI,CAAC,cAAc,CAAC,OAAO,EAAE;AAC/B,YAAA,QAAQ,EAAE,gBAAgB;AAC1B,YAAA,GAAG,EAAE,SAAS;AACd,YAAA,KAAK,EAAE,SAAS;AAChB,YAAA,IAAI,EAAE,SAAS;AACf,YAAA,IAAI,EAAE,SAAS;AACf,YAAA,MAAM,EAAE,SAAS;AACjB,YAAA,SAAS,EAAE,KAAK;SACjB;aACE,aAAa,CAAC,QAAQ;AACtB,aAAA,GAAG,CAAC,CAAC,KAAK,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,CAC7C;AACD,QAAA,OAAO,CAAA,EAAG,MAAM,CAAC,KAAK,CAAC,CAAA,CAAA,EAAI,MAAM,CAAC,OAAO,CAAC,CAAA,CAAA,EAAI,MAAM,CAAC,MAAM,CAAC,CAAA,IAAA,EAAO,MAAM,CAAC,MAAM,CAAC,CAAA,CAAA,EAAI,MAAM,CAAC,QAAQ,CAAC,CAAA,CAAA,EAAI,kBAAkB,CAAA,CAAE;IAC/H;AAEA,IAAA,OAAO,KAAK;AACd;AAEA,SAAS,UAAU,CAAC,GAAW,EAAE,GAAW,EAAE,GAAW,EAAE,IAAY,EAAE,MAAc,EAAA;AACrF,IAAA,IAAI,GAAG,GAAG,CAAC,IAAI,GAAG,GAAG,EAAE,IAAI,IAAI,GAAG,EAAE,IAAI,MAAM,GAAG,EAAE,IAAI,GAAG,GAAG,CAAC;AAAE,QAAA,OAAO,KAAK;AAC5E,IAAA,OAAO,GAAG,IAAI,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,GAAG,EAAE,CAAC,CAAC,CAAC,CAAC,UAAU,EAAE;AAC5D;AAEA;AAEM,SAAU,SAAS,CAAC,KAAa,EAAA;IACrC,OAAO,KAAK,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC;AACjC;AAEA;AACA,SAAS,OAAO,CAAC,OAAe,EAAE,MAAyB,EAAE,WAA8B,EAAA;IACzF,IAAI,KAAK,GAAG,EAAE;IACd,IAAI,OAAO,GAAG,CAAC;AACf,IAAA,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE;AACtC,QAAA,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,OAAO,EAAE,OAAO,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC;AACzD,QAAA,IAAI,CAAC,KAAK;YAAE;QACZ,KAAK,IAAI,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,GAAG,WAAW,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,KAAK;AACpD,QAAA,OAAO,IAAI,MAAM,CAAC,CAAC,CAAC;IACtB;AACA,IAAA,OAAO,KAAK;AACd;AAEA;AACM,SAAU,WAAW,CAAC,KAAa,EAAA;AACvC,IAAA,OAAO,OAAO,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;AAC9E;AAEA;AACM,SAAU,YAAY,CAAC,KAAa,EAAA;AACxC,IAAA,OAAO,OAAO,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAC;AACtF;AAEA;AACM,SAAU,iBAAiB,CAAC,IAAoB,EAAE,KAAa,EAAA;AACnE,IAAA,OAAO,IAAI,KAAK,KAAK,GAAG,WAAW,CAAC,KAAK,CAAC,GAAG,YAAY,CAAC,KAAK,CAAC;AAClE;AAEA;AACM,SAAU,WAAW,CAAC,KAAa,EAAA;IACvC,OAAO,OAAO,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;AAC7D;AAEA;AACM,SAAU,gBAAgB,CAAC,KAAa,EAAA;AAC5C,IAAA,MAAM,OAAO,GAAG,SAAS,CAAC,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;AAC7C,IAAA,IAAI,CAAC,OAAO;AAAE,QAAA,OAAO,EAAE;IAEvB,MAAM,GAAG,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC;AAC/B,IAAA,IAAI,OAAO,CAAC,MAAM,IAAI,CAAC;QAAE,OAAO,CAAA,CAAA,EAAI,GAAG,CAAA,CAAE;IAEzC,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC;AAC/B,IAAA,MAAM,sBAAsB,GAAG,OAAO,CAAC,MAAM,GAAG,EAAE,GAAG,CAAC,GAAG,CAAC;AAC1D,IAAA,MAAM,KAAK,GACT,MAAM,CAAC,MAAM,GAAG;AACd,UAAE,CAAA,EAAG,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,sBAAsB,CAAC,IAAI,MAAM,CAAC,KAAK,CAAC,sBAAsB,CAAC,CAAA;UAClF,MAAM;AACZ,IAAA,OAAO,CAAA,CAAA,EAAI,GAAG,CAAA,EAAA,EAAK,KAAK,EAAE;AAC5B;AAEO,MAAM,gBAAgB,GAAkC;AAC7D,IAAA,OAAO,EAAE,YAAY;AACrB,IAAA,OAAO,EAAE,WAAW;AACpB,IAAA,IAAI,EAAE,MAAM;AACZ,IAAA,OAAO,EAAE,SAAS;AAClB,IAAA,QAAQ,EAAE,WAAW;AACrB,IAAA,SAAS,EAAE,WAAW;;AAGjB,MAAM,WAAW,GAAG,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4B1B;;;;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"orechio-platform-angular-billing-checkout.page-CNl0PPiE.mjs","sources":["../../../projects/platform-angular/billing/src/pages/checkout.page.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, OnInit, inject, signal } from '@angular/core';\r\nimport { ActivatedRoute, Router, RouterLink } from '@angular/router';\r\nimport { forkJoin } from 'rxjs';\r\nimport { ProblemDetails } from '@orechio/platform-angular';\r\nimport { BillingClient } from '../billing-client';\r\nimport { BillingErrorComponent } from '../billing-error.component';\r\nimport { ESTILO_BASE, moeda } from '../billing-format';\r\nimport { BillingCycle, PixData, PricingPreview, Subscription, SubscriptionStatus } from '../billing-models';\r\nimport { PixPanelComponent } from '../pix-panel.component';\r\n\r\nconst STATUS_COM_CHECKOUT: SubscriptionStatus[] = ['Trial', 'PendingPayment', 'Grace', 'Suspended'];\r\n\r\nconst CICLOS: { valor: BillingCycle; rotulo: string }[] = [\r\n { valor: 'Monthly', rotulo: 'Mensal' },\r\n { valor: 'Yearly', rotulo: 'Anual' },\r\n];\r\n\r\n/**\r\n * Pagar a assinatura (ADR-021 §3.5, item 3): o plano ATUAL — trocar de plano é v2 —, o preço\r\n * de cada ciclo vindo de `pricing/preview`, e o checkout PIX.\r\n */\r\n@Component({\r\n selector: 'orechio-billing-checkout',\r\n standalone: true,\r\n imports: [RouterLink, BillingErrorComponent, PixPanelComponent],\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n template: `\r\n <section class=\"orechio-billing\">\r\n <h1>Pagar assinatura</h1>\r\n\r\n @if (pix(); as dados) {\r\n <p>Pague o PIX abaixo. A assinatura é atualizada assim que o pagamento for confirmado.</p>\r\n <orechio-billing-pix [pix]=\"dados\" [invoiceUrl]=\"invoiceUrl()\" />\r\n <a routerLink=\"..\">Voltar para a assinatura</a>\r\n } @else if (inelegivel()) {\r\n <p>Não há pagamento de assinatura a fazer agora.</p>\r\n <a routerLink=\"../invoices\">Ver faturas</a>\r\n } @else if (previas().length) {\r\n <h2>{{ previas()[0].planName }}</h2>\r\n\r\n <fieldset>\r\n <legend>Ciclo de cobrança</legend>\r\n @for (previa of previas(); track previa.cycle) {\r\n <label>\r\n <input\r\n type=\"radio\"\r\n name=\"ciclo\"\r\n [value]=\"previa.cycle\"\r\n [checked]=\"ciclo() === previa.cycle\"\r\n (change)=\"ciclo.set(previa.cycle)\"\r\n />\r\n {{ rotulo(previa.cycle) }} — {{ moeda(previa.total) }}\r\n </label>\r\n }\r\n </fieldset>\r\n\r\n <button type=\"button\" data-gerar-pix (click)=\"pagar()\" [disabled]=\"enviando()\">\r\n {{ enviando() ? 'Gerando PIX…' : 'Gerar PIX' }}\r\n </button>\r\n } @else if (!erro()) {\r\n <p class=\"orechio-billing__muted\">Carregando…</p>\r\n }\r\n\r\n @if (erro(); as problema) {\r\n <orechio-billing-error [problema]=\"problema\" />\r\n }\r\n </section>\r\n `,\r\n styles: ESTILO_BASE,\r\n})\r\nexport class CheckoutPage implements OnInit {\r\n private readonly client = inject(BillingClient);\r\n private readonly router = inject(Router);\r\n private readonly route = inject(ActivatedRoute);\r\n\r\n protected readonly moeda = moeda;\r\n protected readonly previas = signal<PricingPreview[]>([]);\r\n protected readonly ciclo = signal<BillingCycle>('Monthly');\r\n protected readonly inelegivel = signal(false);\r\n protected readonly enviando = signal(false);\r\n protected readonly pix = signal<PixData | null>(null);\r\n protected readonly invoiceUrl = signal<string | null>(null);\r\n protected readonly erro = signal<ProblemDetails | null>(null);\r\n\r\n protected rotulo(ciclo: BillingCycle): string {\r\n return CICLOS.find((c) => c.valor === ciclo)?.rotulo ?? ciclo;\r\n }\r\n\r\n ngOnInit(): void {\r\n forkJoin({ assinatura: this.client.getSubscription(), perfil: this.client.getProfile() }).subscribe({\r\n next: ({ assinatura, perfil }) => {\r\n // O checkout serve Trial, PendingPayment, Grace e Suspended (`InitiateCheckoutCommand`,\r\n // passo 4; os dois últimos desde a BILLING-2b, e só sem fatura aberta — ver `pagar`).\r\n // Checado antes do perfil: não se pede cadastro para um pagamento que não existe.\r\n if (!STATUS_COM_CHECKOUT.includes(assinatura.status)) {\r\n this.inelegivel.set(true);\r\n return;\r\n }\r\n\r\n // O checkout recusa sem perfil (`billing_profile.not_found`). Mandar para o perfil\r\n // ANTES poupa o cliente de escolher o ciclo para receber um erro.\r\n if (!perfil.exists || !perfil.isComplete) {\r\n void this.router.navigate(['../profile'], {\r\n relativeTo: this.route,\r\n queryParams: { next: 'checkout' },\r\n });\r\n return;\r\n }\r\n\r\n this.ciclo.set(assinatura.cycle);\r\n this.carregarPrecos(assinatura);\r\n },\r\n error: (problema: ProblemDetails) => this.erro.set(problema),\r\n });\r\n }\r\n\r\n protected pagar(): void {\r\n if (this.enviando()) return;\r\n this.enviando.set(true);\r\n this.erro.set(null);\r\n\r\n this.client.checkout(this.ciclo()).subscribe({\r\n next: (resultado) => {\r\n this.enviando.set(false);\r\n this.invoiceUrl.set(resultado.invoiceUrl ?? null);\r\n if (resultado.pix) this.pix.set(resultado.pix);\r\n else this.erro.set({ code: 'checkout.pix_missing', status: 200 });\r\n },\r\n error: (problema: ProblemDetails) => {\r\n this.enviando.set(false);\r\n // Em Grace/Suspended com fatura aberta a Platform recusa: a aberta é a que se paga.\r\n if (problema.code === 'checkout.open_invoice_exists') {\r\n void this.router.navigate(['../invoices'], { relativeTo: this.route });\r\n return;\r\n }\r\n this.erro.set(problema);\r\n },\r\n });\r\n }\r\n\r\n private carregarPrecos(assinatura: Subscription): void {\r\n forkJoin(\r\n CICLOS.map(({ valor }) =>\r\n this.client.previewPricing(assinatura.planId, assinatura.branchCountSnapshot, valor),\r\n ),\r\n ).subscribe({\r\n next: (previas) => this.previas.set(previas),\r\n error: (problema: ProblemDetails) => this.erro.set(problema),\r\n });\r\n }\r\n}\r\n"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"orechio-platform-angular-billing-invoice.page-CLojIJkf.mjs","sources":["../../../projects/platform-angular/billing/src/pages/invoice.page.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, OnInit, computed, inject, signal } from '@angular/core';\r\nimport { ActivatedRoute, RouterLink } from '@angular/router';\r\nimport { ProblemDetails } from '@orechio/platform-angular';\r\nimport { BillingClient } from '../billing-client';\r\nimport { BillingErrorComponent } from '../billing-error.component';\r\nimport { ESTILO_BASE, STATUS_DA_FATURA, data, moeda } from '../billing-format';\r\nimport { InvoiceDetail, PixData } from '../billing-models';\r\nimport { PixPanelComponent } from '../pix-panel.component';\r\n\r\n/**\r\n * Detalhe de fatura. O detalhe da Platform não traz dado de pagamento (`InvoiceDetailDto`):\r\n * o QR vem do refresh, que a Platform aceita para fatura PIX `Pending` ou `Overdue`\r\n * (`RefreshPaymentDataCommand`, passo 2) — o que inclui a fatura de renovação.\r\n */\r\n@Component({\r\n selector: 'orechio-billing-invoice',\r\n standalone: true,\r\n imports: [RouterLink, BillingErrorComponent, PixPanelComponent],\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n template: `\r\n <section class=\"orechio-billing\">\r\n @if (fatura(); as f) {\r\n <h1>Fatura {{ f.number }}</h1>\r\n <p>{{ status[f.status] }} — {{ moeda(f.amount) }} — vence em {{ data(f.dueDate) }}</p>\r\n <p class=\"orechio-billing__muted\">\r\n {{ f.planNameAtTime }}, de {{ data(f.periodStartsAt) }} a {{ data(f.periodEndsAt) }}\r\n </p>\r\n\r\n <ul>\r\n @for (item of f.items; track item.order) {\r\n <li>{{ item.description }} — {{ item.quantity }} × {{ moeda(item.unitAmount) }} = {{ moeda(item.totalAmount) }}</li>\r\n }\r\n </ul>\r\n\r\n @if (podeGerarQr()) {\r\n <button type=\"button\" data-gerar-qr (click)=\"gerarQr()\" [disabled]=\"gerando()\">\r\n {{ gerando() ? 'Gerando…' : 'Gerar novo QR' }}\r\n </button>\r\n }\r\n\r\n @if (pix(); as dados) {\r\n <orechio-billing-pix [pix]=\"dados\" [invoiceUrl]=\"invoiceUrl()\" />\r\n }\r\n } @else if (!erro()) {\r\n <p class=\"orechio-billing__muted\">Carregando…</p>\r\n }\r\n\r\n @if (erro(); as problema) {\r\n <orechio-billing-error [problema]=\"problema\" />\r\n }\r\n\r\n <a routerLink=\"..\">Voltar para as faturas</a>\r\n </section>\r\n `,\r\n styles: ESTILO_BASE,\r\n})\r\nexport class InvoicePage implements OnInit {\r\n private readonly client = inject(BillingClient);\r\n private readonly route = inject(ActivatedRoute);\r\n\r\n protected readonly status = STATUS_DA_FATURA;\r\n protected readonly moeda = moeda;\r\n protected readonly data = data;\r\n protected readonly fatura = signal<InvoiceDetail | null>(null);\r\n protected readonly pix = signal<PixData | null>(null);\r\n protected readonly invoiceUrl = signal<string | null>(null);\r\n protected readonly gerando = signal(false);\r\n protected readonly erro = signal<ProblemDetails | null>(null);\r\n protected readonly podeGerarQr = computed(() => {\r\n const f = this.fatura();\r\n return !!f && f.paymentMethod === 'Pix' && (f.status === 'Pending' || f.status === 'Overdue');\r\n });\r\n\r\n ngOnInit(): void {\r\n const id = this.route.snapshot.paramMap.get('id') ?? '';\r\n this.client.getInvoice(id).subscribe({\r\n next: (fatura) => this.fatura.set(fatura),\r\n error: (problema: ProblemDetails) => this.erro.set(problema),\r\n });\r\n }\r\n\r\n protected gerarQr(): void {\r\n const f = this.fatura();\r\n if (!f || this.gerando()) return;\r\n\r\n this.gerando.set(true);\r\n this.erro.set(null);\r\n this.client.refreshInvoicePix(f.id).subscribe({\r\n next: (pix) => {\r\n this.gerando.set(false);\r\n // O refresh traz o PIX e o `invoiceUrl` juntos: o link acompanha o QR que está na tela.\r\n this.invoiceUrl.set(pix.invoiceUrl ?? null);\r\n this.pix.set(pix);\r\n },\r\n error: (problema: ProblemDetails) => {\r\n this.gerando.set(false);\r\n this.erro.set(problema);\r\n },\r\n });\r\n }\r\n}\r\n"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"orechio-platform-angular-billing-invoices.page-COsCT5NY.mjs","sources":["../../../projects/platform-angular/billing/src/pages/invoices.page.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, OnInit, computed, inject, signal } from '@angular/core';\r\nimport { RouterLink } from '@angular/router';\r\nimport { ProblemDetails } from '@orechio/platform-angular';\r\nimport { BillingClient } from '../billing-client';\r\nimport { BillingErrorComponent } from '../billing-error.component';\r\nimport { ESTILO_BASE, STATUS_DA_FATURA, data, moeda } from '../billing-format';\r\nimport { InvoicesPage as PaginaDeFaturas } from '../billing-models';\r\n\r\nconst TAMANHO_DA_PAGINA = 20;\r\n\r\n@Component({\r\n selector: 'orechio-billing-invoices',\r\n standalone: true,\r\n imports: [RouterLink, BillingErrorComponent],\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n template: `\r\n <section class=\"orechio-billing\">\r\n <h1>Faturas</h1>\r\n\r\n @if (erro(); as problema) {\r\n <orechio-billing-error [problema]=\"problema\" />\r\n } @else if (pagina(); as p) {\r\n @if (p.invoices.length === 0) {\r\n <p class=\"orechio-billing__muted\">Nenhuma fatura ainda.</p>\r\n } @else {\r\n <ul>\r\n @for (fatura of p.invoices; track fatura.id) {\r\n <li>\r\n <a [routerLink]=\"fatura.id\">Fatura {{ fatura.number }}</a>\r\n — {{ status[fatura.status] }} — {{ moeda(fatura.amount) }} — vence em {{ data(fatura.dueDate) }}\r\n </li>\r\n }\r\n </ul>\r\n\r\n <nav>\r\n @if (p.page > 1) {\r\n <button type=\"button\" (click)=\"carregar(p.page - 1)\">Anteriores</button>\r\n }\r\n @if (temProxima()) {\r\n <button type=\"button\" (click)=\"carregar(p.page + 1)\">Próximas</button>\r\n }\r\n </nav>\r\n }\r\n <a routerLink=\"..\">Voltar para a assinatura</a>\r\n } @else {\r\n <p class=\"orechio-billing__muted\">Carregando…</p>\r\n }\r\n </section>\r\n `,\r\n styles: ESTILO_BASE,\r\n})\r\nexport class InvoicesPage implements OnInit {\r\n private readonly client = inject(BillingClient);\r\n\r\n protected readonly status = STATUS_DA_FATURA;\r\n protected readonly moeda = moeda;\r\n protected readonly data = data;\r\n protected readonly pagina = signal<PaginaDeFaturas | null>(null);\r\n protected readonly erro = signal<ProblemDetails | null>(null);\r\n protected readonly temProxima = computed(() => {\r\n const p = this.pagina();\r\n return !!p && p.page * p.pageSize < p.total;\r\n });\r\n\r\n ngOnInit(): void {\r\n this.carregar(1);\r\n }\r\n\r\n protected carregar(pagina: number): void {\r\n this.client.listInvoices(pagina, TAMANHO_DA_PAGINA).subscribe({\r\n next: (resultado) => this.pagina.set(resultado),\r\n error: (problema: ProblemDetails) => this.erro.set(problema),\r\n });\r\n }\r\n}\r\n"],"names":[],"mappings":";;;;;AAQA,MAAM,iBAAiB,GAAG,EAAE;MA2Cf,YAAY,CAAA;AACN,IAAA,MAAM,GAAG,MAAM,CAAC,aAAa,CAAC;IAE5B,MAAM,GAAG,gBAAgB;IACzB,KAAK,GAAG,KAAK;IACb,IAAI,GAAG,IAAI;IACX,MAAM,GAAG,MAAM,CAAyB,IAAI;+EAAC;IAC7C,IAAI,GAAG,MAAM,CAAwB,IAAI;6EAAC;AAC1C,IAAA,UAAU,GAAG,QAAQ,CAAC,MAAK;AAC5C,QAAA,MAAM,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE;AACvB,QAAA,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,GAAG,CAAC,CAAC,QAAQ,GAAG,CAAC,CAAC,KAAK;IAC7C,CAAC;mFAAC;IAEF,QAAQ,GAAA;AACN,QAAA,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;IAClB;AAEU,IAAA,QAAQ,CAAC,MAAc,EAAA;QAC/B,IAAI,CAAC,MAAM,CAAC,YAAY,CAAC,MAAM,EAAE,iBAAiB,CAAC,CAAC,SAAS,CAAC;AAC5D,YAAA,IAAI,EAAE,CAAC,SAAS,KAAK,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,SAAS,CAAC;AAC/C,YAAA,KAAK,EAAE,CAAC,QAAwB,KAAK,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC;AAC7D,SAAA,CAAC;IACJ;uGAtBW,YAAY,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAZ,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,YAAY,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,0BAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EApCb,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCT,EAAA,QAAA,EAAA,IAAA,EAAA,MAAA,EAAA,CAAA,o6BAAA,CAAA,EAAA,YAAA,EAAA,CAAA,EAAA,IAAA,EAAA,WAAA,EAAA,IAAA,EAnCS,UAAU,kPAAE,qBAAqB,EAAA,QAAA,EAAA,uBAAA,EAAA,MAAA,EAAA,CAAA,UAAA,CAAA,EAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;2FAsChC,YAAY,EAAA,UAAA,EAAA,CAAA;kBAzCxB,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,0BAA0B,EAAA,UAAA,EACxB,IAAI,EAAA,OAAA,EACP,CAAC,UAAU,EAAE,qBAAqB,CAAC,EAAA,eAAA,EAC3B,uBAAuB,CAAC,MAAM,EAAA,QAAA,EACrC,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAiCT,EAAA,CAAA,EAAA,MAAA,EAAA,CAAA,o6BAAA,CAAA,EAAA;;;;;"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"orechio-platform-angular-billing-pix-panel.component-fdORTehK.mjs","sources":["../../../projects/platform-angular/billing/src/pix-panel.component.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, input, signal } from '@angular/core';\r\nimport { validadeDoPix } from './billing-format';\r\nimport { PixData } from './billing-models';\r\n\r\n/**\r\n * O QR e o copia-e-cola, como a Platform os devolveu. O `encodedImage` é PNG em base64\r\n * (ASAAS); o `payload` é o código que o app do banco aceita colado.\r\n */\r\n@Component({\r\n selector: 'orechio-billing-pix',\r\n standalone: true,\r\n changeDetection: ChangeDetectionStrategy.OnPush,\r\n template: `\r\n <div class=\"orechio-billing-pix\">\r\n <img data-qr [src]=\"'data:image/png;base64,' + pix().encodedImage\" alt=\"QR Code PIX\" width=\"240\" height=\"240\" />\r\n\r\n <p>Ou copie o código PIX e cole no app do seu banco:</p>\r\n <code class=\"orechio-billing-pix__codigo\">{{ pix().payload }}</code>\r\n <button type=\"button\" data-copiar (click)=\"copiar()\">\r\n {{ copiado() ? 'Código copiado' : 'Copiar código' }}\r\n </button>\r\n @if (semAreaDeTransferencia()) {\r\n <p class=\"orechio-billing-pix__aviso\">Selecione o código acima e copie manualmente.</p>\r\n }\r\n\r\n @if (pix().expirationDate; as validade) {\r\n <p class=\"orechio-billing-pix__aviso\">Válido até {{ validadeDoPix(validade) }}.</p>\r\n }\r\n\r\n @if (invoiceUrl(); as url) {\r\n <a data-invoice-url [href]=\"url\" target=\"_blank\" rel=\"noopener noreferrer\">\r\n Pagar pelo site do ASAAS\r\n </a>\r\n }\r\n </div>\r\n `,\r\n styles: `\r\n .orechio-billing-pix { display: flex; flex-direction: column; align-items: flex-start; gap: var(--orechio-space-2, 0.5rem); }\r\n .orechio-billing-pix__codigo { word-break: break-all; }\r\n .orechio-billing-pix__aviso { color: var(--orechio-color-text-muted, #666); }\r\n `,\r\n})\r\nexport class PixPanelComponent {\r\n readonly pix = input.required<PixData>();\r\n /** Página de pagamento do ASAAS, aberta em nova aba; sem ela, o link não aparece. */\r\n readonly invoiceUrl = input<string | null | undefined>(null);\r\n\r\n protected readonly validadeDoPix = validadeDoPix;\r\n protected readonly copiado = signal(false);\r\n protected readonly semAreaDeTransferencia = signal(false);\r\n\r\n protected copiar(): void {\r\n const areaDeTransferencia = globalThis.navigator?.clipboard;\r\n if (!areaDeTransferencia) {\r\n this.semAreaDeTransferencia.set(true);\r\n return;\r\n }\r\n\r\n areaDeTransferencia.writeText(this.pix().payload).then(\r\n () => this.copiado.set(true),\r\n () => this.semAreaDeTransferencia.set(true),\r\n );\r\n }\r\n}\r\n"],"names":[],"mappings":";;;;AAIA;;;AAGG;MAmCU,iBAAiB,CAAA;IACnB,GAAG,GAAG,KAAK,CAAC,QAAQ;4EAAW;;IAE/B,UAAU,GAAG,KAAK,CAA4B,IAAI;mFAAC;IAEzC,aAAa,GAAG,aAAa;IAC7B,OAAO,GAAG,MAAM,CAAC,KAAK;gFAAC;IACvB,sBAAsB,GAAG,MAAM,CAAC,KAAK;+FAAC;IAE/C,MAAM,GAAA;AACd,QAAA,MAAM,mBAAmB,GAAG,UAAU,CAAC,SAAS,EAAE,SAAS;QAC3D,IAAI,CAAC,mBAAmB,EAAE;AACxB,YAAA,IAAI,CAAC,sBAAsB,CAAC,GAAG,CAAC,IAAI,CAAC;YACrC;QACF;AAEA,QAAA,mBAAmB,CAAC,SAAS,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,OAAO,CAAC,CAAC,IAAI,CACpD,MAAM,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,EAC5B,MAAM,IAAI,CAAC,sBAAsB,CAAC,GAAG,CAAC,IAAI,CAAC,CAC5C;IACH;uGApBW,iBAAiB,EAAA,IAAA,EAAA,EAAA,EAAA,MAAA,EAAA,EAAA,CAAA,eAAA,CAAA,SAAA,EAAA,CAAA;AAAjB,IAAA,OAAA,IAAA,GAAA,EAAA,CAAA,oBAAA,CAAA,EAAA,UAAA,EAAA,QAAA,EAAA,OAAA,EAAA,QAAA,EAAA,IAAA,EAAA,iBAAiB,EAAA,YAAA,EAAA,IAAA,EAAA,QAAA,EAAA,qBAAA,EAAA,MAAA,EAAA,EAAA,GAAA,EAAA,EAAA,iBAAA,EAAA,KAAA,EAAA,UAAA,EAAA,KAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,IAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,UAAA,EAAA,EAAA,iBAAA,EAAA,YAAA,EAAA,UAAA,EAAA,YAAA,EAAA,QAAA,EAAA,IAAA,EAAA,UAAA,EAAA,KAAA,EAAA,iBAAA,EAAA,IAAA,EAAA,EAAA,EAAA,QAAA,EAAA,EAAA,EAAA,QAAA,EA9BlB,CAAA;;;;;;;;;;;;;;;;;;;;;;;AAuBT,EAAA,CAAA,EAAA,QAAA,EAAA,IAAA,EAAA,MAAA,EAAA,CAAA,+OAAA,CAAA,EAAA,eAAA,EAAA,EAAA,CAAA,uBAAA,CAAA,MAAA,EAAA,CAAA;;2FAOU,iBAAiB,EAAA,UAAA,EAAA,CAAA;kBAlC7B,SAAS;AACE,YAAA,IAAA,EAAA,CAAA,EAAA,QAAA,EAAA,qBAAqB,cACnB,IAAI,EAAA,eAAA,EACC,uBAAuB,CAAC,MAAM,EAAA,QAAA,EACrC,CAAA;;;;;;;;;;;;;;;;;;;;;;;AAuBT,EAAA,CAAA,EAAA,MAAA,EAAA,CAAA,+OAAA,CAAA,EAAA;;;;;"}
|