@orechio/platform-angular 0.2.0 → 0.6.0
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/README.md +64 -64
- package/fesm2022/orechio-platform-angular-account-account-client-mPl0tzMg.mjs +56 -0
- package/fesm2022/orechio-platform-angular-account-account-client-mPl0tzMg.mjs.map +1 -0
- package/fesm2022/orechio-platform-angular-account-forgot-password.page-5_8R5B4m.mjs +136 -0
- package/fesm2022/orechio-platform-angular-account-forgot-password.page-5_8R5B4m.mjs.map +1 -0
- package/fesm2022/orechio-platform-angular-account-initial-branch.page-Cmy07JGF.mjs +121 -0
- package/fesm2022/orechio-platform-angular-account-initial-branch.page-Cmy07JGF.mjs.map +1 -0
- package/fesm2022/orechio-platform-angular-account-resend-verification.page-qckKAlPk.mjs +122 -0
- package/fesm2022/orechio-platform-angular-account-resend-verification.page-qckKAlPk.mjs.map +1 -0
- package/fesm2022/orechio-platform-angular-account-token-password.page-AHqb8mFk.mjs +181 -0
- package/fesm2022/orechio-platform-angular-account-token-password.page-AHqb8mFk.mjs.map +1 -0
- package/fesm2022/orechio-platform-angular-account-verify-email.page-CHs0EwDf.mjs +121 -0
- package/fesm2022/orechio-platform-angular-account-verify-email.page-CHs0EwDf.mjs.map +1 -0
- package/fesm2022/orechio-platform-angular-account.mjs +106 -0
- package/fesm2022/orechio-platform-angular-account.mjs.map +1 -0
- package/fesm2022/orechio-platform-angular.mjs +224 -3
- package/fesm2022/orechio-platform-angular.mjs.map +1 -1
- package/package.json +5 -11
- package/types/orechio-platform-angular-account.d.ts +97 -0
- package/types/orechio-platform-angular.d.ts +120 -2
- package/LICENSE +0 -21
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { inject, signal, ChangeDetectionStrategy, Component } from '@angular/core';
|
|
3
|
+
import * as i1 from '@angular/forms';
|
|
4
|
+
import { FormsModule } from '@angular/forms';
|
|
5
|
+
import { ActivatedRoute, Router } from '@angular/router';
|
|
6
|
+
import { A as AccountClient } from './orechio-platform-angular-account-account-client-mPl0tzMg.mjs';
|
|
7
|
+
import { ORECHIO_ACCOUNT_CONFIG } from './orechio-platform-angular-account.mjs';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Base das duas telas que consomem um token da URL e definem uma senha:
|
|
11
|
+
* `reset-password` e `accept-invite`.
|
|
12
|
+
*
|
|
13
|
+
* <p>
|
|
14
|
+
* São o mesmo formulário com textos diferentes e uma chamada diferente. Duas cópias
|
|
15
|
+
* divergiriam na primeira correção — e a primeira correção aqui seria justamente a regra do
|
|
16
|
+
* token sair da URL, que é a que não pode falhar em nenhuma das duas.
|
|
17
|
+
* </p>
|
|
18
|
+
*/
|
|
19
|
+
class TokenPasswordPage {
|
|
20
|
+
route = inject(ActivatedRoute);
|
|
21
|
+
router = inject(Router);
|
|
22
|
+
client = inject(AccountClient);
|
|
23
|
+
config = inject(ORECHIO_ACCOUNT_CONFIG);
|
|
24
|
+
token = '';
|
|
25
|
+
/**
|
|
26
|
+
* `reset` ou `invite` — decide o texto e qual chamada do BFF usar.
|
|
27
|
+
*
|
|
28
|
+
* Lido do `data` da rota DIRETAMENTE, e não por `input()`. Um signal input só receberia
|
|
29
|
+
* o `data` se o host tivesse habilitado `withComponentInputBinding()` no router — e, sem
|
|
30
|
+
* isso, o `accept-invite` cairia no default `reset` e chamaria o endpoint errado. Um bug
|
|
31
|
+
* silencioso, dependente de configuração do host, num fluxo que o usuário percorre uma
|
|
32
|
+
* vez. Ler da rota funciona com qualquer configuração.
|
|
33
|
+
*/
|
|
34
|
+
modo = () => this.route.snapshot.data['modo'] === 'invite' ? 'invite' : 'reset';
|
|
35
|
+
senha = '';
|
|
36
|
+
enviando = signal(false, /* @ts-ignore */
|
|
37
|
+
...(ngDevMode ? [{ debugName: "enviando" }] : /* istanbul ignore next */ []));
|
|
38
|
+
concluido = signal(false, /* @ts-ignore */
|
|
39
|
+
...(ngDevMode ? [{ debugName: "concluido" }] : /* istanbul ignore next */ []));
|
|
40
|
+
semToken = signal(false, /* @ts-ignore */
|
|
41
|
+
...(ngDevMode ? [{ debugName: "semToken" }] : /* istanbul ignore next */ []));
|
|
42
|
+
erro = signal(null, /* @ts-ignore */
|
|
43
|
+
...(ngDevMode ? [{ debugName: "erro" }] : /* istanbul ignore next */ []));
|
|
44
|
+
titulo = () => this.modo() === 'invite' ? 'Defina sua senha de acesso' : 'Escolha uma nova senha';
|
|
45
|
+
tituloDeSucesso = () => this.modo() === 'invite' ? 'Acesso liberado' : 'Senha alterada';
|
|
46
|
+
mensagemDeErro() {
|
|
47
|
+
const code = this.erro()?.code;
|
|
48
|
+
if (code === 'token.invalid_or_expired')
|
|
49
|
+
return 'Este link não é mais válido.';
|
|
50
|
+
if (code === 'rate_limit_exceeded')
|
|
51
|
+
return 'Muitas tentativas. Aguarde alguns minutos.';
|
|
52
|
+
if (code === 'platform.unavailable')
|
|
53
|
+
return 'Serviço indisponível. Tente novamente em instantes.';
|
|
54
|
+
return 'Não foi possível salvar. Verifique a senha e tente de novo.';
|
|
55
|
+
}
|
|
56
|
+
async ngOnInit() {
|
|
57
|
+
const token = this.route.snapshot.queryParamMap.get('token');
|
|
58
|
+
if (!token) {
|
|
59
|
+
this.semToken.set(true);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
this.token = token;
|
|
63
|
+
// O token sai da URL ANTES de qualquer coisa — inclusive antes de o usuário digitar a
|
|
64
|
+
// senha. Ele sobrevive no histórico, vaza por `Referer` para todo recurso de terceiro
|
|
65
|
+
// que esta tela carregue, e entra em log de proxy. `replaceUrl: true` substitui a
|
|
66
|
+
// entrada do histórico: sem isso, "voltar" traz a URL com o token de volta.
|
|
67
|
+
await this.router.navigate([], {
|
|
68
|
+
relativeTo: this.route,
|
|
69
|
+
queryParams: {},
|
|
70
|
+
replaceUrl: true,
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
enviar() {
|
|
74
|
+
if (!this.senha || this.enviando())
|
|
75
|
+
return;
|
|
76
|
+
this.enviando.set(true);
|
|
77
|
+
this.erro.set(null);
|
|
78
|
+
const chamada = this.modo() === 'invite'
|
|
79
|
+
? this.client.acceptInvite(this.token, this.senha)
|
|
80
|
+
: this.client.resetPassword(this.token, this.senha);
|
|
81
|
+
chamada.subscribe({
|
|
82
|
+
next: () => {
|
|
83
|
+
// A senha não fica em memória depois de usada. Não é defesa contra atacante — quem
|
|
84
|
+
// executa script na página já venceu —, é higiene: um dump de heap ou um relatório
|
|
85
|
+
// de erro que serialize o componente não leva a senha junto.
|
|
86
|
+
this.senha = '';
|
|
87
|
+
this.token = '';
|
|
88
|
+
this.concluido.set(true);
|
|
89
|
+
this.enviando.set(false);
|
|
90
|
+
},
|
|
91
|
+
error: (problema) => {
|
|
92
|
+
this.erro.set(problema);
|
|
93
|
+
this.enviando.set(false);
|
|
94
|
+
},
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
irParaLogin() {
|
|
98
|
+
void this.router.navigateByUrl(this.config.loginPath);
|
|
99
|
+
}
|
|
100
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: TokenPasswordPage, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
101
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.2", type: TokenPasswordPage, isStandalone: true, selector: "orechio-account-token-password", ngImport: i0, template: `
|
|
102
|
+
<section class="orechio-account">
|
|
103
|
+
<ng-content select="[orechioAccountBrand]"></ng-content>
|
|
104
|
+
|
|
105
|
+
@if (concluido()) {
|
|
106
|
+
<h1>{{ tituloDeSucesso() }}</h1>
|
|
107
|
+
<p>Você já pode entrar com a nova senha.</p>
|
|
108
|
+
<button type="button" (click)="irParaLogin()">Ir para o login</button>
|
|
109
|
+
} @else if (semToken()) {
|
|
110
|
+
<h1>Link inválido</h1>
|
|
111
|
+
<p class="orechio-account__error">Este link não é mais válido.</p>
|
|
112
|
+
} @else {
|
|
113
|
+
<h1>{{ titulo() }}</h1>
|
|
114
|
+
|
|
115
|
+
<form (ngSubmit)="enviar()">
|
|
116
|
+
<label for="orechio-account-nova-senha">Nova senha</label>
|
|
117
|
+
<input
|
|
118
|
+
id="orechio-account-nova-senha"
|
|
119
|
+
name="senha"
|
|
120
|
+
type="password"
|
|
121
|
+
autocomplete="new-password"
|
|
122
|
+
[(ngModel)]="senha"
|
|
123
|
+
[disabled]="enviando()"
|
|
124
|
+
required
|
|
125
|
+
/>
|
|
126
|
+
|
|
127
|
+
@if (erro()) {
|
|
128
|
+
<p class="orechio-account__error">{{ mensagemDeErro() }}</p>
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
<button type="submit" [disabled]="enviando() || !senha">
|
|
132
|
+
{{ enviando() ? 'Salvando…' : 'Salvar' }}
|
|
133
|
+
</button>
|
|
134
|
+
</form>
|
|
135
|
+
}
|
|
136
|
+
</section>
|
|
137
|
+
`, isInline: true, styles: [".orechio-account{display:flex;flex-direction:column;gap:var(--orechio-space-3, .75rem);max-width:28rem;margin:0 auto;padding:var(--orechio-space-6, 1.5rem);color:var(--orechio-color-text, inherit);font-family:var(--orechio-font-body, inherit)}.orechio-account form{display:flex;flex-direction:column;gap:var(--orechio-space-2, .5rem)}.orechio-account__error{color:var(--orechio-color-danger, #b00020)}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.ɵNgNoValidate, selector: "form:not([ngNoForm]):not([ngNativeValidate])" }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgControlStatusGroup, selector: "[formGroupName],[formArrayName],[ngModelGroup],[formGroup],[formArray],form:not([ngNoForm]),[ngForm]" }, { kind: "directive", type: i1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "directive", type: i1.NgForm, selector: "form:not([ngNoForm]):not([formGroup]):not([formArray]),ng-form,[ngForm]", inputs: ["ngFormOptions"], outputs: ["ngSubmit"], exportAs: ["ngForm"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
138
|
+
}
|
|
139
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: TokenPasswordPage, decorators: [{
|
|
140
|
+
type: Component,
|
|
141
|
+
args: [{ selector: 'orechio-account-token-password', standalone: true, imports: [FormsModule], changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
142
|
+
<section class="orechio-account">
|
|
143
|
+
<ng-content select="[orechioAccountBrand]"></ng-content>
|
|
144
|
+
|
|
145
|
+
@if (concluido()) {
|
|
146
|
+
<h1>{{ tituloDeSucesso() }}</h1>
|
|
147
|
+
<p>Você já pode entrar com a nova senha.</p>
|
|
148
|
+
<button type="button" (click)="irParaLogin()">Ir para o login</button>
|
|
149
|
+
} @else if (semToken()) {
|
|
150
|
+
<h1>Link inválido</h1>
|
|
151
|
+
<p class="orechio-account__error">Este link não é mais válido.</p>
|
|
152
|
+
} @else {
|
|
153
|
+
<h1>{{ titulo() }}</h1>
|
|
154
|
+
|
|
155
|
+
<form (ngSubmit)="enviar()">
|
|
156
|
+
<label for="orechio-account-nova-senha">Nova senha</label>
|
|
157
|
+
<input
|
|
158
|
+
id="orechio-account-nova-senha"
|
|
159
|
+
name="senha"
|
|
160
|
+
type="password"
|
|
161
|
+
autocomplete="new-password"
|
|
162
|
+
[(ngModel)]="senha"
|
|
163
|
+
[disabled]="enviando()"
|
|
164
|
+
required
|
|
165
|
+
/>
|
|
166
|
+
|
|
167
|
+
@if (erro()) {
|
|
168
|
+
<p class="orechio-account__error">{{ mensagemDeErro() }}</p>
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
<button type="submit" [disabled]="enviando() || !senha">
|
|
172
|
+
{{ enviando() ? 'Salvando…' : 'Salvar' }}
|
|
173
|
+
</button>
|
|
174
|
+
</form>
|
|
175
|
+
}
|
|
176
|
+
</section>
|
|
177
|
+
`, styles: [".orechio-account{display:flex;flex-direction:column;gap:var(--orechio-space-3, .75rem);max-width:28rem;margin:0 auto;padding:var(--orechio-space-6, 1.5rem);color:var(--orechio-color-text, inherit);font-family:var(--orechio-font-body, inherit)}.orechio-account form{display:flex;flex-direction:column;gap:var(--orechio-space-2, .5rem)}.orechio-account__error{color:var(--orechio-color-danger, #b00020)}\n"] }]
|
|
178
|
+
}] });
|
|
179
|
+
|
|
180
|
+
export { TokenPasswordPage };
|
|
181
|
+
//# sourceMappingURL=orechio-platform-angular-account-token-password.page-AHqb8mFk.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"orechio-platform-angular-account-token-password.page-AHqb8mFk.mjs","sources":["../../../projects/platform-angular/account/src/pages/token-password.page.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, OnInit, inject, signal } from '@angular/core';\nimport { FormsModule } from '@angular/forms';\nimport { ActivatedRoute, Router } from '@angular/router';\nimport { Observable } from 'rxjs';\nimport { ProblemDetails } from '@orechio/platform-angular';\nimport { AccountClient, OpaqueAcknowledgement } from '../account-client';\nimport { ORECHIO_ACCOUNT_CONFIG } from '../account-config';\n\n/**\n * Base das duas telas que consomem um token da URL e definem uma senha:\n * `reset-password` e `accept-invite`.\n *\n * <p>\n * São o mesmo formulário com textos diferentes e uma chamada diferente. Duas cópias\n * divergiriam na primeira correção — e a primeira correção aqui seria justamente a regra do\n * token sair da URL, que é a que não pode falhar em nenhuma das duas.\n * </p>\n */\n@Component({\n selector: 'orechio-account-token-password',\n standalone: true,\n imports: [FormsModule],\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n <section class=\"orechio-account\">\n <ng-content select=\"[orechioAccountBrand]\"></ng-content>\n\n @if (concluido()) {\n <h1>{{ tituloDeSucesso() }}</h1>\n <p>Você já pode entrar com a nova senha.</p>\n <button type=\"button\" (click)=\"irParaLogin()\">Ir para o login</button>\n } @else if (semToken()) {\n <h1>Link inválido</h1>\n <p class=\"orechio-account__error\">Este link não é mais válido.</p>\n } @else {\n <h1>{{ titulo() }}</h1>\n\n <form (ngSubmit)=\"enviar()\">\n <label for=\"orechio-account-nova-senha\">Nova senha</label>\n <input\n id=\"orechio-account-nova-senha\"\n name=\"senha\"\n type=\"password\"\n autocomplete=\"new-password\"\n [(ngModel)]=\"senha\"\n [disabled]=\"enviando()\"\n required\n />\n\n @if (erro()) {\n <p class=\"orechio-account__error\">{{ mensagemDeErro() }}</p>\n }\n\n <button type=\"submit\" [disabled]=\"enviando() || !senha\">\n {{ enviando() ? 'Salvando…' : 'Salvar' }}\n </button>\n </form>\n }\n </section>\n `,\n styles: `\n .orechio-account {\n display: flex;\n flex-direction: column;\n gap: var(--orechio-space-3, 0.75rem);\n max-width: 28rem;\n margin: 0 auto;\n padding: var(--orechio-space-6, 1.5rem);\n color: var(--orechio-color-text, inherit);\n font-family: var(--orechio-font-body, inherit);\n }\n .orechio-account form { display: flex; flex-direction: column; gap: var(--orechio-space-2, 0.5rem); }\n .orechio-account__error { color: var(--orechio-color-danger, #b00020); }\n `,\n})\nexport class TokenPasswordPage implements OnInit {\n private readonly route = inject(ActivatedRoute);\n private readonly router = inject(Router);\n private readonly client = inject(AccountClient);\n private readonly config = inject(ORECHIO_ACCOUNT_CONFIG);\n\n private token = '';\n\n /**\n * `reset` ou `invite` — decide o texto e qual chamada do BFF usar.\n *\n * Lido do `data` da rota DIRETAMENTE, e não por `input()`. Um signal input só receberia\n * o `data` se o host tivesse habilitado `withComponentInputBinding()` no router — e, sem\n * isso, o `accept-invite` cairia no default `reset` e chamaria o endpoint errado. Um bug\n * silencioso, dependente de configuração do host, num fluxo que o usuário percorre uma\n * vez. Ler da rota funciona com qualquer configuração.\n */\n private readonly modo = (): 'reset' | 'invite' =>\n this.route.snapshot.data['modo'] === 'invite' ? 'invite' : 'reset';\n\n protected senha = '';\n protected readonly enviando = signal(false);\n protected readonly concluido = signal(false);\n protected readonly semToken = signal(false);\n protected readonly erro = signal<ProblemDetails | null>(null);\n\n protected titulo = () =>\n this.modo() === 'invite' ? 'Defina sua senha de acesso' : 'Escolha uma nova senha';\n\n protected tituloDeSucesso = () =>\n this.modo() === 'invite' ? 'Acesso liberado' : 'Senha alterada';\n\n protected mensagemDeErro(): string {\n const code = this.erro()?.code;\n if (code === 'token.invalid_or_expired') return 'Este link não é mais válido.';\n if (code === 'rate_limit_exceeded') return 'Muitas tentativas. Aguarde alguns minutos.';\n if (code === 'platform.unavailable') return 'Serviço indisponível. Tente novamente em instantes.';\n return 'Não foi possível salvar. Verifique a senha e tente de novo.';\n }\n\n async ngOnInit(): Promise<void> {\n const token = this.route.snapshot.queryParamMap.get('token');\n\n if (!token) {\n this.semToken.set(true);\n return;\n }\n\n this.token = token;\n\n // O token sai da URL ANTES de qualquer coisa — inclusive antes de o usuário digitar a\n // senha. Ele sobrevive no histórico, vaza por `Referer` para todo recurso de terceiro\n // que esta tela carregue, e entra em log de proxy. `replaceUrl: true` substitui a\n // entrada do histórico: sem isso, \"voltar\" traz a URL com o token de volta.\n await this.router.navigate([], {\n relativeTo: this.route,\n queryParams: {},\n replaceUrl: true,\n });\n }\n\n protected enviar(): void {\n if (!this.senha || this.enviando()) return;\n\n this.enviando.set(true);\n this.erro.set(null);\n\n const chamada: Observable<OpaqueAcknowledgement> =\n this.modo() === 'invite'\n ? this.client.acceptInvite(this.token, this.senha)\n : this.client.resetPassword(this.token, this.senha);\n\n chamada.subscribe({\n next: () => {\n // A senha não fica em memória depois de usada. Não é defesa contra atacante — quem\n // executa script na página já venceu —, é higiene: um dump de heap ou um relatório\n // de erro que serialize o componente não leva a senha junto.\n this.senha = '';\n this.token = '';\n this.concluido.set(true);\n this.enviando.set(false);\n },\n error: (problema: ProblemDetails) => {\n this.erro.set(problema);\n this.enviando.set(false);\n },\n });\n }\n\n protected irParaLogin(): void {\n void this.router.navigateByUrl(this.config.loginPath);\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { inject, signal, ChangeDetectionStrategy, Component } from '@angular/core';
|
|
3
|
+
import { ActivatedRoute, Router } from '@angular/router';
|
|
4
|
+
import { A as AccountClient } from './orechio-platform-angular-account-account-client-mPl0tzMg.mjs';
|
|
5
|
+
import { ORECHIO_ACCOUNT_CONFIG } from './orechio-platform-angular-account.mjs';
|
|
6
|
+
|
|
7
|
+
class VerifyEmailPage {
|
|
8
|
+
route = inject(ActivatedRoute);
|
|
9
|
+
router = inject(Router);
|
|
10
|
+
client = inject(AccountClient);
|
|
11
|
+
config = inject(ORECHIO_ACCOUNT_CONFIG);
|
|
12
|
+
estado = signal('confirmando', /* @ts-ignore */
|
|
13
|
+
...(ngDevMode ? [{ debugName: "estado" }] : /* istanbul ignore next */ []));
|
|
14
|
+
erro = signal(null, /* @ts-ignore */
|
|
15
|
+
...(ngDevMode ? [{ debugName: "erro" }] : /* istanbul ignore next */ []));
|
|
16
|
+
mensagemDeErro() {
|
|
17
|
+
const code = this.erro()?.code;
|
|
18
|
+
if (code === 'token.invalid_or_expired')
|
|
19
|
+
return 'Este link não é mais válido.';
|
|
20
|
+
if (code === 'rate_limit_exceeded')
|
|
21
|
+
return 'Muitas tentativas. Aguarde alguns minutos.';
|
|
22
|
+
return 'Tente novamente em instantes.';
|
|
23
|
+
}
|
|
24
|
+
ngOnInit() {
|
|
25
|
+
const token = this.route.snapshot.queryParamMap.get('token');
|
|
26
|
+
if (!token) {
|
|
27
|
+
this.estado.set('falhou');
|
|
28
|
+
this.erro.set({ code: 'token.invalid_or_expired', status: 400 });
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
this.client.verifyEmail(token).subscribe({
|
|
32
|
+
next: async (resultado) => {
|
|
33
|
+
// ── O token SAI da URL antes de qualquer navegação ────────────────────
|
|
34
|
+
//
|
|
35
|
+
// Um token de verificação na barra de endereços sobrevive ao histórico do
|
|
36
|
+
// navegador, vaza no cabeçalho `Referer` para qualquer recurso de terceiro que a
|
|
37
|
+
// próxima tela carregue, e entra em log de proxy. Ele já foi consumido neste
|
|
38
|
+
// ponto — mas "já foi consumido" é uma garantia do servidor, não uma razão para
|
|
39
|
+
// deixá-lo circulando.
|
|
40
|
+
//
|
|
41
|
+
// `replaceUrl: true` SUBSTITUI a entrada no histórico em vez de empilhar: com
|
|
42
|
+
// `false`, o botão "voltar" traria o usuário de volta à URL com o token.
|
|
43
|
+
await this.router.navigate([], {
|
|
44
|
+
relativeTo: this.route,
|
|
45
|
+
queryParams: {},
|
|
46
|
+
replaceUrl: true,
|
|
47
|
+
});
|
|
48
|
+
if (resultado.provisioningRequired) {
|
|
49
|
+
await this.router.navigate(['../onboarding/branch'], {
|
|
50
|
+
relativeTo: this.route,
|
|
51
|
+
replaceUrl: true,
|
|
52
|
+
});
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
// Sem provisioning, a conta já está operacional — mas o pacote não sabe o que o
|
|
56
|
+
// produto faz em seguida. Só o host sabe.
|
|
57
|
+
await this.navegarParaOHost();
|
|
58
|
+
},
|
|
59
|
+
error: (problema) => {
|
|
60
|
+
this.erro.set(problema);
|
|
61
|
+
this.estado.set('falhou');
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
async navegarParaOHost() {
|
|
66
|
+
// `verify-email` sem provisioning não devolve os ids do tenant — a conta foi ativada,
|
|
67
|
+
// a sessão existe, e o host decide o destino a partir do que ele já sabe. Os campos
|
|
68
|
+
// vazios são honestos: o pacote não inventa identificadores que não recebeu.
|
|
69
|
+
const destino = this.config.onCompleted({
|
|
70
|
+
tenantId: '',
|
|
71
|
+
branchId: '',
|
|
72
|
+
applicationId: '',
|
|
73
|
+
userId: '',
|
|
74
|
+
nextStep: 'OperationalReady',
|
|
75
|
+
});
|
|
76
|
+
await this.router.navigateByUrl(destino, { replaceUrl: true });
|
|
77
|
+
}
|
|
78
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: VerifyEmailPage, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
79
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.2", type: VerifyEmailPage, isStandalone: true, selector: "orechio-account-verify-email", ngImport: i0, template: `
|
|
80
|
+
<section class="orechio-account">
|
|
81
|
+
<ng-content select="[orechioAccountBrand]"></ng-content>
|
|
82
|
+
|
|
83
|
+
@if (estado() === 'confirmando') {
|
|
84
|
+
<h1>Confirmando seu e-mail…</h1>
|
|
85
|
+
<p class="orechio-account__muted">Isto leva só um instante.</p>
|
|
86
|
+
} @else if (estado() === 'falhou') {
|
|
87
|
+
<h1>Não foi possível confirmar</h1>
|
|
88
|
+
<p class="orechio-account__error">{{ mensagemDeErro() }}</p>
|
|
89
|
+
@if (erro()?.code === 'token.invalid_or_expired') {
|
|
90
|
+
<p class="orechio-account__muted">
|
|
91
|
+
Links de confirmação expiram. Peça um novo na tela de cadastro.
|
|
92
|
+
</p>
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
</section>
|
|
96
|
+
`, isInline: true, styles: [".orechio-account{display:flex;flex-direction:column;gap:var(--orechio-space-3, .75rem);max-width:28rem;margin:0 auto;padding:var(--orechio-space-6, 1.5rem);color:var(--orechio-color-text, inherit);font-family:var(--orechio-font-body, inherit)}.orechio-account__muted{color:var(--orechio-color-text-muted, #666)}.orechio-account__error{color:var(--orechio-color-danger, #b00020)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
97
|
+
}
|
|
98
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.2", ngImport: i0, type: VerifyEmailPage, decorators: [{
|
|
99
|
+
type: Component,
|
|
100
|
+
args: [{ selector: 'orechio-account-verify-email', standalone: true, changeDetection: ChangeDetectionStrategy.OnPush, template: `
|
|
101
|
+
<section class="orechio-account">
|
|
102
|
+
<ng-content select="[orechioAccountBrand]"></ng-content>
|
|
103
|
+
|
|
104
|
+
@if (estado() === 'confirmando') {
|
|
105
|
+
<h1>Confirmando seu e-mail…</h1>
|
|
106
|
+
<p class="orechio-account__muted">Isto leva só um instante.</p>
|
|
107
|
+
} @else if (estado() === 'falhou') {
|
|
108
|
+
<h1>Não foi possível confirmar</h1>
|
|
109
|
+
<p class="orechio-account__error">{{ mensagemDeErro() }}</p>
|
|
110
|
+
@if (erro()?.code === 'token.invalid_or_expired') {
|
|
111
|
+
<p class="orechio-account__muted">
|
|
112
|
+
Links de confirmação expiram. Peça um novo na tela de cadastro.
|
|
113
|
+
</p>
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
</section>
|
|
117
|
+
`, styles: [".orechio-account{display:flex;flex-direction:column;gap:var(--orechio-space-3, .75rem);max-width:28rem;margin:0 auto;padding:var(--orechio-space-6, 1.5rem);color:var(--orechio-color-text, inherit);font-family:var(--orechio-font-body, inherit)}.orechio-account__muted{color:var(--orechio-color-text-muted, #666)}.orechio-account__error{color:var(--orechio-color-danger, #b00020)}\n"] }]
|
|
118
|
+
}] });
|
|
119
|
+
|
|
120
|
+
export { VerifyEmailPage };
|
|
121
|
+
//# sourceMappingURL=orechio-platform-angular-account-verify-email.page-CHs0EwDf.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"orechio-platform-angular-account-verify-email.page-CHs0EwDf.mjs","sources":["../../../projects/platform-angular/account/src/pages/verify-email.page.ts"],"sourcesContent":["import { ChangeDetectionStrategy, Component, OnInit, inject, signal } from '@angular/core';\nimport { ActivatedRoute, Router } from '@angular/router';\nimport { ProblemDetails } from '@orechio/platform-angular';\nimport { AccountClient } from '../account-client';\nimport { ORECHIO_ACCOUNT_CONFIG } from '../account-config';\n\n@Component({\n selector: 'orechio-account-verify-email',\n standalone: true,\n changeDetection: ChangeDetectionStrategy.OnPush,\n template: `\n <section class=\"orechio-account\">\n <ng-content select=\"[orechioAccountBrand]\"></ng-content>\n\n @if (estado() === 'confirmando') {\n <h1>Confirmando seu e-mail…</h1>\n <p class=\"orechio-account__muted\">Isto leva só um instante.</p>\n } @else if (estado() === 'falhou') {\n <h1>Não foi possível confirmar</h1>\n <p class=\"orechio-account__error\">{{ mensagemDeErro() }}</p>\n @if (erro()?.code === 'token.invalid_or_expired') {\n <p class=\"orechio-account__muted\">\n Links de confirmação expiram. Peça um novo na tela de cadastro.\n </p>\n }\n }\n </section>\n `,\n styles: `\n .orechio-account {\n display: flex;\n flex-direction: column;\n gap: var(--orechio-space-3, 0.75rem);\n max-width: 28rem;\n margin: 0 auto;\n padding: var(--orechio-space-6, 1.5rem);\n color: var(--orechio-color-text, inherit);\n font-family: var(--orechio-font-body, inherit);\n }\n .orechio-account__muted { color: var(--orechio-color-text-muted, #666); }\n .orechio-account__error { color: var(--orechio-color-danger, #b00020); }\n `,\n})\nexport class VerifyEmailPage implements OnInit {\n private readonly route = inject(ActivatedRoute);\n private readonly router = inject(Router);\n private readonly client = inject(AccountClient);\n private readonly config = inject(ORECHIO_ACCOUNT_CONFIG);\n\n protected readonly estado = signal<'confirmando' | 'falhou'>('confirmando');\n protected readonly erro = signal<ProblemDetails | null>(null);\n\n protected mensagemDeErro(): string {\n const code = this.erro()?.code;\n if (code === 'token.invalid_or_expired') return 'Este link não é mais válido.';\n if (code === 'rate_limit_exceeded') return 'Muitas tentativas. Aguarde alguns minutos.';\n return 'Tente novamente em instantes.';\n }\n\n ngOnInit(): void {\n const token = this.route.snapshot.queryParamMap.get('token');\n\n if (!token) {\n this.estado.set('falhou');\n this.erro.set({ code: 'token.invalid_or_expired', status: 400 });\n return;\n }\n\n this.client.verifyEmail(token).subscribe({\n next: async (resultado) => {\n // ── O token SAI da URL antes de qualquer navegação ────────────────────\n //\n // Um token de verificação na barra de endereços sobrevive ao histórico do\n // navegador, vaza no cabeçalho `Referer` para qualquer recurso de terceiro que a\n // próxima tela carregue, e entra em log de proxy. Ele já foi consumido neste\n // ponto — mas \"já foi consumido\" é uma garantia do servidor, não uma razão para\n // deixá-lo circulando.\n //\n // `replaceUrl: true` SUBSTITUI a entrada no histórico em vez de empilhar: com\n // `false`, o botão \"voltar\" traria o usuário de volta à URL com o token.\n await this.router.navigate([], {\n relativeTo: this.route,\n queryParams: {},\n replaceUrl: true,\n });\n\n if (resultado.provisioningRequired) {\n await this.router.navigate(['../onboarding/branch'], {\n relativeTo: this.route,\n replaceUrl: true,\n });\n return;\n }\n\n // Sem provisioning, a conta já está operacional — mas o pacote não sabe o que o\n // produto faz em seguida. Só o host sabe.\n await this.navegarParaOHost();\n },\n error: (problema: ProblemDetails) => {\n this.erro.set(problema);\n this.estado.set('falhou');\n },\n });\n }\n\n private async navegarParaOHost(): Promise<void> {\n // `verify-email` sem provisioning não devolve os ids do tenant — a conta foi ativada,\n // a sessão existe, e o host decide o destino a partir do que ele já sabe. Os campos\n // vazios são honestos: o pacote não inventa identificadores que não recebeu.\n const destino = this.config.onCompleted({\n tenantId: '',\n branchId: '',\n applicationId: '',\n userId: '',\n nextStep: 'OperationalReady',\n });\n\n await this.router.navigateByUrl(destino, { replaceUrl: true });\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { InjectionToken } from '@angular/core';
|
|
2
|
+
|
|
3
|
+
const ORECHIO_ACCOUNT_CONFIG = new InjectionToken('ORECHIO_ACCOUNT_CONFIG');
|
|
4
|
+
function provideOrechioAccount(options) {
|
|
5
|
+
if (typeof options?.onCompleted !== 'function') {
|
|
6
|
+
// Falha na composição, não no primeiro cliente que termina o cadastro. Sem
|
|
7
|
+
// `onCompleted` o pacote não teria para onde mandar ninguém, e o sintoma apareceria
|
|
8
|
+
// exatamente no pior momento: com o usuário já cadastrado, olhando uma tela parada.
|
|
9
|
+
throw new Error('provideOrechioAccount: `onCompleted` é obrigatório — é o host que decide a navegação.');
|
|
10
|
+
}
|
|
11
|
+
const basePath = (options.basePath ?? '/api/account').replace(/\/+$/, '');
|
|
12
|
+
const config = {
|
|
13
|
+
basePath,
|
|
14
|
+
paths: {
|
|
15
|
+
verifyEmail: options.paths?.verifyEmail ?? '/verify-email',
|
|
16
|
+
initialBranch: options.paths?.initialBranch ?? '/onboarding/branch',
|
|
17
|
+
forgotPassword: options.paths?.forgotPassword ?? '/forgot-password',
|
|
18
|
+
resetPassword: options.paths?.resetPassword ?? '/reset-password',
|
|
19
|
+
acceptInvite: options.paths?.acceptInvite ?? '/accept-invite',
|
|
20
|
+
resendVerification: options.paths?.resendVerification ?? '/resend-verification',
|
|
21
|
+
},
|
|
22
|
+
onCompleted: options.onCompleted,
|
|
23
|
+
branchLabel: options.branchLabel ?? 'unidade',
|
|
24
|
+
loginPath: options.loginPath ?? '/login',
|
|
25
|
+
};
|
|
26
|
+
return [{ provide: ORECHIO_ACCOUNT_CONFIG, useValue: config }];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* As rotas de conta, para o host registrar ONDE ELE QUISER.
|
|
31
|
+
*
|
|
32
|
+
* <p>
|
|
33
|
+
* São `Routes[]` e não um módulo com prefixo fixo de propósito: o produto monta em
|
|
34
|
+
* `/conta`, em `/auth`, na raiz, ou espalhadas — o pacote não reivindica caminho. O que ele
|
|
35
|
+
* fixa são os caminhos RELATIVOS entre elas, porque o `verify-email` navega para
|
|
36
|
+
* `../onboarding/branch` quando há provisioning pendente.
|
|
37
|
+
* </p>
|
|
38
|
+
*
|
|
39
|
+
* <pre>
|
|
40
|
+
* // no host:
|
|
41
|
+
* { path: 'conta', children: ORECHIO_ACCOUNT_ROUTES }
|
|
42
|
+
* </pre>
|
|
43
|
+
*
|
|
44
|
+
* <p>
|
|
45
|
+
* Todas com `loadComponent`: quem só usa recuperação de senha não carrega a tela de
|
|
46
|
+
* primeira filial, e o bundle inicial do produto não paga por telas que a maioria dos
|
|
47
|
+
* usuários vê uma vez na vida.
|
|
48
|
+
* </p>
|
|
49
|
+
*/
|
|
50
|
+
const ORECHIO_ACCOUNT_ROUTES = [
|
|
51
|
+
{
|
|
52
|
+
path: 'verify-email',
|
|
53
|
+
title: 'Confirmando e-mail',
|
|
54
|
+
loadComponent: () => import('./orechio-platform-angular-account-verify-email.page-CHs0EwDf.mjs').then((m) => m.VerifyEmailPage),
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
path: 'onboarding/branch',
|
|
58
|
+
title: 'Primeira unidade',
|
|
59
|
+
loadComponent: () => import('./orechio-platform-angular-account-initial-branch.page-Cmy07JGF.mjs').then((m) => m.InitialBranchPage),
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
path: 'forgot-password',
|
|
63
|
+
title: 'Recuperar acesso',
|
|
64
|
+
loadComponent: () => import('./orechio-platform-angular-account-forgot-password.page-5_8R5B4m.mjs').then((m) => m.ForgotPasswordPage),
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
path: 'reset-password',
|
|
68
|
+
title: 'Nova senha',
|
|
69
|
+
loadComponent: () => import('./orechio-platform-angular-account-token-password.page-AHqb8mFk.mjs').then((m) => m.TokenPasswordPage),
|
|
70
|
+
// `reset` também é o default do componente; declarado aqui para a rota dizer o que faz
|
|
71
|
+
// sem exigir que se leia a implementação.
|
|
72
|
+
data: { modo: 'reset' },
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
path: 'accept-invite',
|
|
76
|
+
title: 'Primeiro acesso',
|
|
77
|
+
loadComponent: () => import('./orechio-platform-angular-account-token-password.page-AHqb8mFk.mjs').then((m) => m.TokenPasswordPage),
|
|
78
|
+
data: { modo: 'invite' },
|
|
79
|
+
},
|
|
80
|
+
{
|
|
81
|
+
path: 'resend-verification',
|
|
82
|
+
title: 'Reenviar confirmação',
|
|
83
|
+
loadComponent: () => import('./orechio-platform-angular-account-resend-verification.page-qckKAlPk.mjs').then((m) => m.ResendVerificationPage),
|
|
84
|
+
},
|
|
85
|
+
];
|
|
86
|
+
|
|
87
|
+
/*
|
|
88
|
+
* Public API Surface of @orechio/platform-angular/account
|
|
89
|
+
*
|
|
90
|
+
* ACCOUNT-1C — o entry de conta. Ele traz as TELAS do onboarding compartilhado; a sessão,
|
|
91
|
+
* o login e os interceptors continuam no entry primário, onde já estavam (ADR-019 §5.1:
|
|
92
|
+
* login/refresh/logout são KEEP-SEPARATE — Store e Portal resolvem sessão de formas
|
|
93
|
+
* materialmente diferentes).
|
|
94
|
+
*
|
|
95
|
+
* O que NÃO é exportado é parte do contrato: `AccountClient` é detalhe de implementação, e
|
|
96
|
+
* as páginas são alcançadas por `ORECHIO_ACCOUNT_ROUTES`, nunca importadas diretamente —
|
|
97
|
+
* exportá-las convidaria o host a montar a própria árvore de rotas por cima delas, e os
|
|
98
|
+
* caminhos relativos entre as telas deixariam de valer.
|
|
99
|
+
*/
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Generated bundle index. Do not edit.
|
|
103
|
+
*/
|
|
104
|
+
|
|
105
|
+
export { ORECHIO_ACCOUNT_CONFIG, ORECHIO_ACCOUNT_ROUTES, provideOrechioAccount };
|
|
106
|
+
//# sourceMappingURL=orechio-platform-angular-account.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"orechio-platform-angular-account.mjs","sources":["../../../projects/platform-angular/account/src/account-config.ts","../../../projects/platform-angular/account/src/account.routes.ts","../../../projects/platform-angular/account/src/public-api.ts","../../../projects/platform-angular/account/src/orechio-platform-angular-account.ts"],"sourcesContent":["import { InjectionToken, Provider } from '@angular/core';\nimport { UrlTree } from '@angular/router';\n\n/**\n * A fronteira do core compartilhado, como o browser a recebe (ADR-019 §3).\n *\n * <p>\n * Sem token nenhum, e isso é o contrato — não uma omissão. O BFF recebe os tokens da\n * Platform e os entrega ao mecanismo de sessão do host; o que chega aqui é o estado da\n * conta, nunca o material dela.\n * </p>\n */\nexport interface AccountCompletionResult {\n tenantId: string;\n branchId: string;\n applicationId: string;\n userId: string;\n /**\n * `OperationalReady` ou `ProductContinuation` — enum fechado do lado da Platform.\n * Trafega como string e é repassado **sem interpretação**: quem traduz isso em rota é o\n * host, nunca este pacote.\n */\n nextStep: string;\n}\n\n/** Rotas do BFF que o host montou. Defaults batem com `MapOrechioAccountEndpoints`. */\nexport interface OrechioAccountPaths {\n verifyEmail?: string;\n initialBranch?: string;\n forgotPassword?: string;\n resetPassword?: string;\n acceptInvite?: string;\n resendVerification?: string;\n}\n\nexport interface OrechioAccountConfig {\n /**\n * Prefixo do BFF de conta, same-origin. Nunca uma URL absoluta cross-origin: o modelo é\n * BFF no domínio do produto, e é isso que faz o cookie ser first-party.\n */\n basePath: string;\n\n paths: Required<OrechioAccountPaths>;\n\n /**\n * **O único ponto de extensão do pacote.**\n *\n * Chamado quando a conta fica operacional. O host decide para onde ir: um produto com\n * cadastro próprio manda para o passo dele, um produto sem passo próprio manda direto para\n * o painel. O pacote não conhece nenhuma dessas rotas, e é essa ignorância que o mantém\n * compartilhável (ADR-019 §3).\n *\n * Nem os nomes desses destinos aparecem aqui — nem em comentário. Um exemplo concreto de\n * um produto irmão é a primeira forma do acoplamento: vira referência, depois default,\n * depois `if`. A guarda de fronteira varre estes fontes e falha em termo vertical.\n */\n onCompleted: (result: AccountCompletionResult) => UrlTree | string;\n\n /**\n * Como o produto chama a primeira unidade operacional, no vocabulário dele. \"Unidade\" é o\n * default por ser o termo mais neutro que existe — cada produto tem o seu, e nenhum deles\n * é assunto do pacote. É por isso que isto é configuração e não literal na tela.\n */\n branchLabel: string;\n\n /** Para onde mandar quem termina um fluxo que não abre sessão (reset, convite aceito). */\n loginPath: string;\n}\n\nexport const ORECHIO_ACCOUNT_CONFIG = new InjectionToken<OrechioAccountConfig>(\n 'ORECHIO_ACCOUNT_CONFIG',\n);\n\nexport interface OrechioAccountOptions {\n basePath?: string;\n paths?: OrechioAccountPaths;\n onCompleted: OrechioAccountConfig['onCompleted'];\n branchLabel?: string;\n loginPath?: string;\n}\n\nexport function provideOrechioAccount(options: OrechioAccountOptions): Provider[] {\n if (typeof options?.onCompleted !== 'function') {\n // Falha na composição, não no primeiro cliente que termina o cadastro. Sem\n // `onCompleted` o pacote não teria para onde mandar ninguém, e o sintoma apareceria\n // exatamente no pior momento: com o usuário já cadastrado, olhando uma tela parada.\n throw new Error(\n 'provideOrechioAccount: `onCompleted` é obrigatório — é o host que decide a navegação.',\n );\n }\n\n const basePath = (options.basePath ?? '/api/account').replace(/\\/+$/, '');\n\n const config: OrechioAccountConfig = {\n basePath,\n paths: {\n verifyEmail: options.paths?.verifyEmail ?? '/verify-email',\n initialBranch: options.paths?.initialBranch ?? '/onboarding/branch',\n forgotPassword: options.paths?.forgotPassword ?? '/forgot-password',\n resetPassword: options.paths?.resetPassword ?? '/reset-password',\n acceptInvite: options.paths?.acceptInvite ?? '/accept-invite',\n resendVerification: options.paths?.resendVerification ?? '/resend-verification',\n },\n onCompleted: options.onCompleted,\n branchLabel: options.branchLabel ?? 'unidade',\n loginPath: options.loginPath ?? '/login',\n };\n\n return [{ provide: ORECHIO_ACCOUNT_CONFIG, useValue: config }];\n}\n","import { Routes } from '@angular/router';\n\n/**\n * As rotas de conta, para o host registrar ONDE ELE QUISER.\n *\n * <p>\n * São `Routes[]` e não um módulo com prefixo fixo de propósito: o produto monta em\n * `/conta`, em `/auth`, na raiz, ou espalhadas — o pacote não reivindica caminho. O que ele\n * fixa são os caminhos RELATIVOS entre elas, porque o `verify-email` navega para\n * `../onboarding/branch` quando há provisioning pendente.\n * </p>\n *\n * <pre>\n * // no host:\n * { path: 'conta', children: ORECHIO_ACCOUNT_ROUTES }\n * </pre>\n *\n * <p>\n * Todas com `loadComponent`: quem só usa recuperação de senha não carrega a tela de\n * primeira filial, e o bundle inicial do produto não paga por telas que a maioria dos\n * usuários vê uma vez na vida.\n * </p>\n */\nexport const ORECHIO_ACCOUNT_ROUTES: Routes = [\n {\n path: 'verify-email',\n title: 'Confirmando e-mail',\n loadComponent: () => import('./pages/verify-email.page').then((m) => m.VerifyEmailPage),\n },\n {\n path: 'onboarding/branch',\n title: 'Primeira unidade',\n loadComponent: () => import('./pages/initial-branch.page').then((m) => m.InitialBranchPage),\n },\n {\n path: 'forgot-password',\n title: 'Recuperar acesso',\n loadComponent: () => import('./pages/forgot-password.page').then((m) => m.ForgotPasswordPage),\n },\n {\n path: 'reset-password',\n title: 'Nova senha',\n loadComponent: () => import('./pages/token-password.page').then((m) => m.TokenPasswordPage),\n // `reset` também é o default do componente; declarado aqui para a rota dizer o que faz\n // sem exigir que se leia a implementação.\n data: { modo: 'reset' },\n },\n {\n path: 'accept-invite',\n title: 'Primeiro acesso',\n loadComponent: () => import('./pages/token-password.page').then((m) => m.TokenPasswordPage),\n data: { modo: 'invite' },\n },\n {\n path: 'resend-verification',\n title: 'Reenviar confirmação',\n loadComponent: () =>\n import('./pages/resend-verification.page').then((m) => m.ResendVerificationPage),\n },\n];\n","/*\n * Public API Surface of @orechio/platform-angular/account\n *\n * ACCOUNT-1C — o entry de conta. Ele traz as TELAS do onboarding compartilhado; a sessão,\n * o login e os interceptors continuam no entry primário, onde já estavam (ADR-019 §5.1:\n * login/refresh/logout são KEEP-SEPARATE — Store e Portal resolvem sessão de formas\n * materialmente diferentes).\n *\n * O que NÃO é exportado é parte do contrato: `AccountClient` é detalhe de implementação, e\n * as páginas são alcançadas por `ORECHIO_ACCOUNT_ROUTES`, nunca importadas diretamente —\n * exportá-las convidaria o host a montar a própria árvore de rotas por cima delas, e os\n * caminhos relativos entre as telas deixariam de valer.\n */\n\nexport type {\n AccountCompletionResult,\n OrechioAccountConfig,\n OrechioAccountOptions,\n OrechioAccountPaths,\n} from './account-config';\nexport { ORECHIO_ACCOUNT_CONFIG, provideOrechioAccount } from './account-config';\n\nexport { ORECHIO_ACCOUNT_ROUTES } from './account.routes';\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|