@foodmarketmaker/marketmaker-auth 0.0.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.
@@ -0,0 +1,835 @@
1
+ import * as i0 from '@angular/core';
2
+ import { signal, computed, Injectable, inject, effect, ViewChild, ChangeDetectionStrategy, Component } from '@angular/core';
3
+ import { Amplify } from 'aws-amplify';
4
+ import { CookieStorage } from 'aws-amplify/utils';
5
+ import { cognitoUserPoolsTokenProvider } from 'aws-amplify/auth/cognito';
6
+ import { signIn, signUp, confirmSignUp, resendSignUpCode, signInWithRedirect, signOut, resetPassword, confirmResetPassword, fetchAuthSession } from 'aws-amplify/auth';
7
+ import { CommonModule } from '@angular/common';
8
+ import * as i1$1 from '@fortawesome/angular-fontawesome';
9
+ import { FontAwesomeModule } from '@fortawesome/angular-fontawesome';
10
+ import { faTimes } from '@fortawesome/pro-light-svg-icons';
11
+ import * as i1 from '@angular/forms';
12
+ import { FormsModule } from '@angular/forms';
13
+ import { NgxOtpInputComponent } from 'ngx-otp-input';
14
+
15
+ const SignUpNextSteps = {
16
+ DONE: 'DONE',
17
+ CONFIRM_SIGN_UP: 'CONFIRM_SIGN_UP',
18
+ };
19
+ const SignInNextSteps = {
20
+ DONE: 'DONE',
21
+ RESET_PASSWORD: 'RESET_PASSWORD',
22
+ CONFIRM_SIGN_UP: 'CONFIRM_SIGN_UP',
23
+ };
24
+ const ResetPasswordNextSteps = {
25
+ DONE: 'DONE',
26
+ CONFIRM_RESET_PASSWORD_WITH_CODE: 'CONFIRM_RESET_PASSWORD_WITH_CODE',
27
+ };
28
+ const AMAZON = 'Amazon';
29
+ const APPLE = 'Apple';
30
+ const FACEBOOK = 'Facebook';
31
+ const GOOGLE = 'Google';
32
+ class AuthService {
33
+ devClientId = '2q2fh1vn4c2ce9nfe516tl1dld';
34
+ prodClientId = '76htqb41727bo3v0096hpl8qnm';
35
+ _signedInSignal = signal(false, ...(ngDevMode ? [{ debugName: "_signedInSignal" }] : /* istanbul ignore next */ []));
36
+ _usernameSignal = signal(undefined, ...(ngDevMode ? [{ debugName: "_usernameSignal" }] : /* istanbul ignore next */ []));
37
+ _idTokenSignal = signal(undefined, ...(ngDevMode ? [{ debugName: "_idTokenSignal" }] : /* istanbul ignore next */ []));
38
+ _accessTokenSignal = signal(undefined, ...(ngDevMode ? [{ debugName: "_accessTokenSignal" }] : /* istanbul ignore next */ []));
39
+ impersonating = false;
40
+ impersonatingEmail = undefined;
41
+ constructor() {
42
+ this.initAuth();
43
+ this.refreshSession().then(() => {
44
+ console.log('AuthService: session refreshed');
45
+ });
46
+ }
47
+ get signedInSignal() {
48
+ return this._signedInSignal.asReadonly();
49
+ }
50
+ get idTokenSignal() {
51
+ return this._idTokenSignal.asReadonly();
52
+ }
53
+ get accessTokenSignal() {
54
+ return this._accessTokenSignal.asReadonly();
55
+ }
56
+ get usernameSignal() {
57
+ return this._usernameSignal.asReadonly();
58
+ }
59
+ bearerTokenSignal = computed(() => {
60
+ const token = this._idTokenSignal();
61
+ return token ? `Bearer ${token.toString()}` : undefined;
62
+ }, ...(ngDevMode ? [{ debugName: "bearerTokenSignal" }] : /* istanbul ignore next */ []));
63
+ get emailSignal() {
64
+ return computed(() => this.extractFromIdToken('email'));
65
+ }
66
+ get nameSignal() {
67
+ return computed(() => this.extractFromIdToken('name'));
68
+ }
69
+ get pictureSignal() {
70
+ return computed(() => this.extractFromIdToken('picture'));
71
+ }
72
+ get signedIn() {
73
+ return this._signedInSignal();
74
+ }
75
+ get bearerToken() {
76
+ return this.bearerTokenSignal();
77
+ }
78
+ get email() {
79
+ return this.emailSignal();
80
+ }
81
+ buildPoolConfig() {
82
+ const url = location.origin + '/';
83
+ const isLocal = location.host.startsWith('local');
84
+ return {
85
+ userPoolClientId: isLocal ? this.devClientId : this.prodClientId,
86
+ redirectSignInUrl: url,
87
+ redirectSignOutUrl: url,
88
+ };
89
+ }
90
+ initAuth() {
91
+ const config = this.buildPoolConfig();
92
+ Amplify.configure({
93
+ Auth: {
94
+ Cognito: {
95
+ userPoolId: 'us-west-2_p1Epu0go8',
96
+ userPoolClientId: config.userPoolClientId,
97
+ identityPoolId: 'us-west-2:008f23d3-6c9f-4aa4-ad6f-6a4db545024c',
98
+ loginWith: {
99
+ oauth: {
100
+ domain: 'signinsample98d73a66-98d73a66-dev.auth.us-west-2.amazoncognito.com',
101
+ scopes: ['phone', 'email', 'openid', 'profile', 'aws.cognito.signin.user.admin'],
102
+ redirectSignIn: [config.redirectSignInUrl],
103
+ redirectSignOut: [config.redirectSignOutUrl],
104
+ responseType: 'code',
105
+ },
106
+ },
107
+ },
108
+ },
109
+ });
110
+ const domain = this.domainFromUrl(window.location.href) ?? undefined;
111
+ const secure = window.location.protocol.includes('https');
112
+ cognitoUserPoolsTokenProvider.setKeyValueStorage(new CookieStorage({
113
+ domain,
114
+ path: '/',
115
+ sameSite: 'strict',
116
+ expires: 31,
117
+ secure,
118
+ }));
119
+ }
120
+ domainFromUrl(url) {
121
+ const lower = url.toLowerCase();
122
+ if (lower.includes('localhost'))
123
+ return 'localhost';
124
+ if (lower.includes('betafoodmarketmaker.com'))
125
+ return 'betafoodmarketmaker.com';
126
+ if (lower.includes('foodmarketmaker.com'))
127
+ return 'foodmarketmaker.com';
128
+ return null;
129
+ }
130
+ async signIn(credentials) {
131
+ try {
132
+ const { isSignedIn, nextStep } = await signIn({
133
+ username: credentials.username,
134
+ password: credentials.password,
135
+ });
136
+ if (isSignedIn) {
137
+ await this.refreshSession();
138
+ return { isSignedIn: true };
139
+ }
140
+ return { isSignedIn: false, nextStep: nextStep.signInStep };
141
+ }
142
+ catch (error) {
143
+ return { isSignedIn: false, error: this.errorMessage(error, 'sign in', credentials.username) };
144
+ }
145
+ }
146
+ async signUp(credentials) {
147
+ try {
148
+ const { isSignUpComplete, nextStep } = await signUp({
149
+ username: credentials.username,
150
+ password: credentials.password,
151
+ options: { userAttributes: { email: credentials.email } },
152
+ });
153
+ if (isSignUpComplete)
154
+ return { isSignedUp: true };
155
+ return { isSignedUp: false, nextStep: nextStep.signUpStep };
156
+ }
157
+ catch (error) {
158
+ return { isSignedUp: false, error: this.errorMessage(error, 'sign up', credentials.username) };
159
+ }
160
+ }
161
+ async confirmSignUp(username, code) {
162
+ try {
163
+ const { isSignUpComplete, nextStep } = await confirmSignUp({ username, confirmationCode: code });
164
+ if (isSignUpComplete)
165
+ return { isSignedUp: true };
166
+ return { isSignedUp: false, nextStep: nextStep.signUpStep };
167
+ }
168
+ catch (error) {
169
+ return { isSignedUp: false, error: this.errorMessage(error, 'confirm account', username) };
170
+ }
171
+ }
172
+ async resendSignUpCode(username) {
173
+ try {
174
+ await resendSignUpCode({ username });
175
+ return { isCodeRequested: true };
176
+ }
177
+ catch (error) {
178
+ return { isCodeRequested: false, error: this.errorMessage(error, 'resend code', username) };
179
+ }
180
+ }
181
+ async socialSignIn(provider) {
182
+ try {
183
+ await signInWithRedirect({ provider: provider });
184
+ await this.refreshSession();
185
+ return { isSignedIn: true };
186
+ }
187
+ catch (error) {
188
+ return { isSignedIn: false, error: this.errorMessage(error, 'social sign in') };
189
+ }
190
+ }
191
+ async signOut() {
192
+ if (!this.signedIn)
193
+ return;
194
+ try {
195
+ await signOut();
196
+ }
197
+ catch (error) {
198
+ console.error('AuthService: signOut error', error);
199
+ }
200
+ finally {
201
+ this._signedInSignal.set(false);
202
+ this._idTokenSignal.set(undefined);
203
+ this._accessTokenSignal.set(undefined);
204
+ }
205
+ }
206
+ async resetPassword(username) {
207
+ try {
208
+ const { isPasswordReset, nextStep } = await resetPassword({ username });
209
+ if (isPasswordReset)
210
+ return { isPasswordReset: true };
211
+ return { isPasswordReset: false, nextStep: nextStep.resetPasswordStep };
212
+ }
213
+ catch (error) {
214
+ return { isPasswordReset: false, error: this.errorMessage(error, 'reset password', username) };
215
+ }
216
+ }
217
+ async confirmResetPassword(username, newPassword, code) {
218
+ try {
219
+ await confirmResetPassword({ username, confirmationCode: code, newPassword });
220
+ return { isPasswordReset: true };
221
+ }
222
+ catch (error) {
223
+ return { isPasswordReset: false, error: this.errorMessage(error, 'confirm new password', username) };
224
+ }
225
+ }
226
+ async refreshSession() {
227
+ const { idToken, accessToken } = (await fetchAuthSession()).tokens ?? {};
228
+ if (idToken !== this._idTokenSignal()) {
229
+ this._idTokenSignal.set(idToken);
230
+ this._signedInSignal.set(idToken != null);
231
+ }
232
+ if (accessToken !== this._accessTokenSignal()) {
233
+ this._accessTokenSignal.set(accessToken);
234
+ }
235
+ }
236
+ isAdmin() {
237
+ return this.hasRole('admin');
238
+ }
239
+ hasRole(rolename) {
240
+ const groups = this.getUserGroups();
241
+ return groups.indexOf(rolename) >= 0;
242
+ }
243
+ impersonate(email) {
244
+ if (!this.isAdmin())
245
+ return false;
246
+ localStorage.setItem('impersonating-email', email);
247
+ localStorage.setItem('impersonating', 'true');
248
+ this.impersonating = true;
249
+ this.impersonatingEmail = email;
250
+ return true;
251
+ }
252
+ cancelImpersonate() {
253
+ this.impersonating = false;
254
+ this.impersonatingEmail = undefined;
255
+ localStorage.removeItem('impersonating-email');
256
+ localStorage.setItem('impersonating', 'false');
257
+ }
258
+ getUserGroups() {
259
+ const payload = this._idTokenSignal()?.payload;
260
+ if (payload && 'cognito:groups' in payload) {
261
+ return payload['cognito:groups'];
262
+ }
263
+ return [];
264
+ }
265
+ extractFromIdToken(property) {
266
+ const payload = this._idTokenSignal()?.payload;
267
+ if (!payload)
268
+ return undefined;
269
+ return property in payload ? payload[property] : undefined;
270
+ }
271
+ errorMessage(error, operation, username) {
272
+ const e = error;
273
+ if (e?.__type) {
274
+ if (e.__type === 'UserNotFoundException' && username)
275
+ return `The user "${username}" was not found.`;
276
+ if (e.__type === 'LimitExceededException')
277
+ return `Too many ${operation} attempts.`;
278
+ if (e.__type === 'UsernameExistsException')
279
+ return 'User already exists.';
280
+ }
281
+ if (e?.message) {
282
+ return e.message.includes(':') ? e.message.split(':')[1].trim() : e.message;
283
+ }
284
+ return 'An unknown error occurred.';
285
+ }
286
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: AuthService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
287
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: AuthService, providedIn: 'root' });
288
+ }
289
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: AuthService, decorators: [{
290
+ type: Injectable,
291
+ args: [{
292
+ providedIn: 'root',
293
+ }]
294
+ }], ctorParameters: () => [] });
295
+
296
+ const AuthModes = {
297
+ SIGN_IN: 'sign-in',
298
+ SIGN_UP: 'sign-up',
299
+ FORGOT_PASSWORD: 'forgot-password',
300
+ CONFIRM_ACCOUNT: 'confirm-account',
301
+ CONFIRM_PASSWORD_RESET: 'confirm-password-reset',
302
+ };
303
+
304
+ class AuthStateService {
305
+ _usernameSignal = signal('', ...(ngDevMode ? [{ debugName: "_usernameSignal" }] : /* istanbul ignore next */ []));
306
+ _passwordSignal = signal('', ...(ngDevMode ? [{ debugName: "_passwordSignal" }] : /* istanbul ignore next */ []));
307
+ _emailSignal = signal('', ...(ngDevMode ? [{ debugName: "_emailSignal" }] : /* istanbul ignore next */ []));
308
+ _modeSignal = signal(AuthModes.SIGN_IN, ...(ngDevMode ? [{ debugName: "_modeSignal" }] : /* istanbul ignore next */ []));
309
+ _errorSignal = signal(null, ...(ngDevMode ? [{ debugName: "_errorSignal" }] : /* istanbul ignore next */ []));
310
+ _autoCloseSignal = signal(false, ...(ngDevMode ? [{ debugName: "_autoCloseSignal" }] : /* istanbul ignore next */ []));
311
+ _displayingSignal = signal(false, ...(ngDevMode ? [{ debugName: "_displayingSignal" }] : /* istanbul ignore next */ []));
312
+ _willCloseSignal = signal(false, ...(ngDevMode ? [{ debugName: "_willCloseSignal" }] : /* istanbul ignore next */ []));
313
+ get modeSignal() {
314
+ return this._modeSignal.asReadonly();
315
+ }
316
+ setMode(mode) {
317
+ this._modeSignal.set(mode);
318
+ }
319
+ get displayingSignal() {
320
+ return this._displayingSignal.asReadonly();
321
+ }
322
+ open() {
323
+ this.setMode(AuthModes.SIGN_IN);
324
+ this._displayingSignal.set(true);
325
+ }
326
+ close() {
327
+ this._willCloseSignal.set(true);
328
+ }
329
+ didClose() {
330
+ this._willCloseSignal.set(false);
331
+ this._displayingSignal.set(false);
332
+ }
333
+ get willCloseSignal() {
334
+ return this._willCloseSignal.asReadonly();
335
+ }
336
+ get usernameSignal() {
337
+ return this._usernameSignal.asReadonly();
338
+ }
339
+ setUsername(username) {
340
+ this._usernameSignal.set(username);
341
+ }
342
+ get passwordSignal() {
343
+ return this._passwordSignal.asReadonly();
344
+ }
345
+ setPassword(password) {
346
+ this._passwordSignal.set(password);
347
+ }
348
+ get emailSignal() {
349
+ return this._emailSignal.asReadonly();
350
+ }
351
+ setEmail(email) {
352
+ this._emailSignal.set(email);
353
+ }
354
+ get errorSignal() {
355
+ return this._errorSignal.asReadonly();
356
+ }
357
+ setError(error) {
358
+ this._errorSignal.set(error);
359
+ }
360
+ resetError() {
361
+ this._errorSignal.set(null);
362
+ }
363
+ get autoCloseSignal() {
364
+ return this._autoCloseSignal.asReadonly();
365
+ }
366
+ setAutoCloseError(message, delay = 4000) {
367
+ this._autoCloseSignal.set(true);
368
+ this.setError(message);
369
+ setTimeout(() => {
370
+ this._autoCloseSignal.set(false);
371
+ this.resetError();
372
+ }, delay);
373
+ }
374
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: AuthStateService, deps: [], target: i0.ɵɵFactoryTarget.Injectable });
375
+ static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: AuthStateService, providedIn: 'root' });
376
+ }
377
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: AuthStateService, decorators: [{
378
+ type: Injectable,
379
+ args: [{
380
+ providedIn: 'root',
381
+ }]
382
+ }] });
383
+
384
+ class AuthSignInComponent {
385
+ auth = inject(AuthService);
386
+ authState = inject(AuthStateService);
387
+ usernameInput;
388
+ username = '';
389
+ password = '';
390
+ signInBusy = false;
391
+ amazon = AMAZON;
392
+ google = GOOGLE;
393
+ facebook = FACEBOOK;
394
+ heading = 'Sign in to MarketMaker';
395
+ welcome = 'Welcome back! Please sign in to continue.';
396
+ haveUsername = computed(() => this.username.trim().length > 6, ...(ngDevMode ? [{ debugName: "haveUsername" }] : /* istanbul ignore next */ []));
397
+ constructor() {
398
+ effect(() => { this.password = this.authState.passwordSignal(); });
399
+ effect(() => { this.username = this.authState.usernameSignal(); });
400
+ }
401
+ ngAfterViewInit() {
402
+ this.usernameInput?.nativeElement.focus();
403
+ }
404
+ onSignIn() {
405
+ this.signInBusy = true;
406
+ this.auth.signIn({ username: this.username, password: this.password }).then((res) => {
407
+ this.signInBusy = false;
408
+ if (res.isSignedIn) {
409
+ this.notifyClose();
410
+ }
411
+ else if (res.error) {
412
+ this.authState.setError(res.error);
413
+ }
414
+ else if (res.nextStep === SignInNextSteps.CONFIRM_SIGN_UP) {
415
+ this.onSwitchConfirmAccount();
416
+ }
417
+ else if (res.nextStep === SignInNextSteps.RESET_PASSWORD) {
418
+ this.onSwitchPasswordReset();
419
+ }
420
+ });
421
+ }
422
+ onSocialSignIn(provider) {
423
+ this.signInBusy = true;
424
+ this.auth.socialSignIn(provider).then(() => {
425
+ this.signInBusy = false;
426
+ this.notifyClose();
427
+ });
428
+ }
429
+ notifyClose() {
430
+ this.saveCredentials();
431
+ this.authState.close();
432
+ }
433
+ saveCredentials() {
434
+ this.authState.setPassword(this.password);
435
+ this.authState.setUsername(this.username);
436
+ }
437
+ onSwitchSignUp() {
438
+ this.saveCredentials();
439
+ this.authState.setMode(AuthModes.SIGN_UP);
440
+ }
441
+ onSwitchPasswordReset() {
442
+ this.saveCredentials();
443
+ this.authState.setMode(AuthModes.FORGOT_PASSWORD);
444
+ }
445
+ onSwitchConfirmAccount() {
446
+ this.saveCredentials();
447
+ this.authState.setMode(AuthModes.CONFIRM_ACCOUNT);
448
+ }
449
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: AuthSignInComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
450
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.15", type: AuthSignInComponent, isStandalone: true, selector: "mma-auth-signin", viewQueries: [{ propertyName: "usernameInput", first: true, predicate: ["usernameInput"], descendants: true }], ngImport: i0, template: "<div class=\"auth-container\">\n <div class=\"messages\">\n <div class=\"sign-in-msg main\">{{ heading }}</div>\n <div class=\"sign-in-msg secondary\">{{ welcome }}</div>\n </div>\n <div class=\"sign-in-row row\"></div>\n\n <div class=\"social-buttons-container\">\n <button class=\"social-button\" (click)=\"onSocialSignIn(amazon)\" title=\"Sign in with Amazon\">\n <div class=\"social-button-inner\"><div class=\"social-logo amazon\"></div></div>\n </button>\n <button class=\"social-button\" (click)=\"onSocialSignIn(google)\" title=\"Sign in with Google\">\n <div class=\"social-button-inner\"><div class=\"social-logo google\"></div></div>\n </button>\n <button class=\"social-button\" (click)=\"onSocialSignIn(facebook)\" title=\"Sign in with Facebook\">\n <div class=\"social-button-inner\"><div class=\"social-logo facebook\"></div></div>\n </button>\n </div>\n\n <div class=\"method-divider\">\n <div class=\"divider\"></div>\n <span class=\"method-label\">or</span>\n <div class=\"divider\"></div>\n </div>\n\n <div class=\"sign-in-row column\">\n <label for=\"username\" class=\"sign-in-label\">Username</label>\n <input\n class=\"sign-in-input\"\n id=\"username\"\n name=\"username\"\n [(ngModel)]=\"username\"\n spellcheck=\"false\"\n autocomplete=\"username\"\n autocapitalize=\"none\"\n placeholder=\"Enter your username\"\n #usernameInput\n />\n </div>\n\n <div class=\"sign-in-row column\">\n <label for=\"password\" class=\"sign-in-label\">Password</label>\n <input\n class=\"sign-in-input\"\n id=\"password\"\n name=\"password\"\n [(ngModel)]=\"password\"\n type=\"password\"\n autocomplete=\"current-password\"\n spellcheck=\"false\"\n autocapitalize=\"none\"\n placeholder=\"Enter your password\"\n />\n <span class=\"forgot-password\" (click)=\"onSwitchPasswordReset()\">Forgot password?</span>\n </div>\n\n <div class=\"buttons-bottom\">\n <button class=\"btn btn-secondary\" (click)=\"authState.close()\">Cancel</button>\n <button class=\"btn btn-important\" [disabled]=\"signInBusy\" (click)=\"onSignIn()\">Sign In</button>\n </div>\n\n <div class=\"sign-in-row switch\">\n Don't have an account?&nbsp;\n <span class=\"switch-mode\" (click)=\"onSwitchSignUp()\">Sign up</span>\n </div>\n</div>\n", styles: [".forgot-password{font-size:.8rem;color:#6c47ff;cursor:pointer;text-align:right;margin-top:2px}\n", ".auth-container{display:flex;flex-direction:column;gap:16px}@media screen and (max-width:767px){.auth-container{gap:8px}}.sign-in-row{width:100%;display:flex;justify-content:center;font-size:14px}.sign-in-row.column{flex-direction:column;gap:4px}.sign-in-row.row{flex-direction:row;gap:12px}.sign-in-row>.switch-mode{font-weight:500;color:#6c47ff;cursor:pointer}.sign-in-row.switch{padding-bottom:1rem}.sign-in-label{width:100%;font-size:.8rem}.sign-in-input{width:calc(100% - 12px);padding:8px 6px;font-size:16px;border:1px solid #b0b0b0;border-radius:4px}.readonly-input{color:#b0b0b0;background:#efefef}.messages{display:flex;flex-direction:column;gap:4px}.sign-in-msg{text-align:center}.sign-in-msg.main{font-size:18px;font-weight:600}.sign-in-msg.secondary{font-size:14px;font-weight:400}.social-buttons-container{width:100%;padding:12px 0;display:flex;gap:.8rem;justify-content:center}.social-button{padding:11px;border-radius:8px;border:1px solid #e0e0e0;background:#fff;color:#6c6c6c;box-shadow:none;font-weight:500;cursor:pointer}.social-button-inner{display:flex;gap:.75rem}.social-logo{width:1rem;height:1rem;background-position:center;background-size:contain;background-repeat:no-repeat}.social-logo.amazon{background-image:url(https://static.foodmarketmaker.com/social/amazon.png)}.social-logo.google{background-image:url(https://static.foodmarketmaker.com/social/google.png)}.social-logo.facebook{background-image:url(https://static.foodmarketmaker.com/social/facebook.png)}.method-divider{width:100%;padding:8px 0;display:flex;flex-direction:row;align-items:center;justify-content:space-between;gap:.5rem;color:#6c6c6c;font-size:.8rem}.divider{width:45%;height:1px;border-top:1px solid #e0e0e0}.buttons-bottom{width:100%;padding:12px 0;display:flex;gap:16px;justify-content:center}.buttons-bottom>.btn-important{background:#f58220}.buttons-bottom>.btn{padding:.5rem 1rem}.buttons-bottom>button:focus{outline:none}.center-in-dialog{width:100%;display:flex;flex-direction:row;justify-content:center}.confirmation-code .ngx-otp-input-box{width:35px;height:38px;font-size:1.35rem}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
451
+ }
452
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: AuthSignInComponent, decorators: [{
453
+ type: Component,
454
+ args: [{ selector: 'mma-auth-signin', standalone: true, imports: [FormsModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"auth-container\">\n <div class=\"messages\">\n <div class=\"sign-in-msg main\">{{ heading }}</div>\n <div class=\"sign-in-msg secondary\">{{ welcome }}</div>\n </div>\n <div class=\"sign-in-row row\"></div>\n\n <div class=\"social-buttons-container\">\n <button class=\"social-button\" (click)=\"onSocialSignIn(amazon)\" title=\"Sign in with Amazon\">\n <div class=\"social-button-inner\"><div class=\"social-logo amazon\"></div></div>\n </button>\n <button class=\"social-button\" (click)=\"onSocialSignIn(google)\" title=\"Sign in with Google\">\n <div class=\"social-button-inner\"><div class=\"social-logo google\"></div></div>\n </button>\n <button class=\"social-button\" (click)=\"onSocialSignIn(facebook)\" title=\"Sign in with Facebook\">\n <div class=\"social-button-inner\"><div class=\"social-logo facebook\"></div></div>\n </button>\n </div>\n\n <div class=\"method-divider\">\n <div class=\"divider\"></div>\n <span class=\"method-label\">or</span>\n <div class=\"divider\"></div>\n </div>\n\n <div class=\"sign-in-row column\">\n <label for=\"username\" class=\"sign-in-label\">Username</label>\n <input\n class=\"sign-in-input\"\n id=\"username\"\n name=\"username\"\n [(ngModel)]=\"username\"\n spellcheck=\"false\"\n autocomplete=\"username\"\n autocapitalize=\"none\"\n placeholder=\"Enter your username\"\n #usernameInput\n />\n </div>\n\n <div class=\"sign-in-row column\">\n <label for=\"password\" class=\"sign-in-label\">Password</label>\n <input\n class=\"sign-in-input\"\n id=\"password\"\n name=\"password\"\n [(ngModel)]=\"password\"\n type=\"password\"\n autocomplete=\"current-password\"\n spellcheck=\"false\"\n autocapitalize=\"none\"\n placeholder=\"Enter your password\"\n />\n <span class=\"forgot-password\" (click)=\"onSwitchPasswordReset()\">Forgot password?</span>\n </div>\n\n <div class=\"buttons-bottom\">\n <button class=\"btn btn-secondary\" (click)=\"authState.close()\">Cancel</button>\n <button class=\"btn btn-important\" [disabled]=\"signInBusy\" (click)=\"onSignIn()\">Sign In</button>\n </div>\n\n <div class=\"sign-in-row switch\">\n Don't have an account?&nbsp;\n <span class=\"switch-mode\" (click)=\"onSwitchSignUp()\">Sign up</span>\n </div>\n</div>\n", styles: [".forgot-password{font-size:.8rem;color:#6c47ff;cursor:pointer;text-align:right;margin-top:2px}\n", ".auth-container{display:flex;flex-direction:column;gap:16px}@media screen and (max-width:767px){.auth-container{gap:8px}}.sign-in-row{width:100%;display:flex;justify-content:center;font-size:14px}.sign-in-row.column{flex-direction:column;gap:4px}.sign-in-row.row{flex-direction:row;gap:12px}.sign-in-row>.switch-mode{font-weight:500;color:#6c47ff;cursor:pointer}.sign-in-row.switch{padding-bottom:1rem}.sign-in-label{width:100%;font-size:.8rem}.sign-in-input{width:calc(100% - 12px);padding:8px 6px;font-size:16px;border:1px solid #b0b0b0;border-radius:4px}.readonly-input{color:#b0b0b0;background:#efefef}.messages{display:flex;flex-direction:column;gap:4px}.sign-in-msg{text-align:center}.sign-in-msg.main{font-size:18px;font-weight:600}.sign-in-msg.secondary{font-size:14px;font-weight:400}.social-buttons-container{width:100%;padding:12px 0;display:flex;gap:.8rem;justify-content:center}.social-button{padding:11px;border-radius:8px;border:1px solid #e0e0e0;background:#fff;color:#6c6c6c;box-shadow:none;font-weight:500;cursor:pointer}.social-button-inner{display:flex;gap:.75rem}.social-logo{width:1rem;height:1rem;background-position:center;background-size:contain;background-repeat:no-repeat}.social-logo.amazon{background-image:url(https://static.foodmarketmaker.com/social/amazon.png)}.social-logo.google{background-image:url(https://static.foodmarketmaker.com/social/google.png)}.social-logo.facebook{background-image:url(https://static.foodmarketmaker.com/social/facebook.png)}.method-divider{width:100%;padding:8px 0;display:flex;flex-direction:row;align-items:center;justify-content:space-between;gap:.5rem;color:#6c6c6c;font-size:.8rem}.divider{width:45%;height:1px;border-top:1px solid #e0e0e0}.buttons-bottom{width:100%;padding:12px 0;display:flex;gap:16px;justify-content:center}.buttons-bottom>.btn-important{background:#f58220}.buttons-bottom>.btn{padding:.5rem 1rem}.buttons-bottom>button:focus{outline:none}.center-in-dialog{width:100%;display:flex;flex-direction:row;justify-content:center}.confirmation-code .ngx-otp-input-box{width:35px;height:38px;font-size:1.35rem}\n"] }]
455
+ }], ctorParameters: () => [], propDecorators: { usernameInput: [{
456
+ type: ViewChild,
457
+ args: ['usernameInput', { static: false }]
458
+ }] } });
459
+
460
+ const filterUsername = (value) => {
461
+ return value.replace(/\s/g, '');
462
+ };
463
+ const filterPassword = (value) => {
464
+ return value.trim();
465
+ };
466
+ const validateOtpCode = (code) => {
467
+ if (!code || code.length !== 6) {
468
+ return false;
469
+ }
470
+ for (let i = 0; i < 6; i++) {
471
+ if (code[i] < '0' || code[i] > '9') {
472
+ return false;
473
+ }
474
+ }
475
+ return true;
476
+ };
477
+ const validateEmail = (email) => {
478
+ const emailRegex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
479
+ return emailRegex.test(email);
480
+ };
481
+ const validatePassword = (password) => {
482
+ return password.length >= 8;
483
+ };
484
+
485
+ class AuthSignUpComponent {
486
+ auth = inject(AuthService);
487
+ authState = inject(AuthStateService);
488
+ usernameInput;
489
+ emailInput;
490
+ passwordInput;
491
+ username = '';
492
+ email = '';
493
+ password = '';
494
+ signUpBusy = false;
495
+ amazon = AMAZON;
496
+ google = GOOGLE;
497
+ facebook = FACEBOOK;
498
+ heading = 'Sign up with MarketMaker';
499
+ welcome = 'Welcome! Sign in with Amazon, Google or Facebook, or fill in your details to get started.';
500
+ haveUsername = computed(() => this.username.trim().length > 5, ...(ngDevMode ? [{ debugName: "haveUsername" }] : /* istanbul ignore next */ []));
501
+ constructor() {
502
+ effect(() => { this.password = this.authState.passwordSignal(); });
503
+ effect(() => { this.username = this.authState.usernameSignal(); });
504
+ effect(() => { this.email = this.authState.emailSignal(); });
505
+ }
506
+ ngAfterViewInit() {
507
+ this.usernameInput?.nativeElement.focus();
508
+ }
509
+ onSignUp() {
510
+ if (!this.validateInput())
511
+ return;
512
+ this.signUpBusy = true;
513
+ this.auth.signUp({ username: this.username, email: this.email, password: this.password }).then((res) => {
514
+ this.signUpBusy = false;
515
+ this.handleSignUpResponse(res);
516
+ });
517
+ }
518
+ handleSignUpResponse(res) {
519
+ if (res.error) {
520
+ this.authState.setError(res.error);
521
+ return;
522
+ }
523
+ if (!res.isSignedUp && res.nextStep === SignUpNextSteps.CONFIRM_SIGN_UP) {
524
+ this.onSwitchConfirmAccount();
525
+ }
526
+ else {
527
+ this.saveCredentials();
528
+ this.authState.close();
529
+ }
530
+ }
531
+ onSocialSignIn(provider) {
532
+ this.auth.socialSignIn(provider);
533
+ this.authState.close();
534
+ }
535
+ onSwitchSignIn() {
536
+ this.saveCredentials();
537
+ this.authState.setMode(AuthModes.SIGN_IN);
538
+ }
539
+ onSwitchConfirmAccount() {
540
+ this.saveCredentials();
541
+ this.authState.setMode(AuthModes.CONFIRM_ACCOUNT);
542
+ }
543
+ saveCredentials() {
544
+ this.authState.setPassword(this.password);
545
+ this.authState.setUsername(this.username);
546
+ this.authState.setEmail(this.email);
547
+ }
548
+ validateInput() {
549
+ if (this.username.length < 6) {
550
+ this.usernameInput?.nativeElement.focus();
551
+ this.authState.setAutoCloseError('Username must be at least six characters in length');
552
+ return false;
553
+ }
554
+ if (/\s/.test(this.username)) {
555
+ this.usernameInput?.nativeElement.focus();
556
+ this.authState.setAutoCloseError('Username may not include whitespace characters');
557
+ return false;
558
+ }
559
+ if (!validateEmail(this.email)) {
560
+ this.emailInput?.nativeElement.focus();
561
+ this.authState.setAutoCloseError('Please enter a valid email address');
562
+ return false;
563
+ }
564
+ if (!validatePassword(this.password)) {
565
+ this.passwordInput?.nativeElement.focus();
566
+ this.authState.setAutoCloseError('Password must be at least 8 characters in length');
567
+ return false;
568
+ }
569
+ return true;
570
+ }
571
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: AuthSignUpComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
572
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.15", type: AuthSignUpComponent, isStandalone: true, selector: "mma-auth-signup", viewQueries: [{ propertyName: "usernameInput", first: true, predicate: ["usernameInput"], descendants: true }, { propertyName: "emailInput", first: true, predicate: ["emailInput"], descendants: true }, { propertyName: "passwordInput", first: true, predicate: ["passwordInput"], descendants: true }], ngImport: i0, template: "<div class=\"auth-container\">\n <div class=\"messages\">\n <div class=\"sign-in-msg main\">{{ heading }}</div>\n <div class=\"sign-in-msg secondary\">{{ welcome }}</div>\n </div>\n <div class=\"sign-in-row row\"></div>\n\n <div class=\"social-buttons-container\">\n <button class=\"social-button\" (click)=\"onSocialSignIn(amazon)\" title=\"Sign in with Amazon\">\n <div class=\"social-button-inner\"><div class=\"social-logo amazon\"></div></div>\n </button>\n <button class=\"social-button\" (click)=\"onSocialSignIn(google)\" title=\"Sign in with Google\">\n <div class=\"social-button-inner\"><div class=\"social-logo google\"></div></div>\n </button>\n <button class=\"social-button\" (click)=\"onSocialSignIn(facebook)\" title=\"Sign in with Facebook\">\n <div class=\"social-button-inner\"><div class=\"social-logo facebook\"></div></div>\n </button>\n </div>\n\n <div class=\"method-divider\">\n <div class=\"divider\"></div>\n <span class=\"method-label\">or</span>\n <div class=\"divider\"></div>\n </div>\n\n <div class=\"sign-in-row column\">\n <label for=\"username\" class=\"sign-in-label\">Username</label>\n <input\n class=\"sign-in-input\"\n id=\"username\"\n name=\"username\"\n [(ngModel)]=\"username\"\n spellcheck=\"false\"\n autocomplete=\"username\"\n autocorrect=\"off\"\n autocapitalize=\"none\"\n placeholder=\"Enter your username\"\n #usernameInput\n />\n </div>\n\n <div class=\"sign-in-row column\">\n <label for=\"email\" class=\"sign-in-label\">Email address</label>\n <input\n class=\"sign-in-input\"\n id=\"email\"\n name=\"email\"\n [(ngModel)]=\"email\"\n type=\"email\"\n autocomplete=\"none\"\n spellcheck=\"false\"\n autocapitalize=\"none\"\n placeholder=\"Enter your email address\"\n #emailInput\n />\n </div>\n\n <div class=\"sign-in-row column\">\n <label for=\"password\" class=\"sign-in-label\">Password</label>\n <input\n class=\"sign-in-input\"\n id=\"password\"\n name=\"password\"\n [(ngModel)]=\"password\"\n type=\"password\"\n spellcheck=\"false\"\n autocomplete=\"new-password\"\n autocapitalize=\"none\"\n placeholder=\"Enter your password\"\n #passwordInput\n />\n </div>\n\n <div class=\"buttons-bottom\">\n <button class=\"btn btn-secondary\" (click)=\"authState.close()\">Cancel</button>\n <button class=\"btn btn-important\" [disabled]=\"signUpBusy\" (click)=\"onSignUp()\">Register</button>\n </div>\n\n <div class=\"sign-in-row switch\">\n Already have an account?&nbsp;\n <span class=\"switch-mode\" (click)=\"onSwitchSignIn()\">Sign in</span>\n </div>\n</div>\n", styles: ["", ".auth-container{display:flex;flex-direction:column;gap:16px}@media screen and (max-width:767px){.auth-container{gap:8px}}.sign-in-row{width:100%;display:flex;justify-content:center;font-size:14px}.sign-in-row.column{flex-direction:column;gap:4px}.sign-in-row.row{flex-direction:row;gap:12px}.sign-in-row>.switch-mode{font-weight:500;color:#6c47ff;cursor:pointer}.sign-in-row.switch{padding-bottom:1rem}.sign-in-label{width:100%;font-size:.8rem}.sign-in-input{width:calc(100% - 12px);padding:8px 6px;font-size:16px;border:1px solid #b0b0b0;border-radius:4px}.readonly-input{color:#b0b0b0;background:#efefef}.messages{display:flex;flex-direction:column;gap:4px}.sign-in-msg{text-align:center}.sign-in-msg.main{font-size:18px;font-weight:600}.sign-in-msg.secondary{font-size:14px;font-weight:400}.social-buttons-container{width:100%;padding:12px 0;display:flex;gap:.8rem;justify-content:center}.social-button{padding:11px;border-radius:8px;border:1px solid #e0e0e0;background:#fff;color:#6c6c6c;box-shadow:none;font-weight:500;cursor:pointer}.social-button-inner{display:flex;gap:.75rem}.social-logo{width:1rem;height:1rem;background-position:center;background-size:contain;background-repeat:no-repeat}.social-logo.amazon{background-image:url(https://static.foodmarketmaker.com/social/amazon.png)}.social-logo.google{background-image:url(https://static.foodmarketmaker.com/social/google.png)}.social-logo.facebook{background-image:url(https://static.foodmarketmaker.com/social/facebook.png)}.method-divider{width:100%;padding:8px 0;display:flex;flex-direction:row;align-items:center;justify-content:space-between;gap:.5rem;color:#6c6c6c;font-size:.8rem}.divider{width:45%;height:1px;border-top:1px solid #e0e0e0}.buttons-bottom{width:100%;padding:12px 0;display:flex;gap:16px;justify-content:center}.buttons-bottom>.btn-important{background:#f58220}.buttons-bottom>.btn{padding:.5rem 1rem}.buttons-bottom>button:focus{outline:none}.center-in-dialog{width:100%;display:flex;flex-direction:row;justify-content:center}.confirmation-code .ngx-otp-input-box{width:35px;height:38px;font-size:1.35rem}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
573
+ }
574
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: AuthSignUpComponent, decorators: [{
575
+ type: Component,
576
+ args: [{ selector: 'mma-auth-signup', standalone: true, imports: [FormsModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"auth-container\">\n <div class=\"messages\">\n <div class=\"sign-in-msg main\">{{ heading }}</div>\n <div class=\"sign-in-msg secondary\">{{ welcome }}</div>\n </div>\n <div class=\"sign-in-row row\"></div>\n\n <div class=\"social-buttons-container\">\n <button class=\"social-button\" (click)=\"onSocialSignIn(amazon)\" title=\"Sign in with Amazon\">\n <div class=\"social-button-inner\"><div class=\"social-logo amazon\"></div></div>\n </button>\n <button class=\"social-button\" (click)=\"onSocialSignIn(google)\" title=\"Sign in with Google\">\n <div class=\"social-button-inner\"><div class=\"social-logo google\"></div></div>\n </button>\n <button class=\"social-button\" (click)=\"onSocialSignIn(facebook)\" title=\"Sign in with Facebook\">\n <div class=\"social-button-inner\"><div class=\"social-logo facebook\"></div></div>\n </button>\n </div>\n\n <div class=\"method-divider\">\n <div class=\"divider\"></div>\n <span class=\"method-label\">or</span>\n <div class=\"divider\"></div>\n </div>\n\n <div class=\"sign-in-row column\">\n <label for=\"username\" class=\"sign-in-label\">Username</label>\n <input\n class=\"sign-in-input\"\n id=\"username\"\n name=\"username\"\n [(ngModel)]=\"username\"\n spellcheck=\"false\"\n autocomplete=\"username\"\n autocorrect=\"off\"\n autocapitalize=\"none\"\n placeholder=\"Enter your username\"\n #usernameInput\n />\n </div>\n\n <div class=\"sign-in-row column\">\n <label for=\"email\" class=\"sign-in-label\">Email address</label>\n <input\n class=\"sign-in-input\"\n id=\"email\"\n name=\"email\"\n [(ngModel)]=\"email\"\n type=\"email\"\n autocomplete=\"none\"\n spellcheck=\"false\"\n autocapitalize=\"none\"\n placeholder=\"Enter your email address\"\n #emailInput\n />\n </div>\n\n <div class=\"sign-in-row column\">\n <label for=\"password\" class=\"sign-in-label\">Password</label>\n <input\n class=\"sign-in-input\"\n id=\"password\"\n name=\"password\"\n [(ngModel)]=\"password\"\n type=\"password\"\n spellcheck=\"false\"\n autocomplete=\"new-password\"\n autocapitalize=\"none\"\n placeholder=\"Enter your password\"\n #passwordInput\n />\n </div>\n\n <div class=\"buttons-bottom\">\n <button class=\"btn btn-secondary\" (click)=\"authState.close()\">Cancel</button>\n <button class=\"btn btn-important\" [disabled]=\"signUpBusy\" (click)=\"onSignUp()\">Register</button>\n </div>\n\n <div class=\"sign-in-row switch\">\n Already have an account?&nbsp;\n <span class=\"switch-mode\" (click)=\"onSwitchSignIn()\">Sign in</span>\n </div>\n</div>\n", styles: [".auth-container{display:flex;flex-direction:column;gap:16px}@media screen and (max-width:767px){.auth-container{gap:8px}}.sign-in-row{width:100%;display:flex;justify-content:center;font-size:14px}.sign-in-row.column{flex-direction:column;gap:4px}.sign-in-row.row{flex-direction:row;gap:12px}.sign-in-row>.switch-mode{font-weight:500;color:#6c47ff;cursor:pointer}.sign-in-row.switch{padding-bottom:1rem}.sign-in-label{width:100%;font-size:.8rem}.sign-in-input{width:calc(100% - 12px);padding:8px 6px;font-size:16px;border:1px solid #b0b0b0;border-radius:4px}.readonly-input{color:#b0b0b0;background:#efefef}.messages{display:flex;flex-direction:column;gap:4px}.sign-in-msg{text-align:center}.sign-in-msg.main{font-size:18px;font-weight:600}.sign-in-msg.secondary{font-size:14px;font-weight:400}.social-buttons-container{width:100%;padding:12px 0;display:flex;gap:.8rem;justify-content:center}.social-button{padding:11px;border-radius:8px;border:1px solid #e0e0e0;background:#fff;color:#6c6c6c;box-shadow:none;font-weight:500;cursor:pointer}.social-button-inner{display:flex;gap:.75rem}.social-logo{width:1rem;height:1rem;background-position:center;background-size:contain;background-repeat:no-repeat}.social-logo.amazon{background-image:url(https://static.foodmarketmaker.com/social/amazon.png)}.social-logo.google{background-image:url(https://static.foodmarketmaker.com/social/google.png)}.social-logo.facebook{background-image:url(https://static.foodmarketmaker.com/social/facebook.png)}.method-divider{width:100%;padding:8px 0;display:flex;flex-direction:row;align-items:center;justify-content:space-between;gap:.5rem;color:#6c6c6c;font-size:.8rem}.divider{width:45%;height:1px;border-top:1px solid #e0e0e0}.buttons-bottom{width:100%;padding:12px 0;display:flex;gap:16px;justify-content:center}.buttons-bottom>.btn-important{background:#f58220}.buttons-bottom>.btn{padding:.5rem 1rem}.buttons-bottom>button:focus{outline:none}.center-in-dialog{width:100%;display:flex;flex-direction:row;justify-content:center}.confirmation-code .ngx-otp-input-box{width:35px;height:38px;font-size:1.35rem}\n"] }]
577
+ }], ctorParameters: () => [], propDecorators: { usernameInput: [{
578
+ type: ViewChild,
579
+ args: ['usernameInput', { static: false }]
580
+ }], emailInput: [{
581
+ type: ViewChild,
582
+ args: ['emailInput', { static: false }]
583
+ }], passwordInput: [{
584
+ type: ViewChild,
585
+ args: ['passwordInput', { static: false }]
586
+ }] } });
587
+
588
+ class AuthForgotComponent {
589
+ auth = inject(AuthService);
590
+ authState = inject(AuthStateService);
591
+ usernameInput;
592
+ username = '';
593
+ forgotBusy = false;
594
+ heading = 'Forgot your password?';
595
+ welcome = 'Enter your username and we will send a message to reset your password.';
596
+ haveUsername = computed(() => this.username.trim().length > 6, ...(ngDevMode ? [{ debugName: "haveUsername" }] : /* istanbul ignore next */ []));
597
+ constructor() {
598
+ effect(() => { this.username = this.authState.usernameSignal(); });
599
+ }
600
+ ngAfterViewInit() {
601
+ this.usernameInput?.nativeElement.focus();
602
+ }
603
+ notifyClose() {
604
+ this.authState.setUsername(this.username);
605
+ this.authState.close();
606
+ }
607
+ onPasswordReset() {
608
+ this.forgotBusy = true;
609
+ this.auth.resetPassword(this.username).then((res) => {
610
+ this.forgotBusy = false;
611
+ if (res.isPasswordReset) {
612
+ this.notifyClose();
613
+ }
614
+ else if (res.nextStep === ResetPasswordNextSteps.CONFIRM_RESET_PASSWORD_WITH_CODE) {
615
+ this.authState.setUsername(this.username);
616
+ this.authState.setMode(AuthModes.CONFIRM_PASSWORD_RESET);
617
+ }
618
+ else if (res.error) {
619
+ this.authState.setError(res.error);
620
+ }
621
+ });
622
+ }
623
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: AuthForgotComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
624
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.15", type: AuthForgotComponent, isStandalone: true, selector: "mma-auth-forgot", viewQueries: [{ propertyName: "usernameInput", first: true, predicate: ["usernameInput"], descendants: true }], ngImport: i0, template: "<div class=\"auth-container\">\n <div class=\"messages\">\n <div class=\"sign-in-msg main\">{{ heading }}</div>\n <div class=\"sign-in-msg secondary\">{{ welcome }}</div>\n </div>\n <div class=\"sign-in-row row\"></div>\n\n <div class=\"sign-in-row column\">\n <label for=\"username\" class=\"sign-in-label\">Username</label>\n <input\n class=\"sign-in-input\"\n id=\"username\"\n name=\"username\"\n [(ngModel)]=\"username\"\n spellcheck=\"false\"\n autocomplete=\"username\"\n autocorrect=\"off\"\n autocapitalize=\"none\"\n placeholder=\"Enter your username\"\n #usernameInput\n />\n </div>\n\n <div class=\"buttons-bottom forgot-password-buttons-bottom\">\n <button class=\"btn btn-secondary\" (click)=\"notifyClose()\">Cancel</button>\n <button class=\"btn btn-important\" [disabled]=\"forgotBusy\" (click)=\"onPasswordReset()\">Reset Password</button>\n </div>\n</div>\n", styles: ["", ".auth-container{display:flex;flex-direction:column;gap:16px}@media screen and (max-width:767px){.auth-container{gap:8px}}.sign-in-row{width:100%;display:flex;justify-content:center;font-size:14px}.sign-in-row.column{flex-direction:column;gap:4px}.sign-in-row.row{flex-direction:row;gap:12px}.sign-in-row>.switch-mode{font-weight:500;color:#6c47ff;cursor:pointer}.sign-in-row.switch{padding-bottom:1rem}.sign-in-label{width:100%;font-size:.8rem}.sign-in-input{width:calc(100% - 12px);padding:8px 6px;font-size:16px;border:1px solid #b0b0b0;border-radius:4px}.readonly-input{color:#b0b0b0;background:#efefef}.messages{display:flex;flex-direction:column;gap:4px}.sign-in-msg{text-align:center}.sign-in-msg.main{font-size:18px;font-weight:600}.sign-in-msg.secondary{font-size:14px;font-weight:400}.social-buttons-container{width:100%;padding:12px 0;display:flex;gap:.8rem;justify-content:center}.social-button{padding:11px;border-radius:8px;border:1px solid #e0e0e0;background:#fff;color:#6c6c6c;box-shadow:none;font-weight:500;cursor:pointer}.social-button-inner{display:flex;gap:.75rem}.social-logo{width:1rem;height:1rem;background-position:center;background-size:contain;background-repeat:no-repeat}.social-logo.amazon{background-image:url(https://static.foodmarketmaker.com/social/amazon.png)}.social-logo.google{background-image:url(https://static.foodmarketmaker.com/social/google.png)}.social-logo.facebook{background-image:url(https://static.foodmarketmaker.com/social/facebook.png)}.method-divider{width:100%;padding:8px 0;display:flex;flex-direction:row;align-items:center;justify-content:space-between;gap:.5rem;color:#6c6c6c;font-size:.8rem}.divider{width:45%;height:1px;border-top:1px solid #e0e0e0}.buttons-bottom{width:100%;padding:12px 0;display:flex;gap:16px;justify-content:center}.buttons-bottom>.btn-important{background:#f58220}.buttons-bottom>.btn{padding:.5rem 1rem}.buttons-bottom>button:focus{outline:none}.center-in-dialog{width:100%;display:flex;flex-direction:row;justify-content:center}.confirmation-code .ngx-otp-input-box{width:35px;height:38px;font-size:1.35rem}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
625
+ }
626
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: AuthForgotComponent, decorators: [{
627
+ type: Component,
628
+ args: [{ selector: 'mma-auth-forgot', standalone: true, imports: [FormsModule], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"auth-container\">\n <div class=\"messages\">\n <div class=\"sign-in-msg main\">{{ heading }}</div>\n <div class=\"sign-in-msg secondary\">{{ welcome }}</div>\n </div>\n <div class=\"sign-in-row row\"></div>\n\n <div class=\"sign-in-row column\">\n <label for=\"username\" class=\"sign-in-label\">Username</label>\n <input\n class=\"sign-in-input\"\n id=\"username\"\n name=\"username\"\n [(ngModel)]=\"username\"\n spellcheck=\"false\"\n autocomplete=\"username\"\n autocorrect=\"off\"\n autocapitalize=\"none\"\n placeholder=\"Enter your username\"\n #usernameInput\n />\n </div>\n\n <div class=\"buttons-bottom forgot-password-buttons-bottom\">\n <button class=\"btn btn-secondary\" (click)=\"notifyClose()\">Cancel</button>\n <button class=\"btn btn-important\" [disabled]=\"forgotBusy\" (click)=\"onPasswordReset()\">Reset Password</button>\n </div>\n</div>\n", styles: [".auth-container{display:flex;flex-direction:column;gap:16px}@media screen and (max-width:767px){.auth-container{gap:8px}}.sign-in-row{width:100%;display:flex;justify-content:center;font-size:14px}.sign-in-row.column{flex-direction:column;gap:4px}.sign-in-row.row{flex-direction:row;gap:12px}.sign-in-row>.switch-mode{font-weight:500;color:#6c47ff;cursor:pointer}.sign-in-row.switch{padding-bottom:1rem}.sign-in-label{width:100%;font-size:.8rem}.sign-in-input{width:calc(100% - 12px);padding:8px 6px;font-size:16px;border:1px solid #b0b0b0;border-radius:4px}.readonly-input{color:#b0b0b0;background:#efefef}.messages{display:flex;flex-direction:column;gap:4px}.sign-in-msg{text-align:center}.sign-in-msg.main{font-size:18px;font-weight:600}.sign-in-msg.secondary{font-size:14px;font-weight:400}.social-buttons-container{width:100%;padding:12px 0;display:flex;gap:.8rem;justify-content:center}.social-button{padding:11px;border-radius:8px;border:1px solid #e0e0e0;background:#fff;color:#6c6c6c;box-shadow:none;font-weight:500;cursor:pointer}.social-button-inner{display:flex;gap:.75rem}.social-logo{width:1rem;height:1rem;background-position:center;background-size:contain;background-repeat:no-repeat}.social-logo.amazon{background-image:url(https://static.foodmarketmaker.com/social/amazon.png)}.social-logo.google{background-image:url(https://static.foodmarketmaker.com/social/google.png)}.social-logo.facebook{background-image:url(https://static.foodmarketmaker.com/social/facebook.png)}.method-divider{width:100%;padding:8px 0;display:flex;flex-direction:row;align-items:center;justify-content:space-between;gap:.5rem;color:#6c6c6c;font-size:.8rem}.divider{width:45%;height:1px;border-top:1px solid #e0e0e0}.buttons-bottom{width:100%;padding:12px 0;display:flex;gap:16px;justify-content:center}.buttons-bottom>.btn-important{background:#f58220}.buttons-bottom>.btn{padding:.5rem 1rem}.buttons-bottom>button:focus{outline:none}.center-in-dialog{width:100%;display:flex;flex-direction:row;justify-content:center}.confirmation-code .ngx-otp-input-box{width:35px;height:38px;font-size:1.35rem}\n"] }]
629
+ }], ctorParameters: () => [], propDecorators: { usernameInput: [{
630
+ type: ViewChild,
631
+ args: ['usernameInput', { static: false }]
632
+ }] } });
633
+
634
+ class AuthConfirmSignupComponent {
635
+ auth = inject(AuthService);
636
+ authState = inject(AuthStateService);
637
+ confirmSignupOtp;
638
+ username = '';
639
+ email = '';
640
+ code = '';
641
+ confirmBusy = false;
642
+ heading = 'Confirm registration';
643
+ get welcome() {
644
+ return `We have sent a code to ${this.concealEmail()}. Enter the code below to confirm your new account.`;
645
+ }
646
+ constructor() {
647
+ effect(() => { this.email = this.authState.emailSignal(); });
648
+ effect(() => { this.username = this.authState.usernameSignal(); });
649
+ }
650
+ onOtpChange(event) {
651
+ this.code = event.value;
652
+ }
653
+ onOtpComplete(otp) {
654
+ this.code = otp;
655
+ }
656
+ onConfirmAccount() {
657
+ if (!validateOtpCode(this.code)) {
658
+ this.authState.setAutoCloseError('Code must be exactly six numerals');
659
+ return;
660
+ }
661
+ this.confirmBusy = true;
662
+ this.auth.confirmSignUp(this.username, this.code).then((res) => {
663
+ this.confirmBusy = false;
664
+ if (res.error) {
665
+ this.authState.setError(res.error);
666
+ }
667
+ else if (res.isSignedUp) {
668
+ this.authState.setMode(AuthModes.SIGN_IN);
669
+ }
670
+ }).finally(() => { this.confirmBusy = false; });
671
+ }
672
+ onSendNewCode() {
673
+ this.auth.resendSignUpCode(this.username).then((res) => {
674
+ if (res.error) {
675
+ this.authState.setError(res.error);
676
+ }
677
+ });
678
+ }
679
+ notifyClose() {
680
+ this.authState.close();
681
+ }
682
+ concealEmail() {
683
+ if (!this.email || this.email.length < 5)
684
+ return 'your email address';
685
+ const [local, domain] = this.email.split('@');
686
+ const conceal = (s) => s[0] + '*'.repeat(Math.max(s.length - 2, 1)) + s.slice(-1);
687
+ return `${conceal(local)}@${conceal(domain)}`;
688
+ }
689
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: AuthConfirmSignupComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
690
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.15", type: AuthConfirmSignupComponent, isStandalone: true, selector: "mma-auth-confirm-signup", viewQueries: [{ propertyName: "confirmSignupOtp", first: true, predicate: ["confirmSignupOtp"], descendants: true }], ngImport: i0, template: "<div class=\"auth-container\">\n <div class=\"messages\">\n <div class=\"sign-in-msg main\">{{ heading }}</div>\n <div class=\"sign-in-msg secondary\" [innerHTML]=\"welcome\"></div>\n </div>\n <div class=\"sign-in-row row\"></div>\n\n <div class=\"sign-in-row column\">\n <label for=\"username\" class=\"sign-in-label\">Username</label>\n <input class=\"sign-in-input readonly-input\" id=\"username\" name=\"username\" [value]=\"username\"\n spellcheck=\"false\" autocorrect=\"off\" autocapitalize=\"none\" readonly />\n </div>\n\n <div class=\"sign-in-row column confirmation-code\">\n <label class=\"sign-in-label\">Confirmation code</label>\n <div class=\"center-in-dialog\">\n <ngx-otp-input\n [length]=\"6\"\n [autoFocus]=\"true\"\n [autoBlur]=\"true\"\n [mask]=\"false\"\n [charPattern]=\"/^[0-9]+$/\"\n inputMode=\"numeric\"\n (otpChange)=\"onOtpChange($event)\"\n (otpComplete)=\"onOtpComplete($event)\"\n #confirmSignupOtp\n ></ngx-otp-input>\n </div>\n </div>\n\n <div class=\"buttons-bottom\">\n <button class=\"btn btn-secondary\" (click)=\"notifyClose()\">Cancel</button>\n <button class=\"btn btn-important\" [disabled]=\"confirmBusy\" (click)=\"onConfirmAccount()\">Confirm Account</button>\n </div>\n\n <div class=\"sign-in-row switch\">\n Didn't receive a code?&nbsp;\n <span class=\"switch-mode\" (click)=\"onSendNewCode()\">Send a new code</span>\n </div>\n</div>\n", styles: ["", ".auth-container{display:flex;flex-direction:column;gap:16px}@media screen and (max-width:767px){.auth-container{gap:8px}}.sign-in-row{width:100%;display:flex;justify-content:center;font-size:14px}.sign-in-row.column{flex-direction:column;gap:4px}.sign-in-row.row{flex-direction:row;gap:12px}.sign-in-row>.switch-mode{font-weight:500;color:#6c47ff;cursor:pointer}.sign-in-row.switch{padding-bottom:1rem}.sign-in-label{width:100%;font-size:.8rem}.sign-in-input{width:calc(100% - 12px);padding:8px 6px;font-size:16px;border:1px solid #b0b0b0;border-radius:4px}.readonly-input{color:#b0b0b0;background:#efefef}.messages{display:flex;flex-direction:column;gap:4px}.sign-in-msg{text-align:center}.sign-in-msg.main{font-size:18px;font-weight:600}.sign-in-msg.secondary{font-size:14px;font-weight:400}.social-buttons-container{width:100%;padding:12px 0;display:flex;gap:.8rem;justify-content:center}.social-button{padding:11px;border-radius:8px;border:1px solid #e0e0e0;background:#fff;color:#6c6c6c;box-shadow:none;font-weight:500;cursor:pointer}.social-button-inner{display:flex;gap:.75rem}.social-logo{width:1rem;height:1rem;background-position:center;background-size:contain;background-repeat:no-repeat}.social-logo.amazon{background-image:url(https://static.foodmarketmaker.com/social/amazon.png)}.social-logo.google{background-image:url(https://static.foodmarketmaker.com/social/google.png)}.social-logo.facebook{background-image:url(https://static.foodmarketmaker.com/social/facebook.png)}.method-divider{width:100%;padding:8px 0;display:flex;flex-direction:row;align-items:center;justify-content:space-between;gap:.5rem;color:#6c6c6c;font-size:.8rem}.divider{width:45%;height:1px;border-top:1px solid #e0e0e0}.buttons-bottom{width:100%;padding:12px 0;display:flex;gap:16px;justify-content:center}.buttons-bottom>.btn-important{background:#f58220}.buttons-bottom>.btn{padding:.5rem 1rem}.buttons-bottom>button:focus{outline:none}.center-in-dialog{width:100%;display:flex;flex-direction:row;justify-content:center}.confirmation-code .ngx-otp-input-box{width:35px;height:38px;font-size:1.35rem}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "component", type: NgxOtpInputComponent, selector: "ngx-otp-input", inputs: ["length", "autoFocus", "autoBlur", "mask", "charPattern", "inputMode", "ariaLabel", "status", "statusMessages"], outputs: ["otpChange", "otpComplete", "otpInvalid"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
691
+ }
692
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: AuthConfirmSignupComponent, decorators: [{
693
+ type: Component,
694
+ args: [{ selector: 'mma-auth-confirm-signup', standalone: true, imports: [FormsModule, NgxOtpInputComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"auth-container\">\n <div class=\"messages\">\n <div class=\"sign-in-msg main\">{{ heading }}</div>\n <div class=\"sign-in-msg secondary\" [innerHTML]=\"welcome\"></div>\n </div>\n <div class=\"sign-in-row row\"></div>\n\n <div class=\"sign-in-row column\">\n <label for=\"username\" class=\"sign-in-label\">Username</label>\n <input class=\"sign-in-input readonly-input\" id=\"username\" name=\"username\" [value]=\"username\"\n spellcheck=\"false\" autocorrect=\"off\" autocapitalize=\"none\" readonly />\n </div>\n\n <div class=\"sign-in-row column confirmation-code\">\n <label class=\"sign-in-label\">Confirmation code</label>\n <div class=\"center-in-dialog\">\n <ngx-otp-input\n [length]=\"6\"\n [autoFocus]=\"true\"\n [autoBlur]=\"true\"\n [mask]=\"false\"\n [charPattern]=\"/^[0-9]+$/\"\n inputMode=\"numeric\"\n (otpChange)=\"onOtpChange($event)\"\n (otpComplete)=\"onOtpComplete($event)\"\n #confirmSignupOtp\n ></ngx-otp-input>\n </div>\n </div>\n\n <div class=\"buttons-bottom\">\n <button class=\"btn btn-secondary\" (click)=\"notifyClose()\">Cancel</button>\n <button class=\"btn btn-important\" [disabled]=\"confirmBusy\" (click)=\"onConfirmAccount()\">Confirm Account</button>\n </div>\n\n <div class=\"sign-in-row switch\">\n Didn't receive a code?&nbsp;\n <span class=\"switch-mode\" (click)=\"onSendNewCode()\">Send a new code</span>\n </div>\n</div>\n", styles: [".auth-container{display:flex;flex-direction:column;gap:16px}@media screen and (max-width:767px){.auth-container{gap:8px}}.sign-in-row{width:100%;display:flex;justify-content:center;font-size:14px}.sign-in-row.column{flex-direction:column;gap:4px}.sign-in-row.row{flex-direction:row;gap:12px}.sign-in-row>.switch-mode{font-weight:500;color:#6c47ff;cursor:pointer}.sign-in-row.switch{padding-bottom:1rem}.sign-in-label{width:100%;font-size:.8rem}.sign-in-input{width:calc(100% - 12px);padding:8px 6px;font-size:16px;border:1px solid #b0b0b0;border-radius:4px}.readonly-input{color:#b0b0b0;background:#efefef}.messages{display:flex;flex-direction:column;gap:4px}.sign-in-msg{text-align:center}.sign-in-msg.main{font-size:18px;font-weight:600}.sign-in-msg.secondary{font-size:14px;font-weight:400}.social-buttons-container{width:100%;padding:12px 0;display:flex;gap:.8rem;justify-content:center}.social-button{padding:11px;border-radius:8px;border:1px solid #e0e0e0;background:#fff;color:#6c6c6c;box-shadow:none;font-weight:500;cursor:pointer}.social-button-inner{display:flex;gap:.75rem}.social-logo{width:1rem;height:1rem;background-position:center;background-size:contain;background-repeat:no-repeat}.social-logo.amazon{background-image:url(https://static.foodmarketmaker.com/social/amazon.png)}.social-logo.google{background-image:url(https://static.foodmarketmaker.com/social/google.png)}.social-logo.facebook{background-image:url(https://static.foodmarketmaker.com/social/facebook.png)}.method-divider{width:100%;padding:8px 0;display:flex;flex-direction:row;align-items:center;justify-content:space-between;gap:.5rem;color:#6c6c6c;font-size:.8rem}.divider{width:45%;height:1px;border-top:1px solid #e0e0e0}.buttons-bottom{width:100%;padding:12px 0;display:flex;gap:16px;justify-content:center}.buttons-bottom>.btn-important{background:#f58220}.buttons-bottom>.btn{padding:.5rem 1rem}.buttons-bottom>button:focus{outline:none}.center-in-dialog{width:100%;display:flex;flex-direction:row;justify-content:center}.confirmation-code .ngx-otp-input-box{width:35px;height:38px;font-size:1.35rem}\n"] }]
695
+ }], ctorParameters: () => [], propDecorators: { confirmSignupOtp: [{
696
+ type: ViewChild,
697
+ args: ['confirmSignupOtp']
698
+ }] } });
699
+
700
+ class AuthConfirmResetComponent {
701
+ auth = inject(AuthService);
702
+ authState = inject(AuthStateService);
703
+ passwordInput;
704
+ confirmResetOtp;
705
+ username = '';
706
+ password = '';
707
+ email = '';
708
+ code = '';
709
+ heading = 'Set a new password';
710
+ get welcome() {
711
+ return `We have sent a code to ${this.concealEmail()}. Enter the code, username and a new password below.`;
712
+ }
713
+ constructor() {
714
+ effect(() => { this.username = this.authState.usernameSignal(); });
715
+ effect(() => { this.email = this.authState.emailSignal(); });
716
+ }
717
+ onOtpChange(event) {
718
+ this.code = event.value;
719
+ }
720
+ onOtpComplete(otp) {
721
+ this.code = otp;
722
+ }
723
+ onConfirmPasswordReset() {
724
+ if (!validateOtpCode(this.code)) {
725
+ this.authState.setAutoCloseError('Confirmation code must be exactly six numerals');
726
+ return;
727
+ }
728
+ if (!validatePassword(this.password)) {
729
+ this.passwordInput?.nativeElement.focus();
730
+ this.authState.setAutoCloseError('Password must be at least 8 characters');
731
+ return;
732
+ }
733
+ this.auth.confirmResetPassword(this.username, this.password, this.code).then((res) => {
734
+ if (res.isPasswordReset) {
735
+ this.authState.close();
736
+ }
737
+ else if (res.error) {
738
+ this.authState.setError(res.error);
739
+ }
740
+ });
741
+ }
742
+ onSwitchPasswordReset() {
743
+ this.authState.setUsername(this.username);
744
+ this.authState.setMode(AuthModes.FORGOT_PASSWORD);
745
+ }
746
+ notifyClose() {
747
+ this.authState.close();
748
+ }
749
+ concealEmail() {
750
+ if (!this.email || this.email.length < 5)
751
+ return 'your email address';
752
+ const [local, domain] = this.email.split('@');
753
+ const conceal = (s) => s[0] + '*'.repeat(Math.max(s.length - 2, 1)) + s.slice(-1);
754
+ return `${conceal(local)}@${conceal(domain)}`;
755
+ }
756
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: AuthConfirmResetComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
757
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "21.2.15", type: AuthConfirmResetComponent, isStandalone: true, selector: "mma-auth-confirm-reset", viewQueries: [{ propertyName: "passwordInput", first: true, predicate: ["passwordInput"], descendants: true }, { propertyName: "confirmResetOtp", first: true, predicate: ["confirmResetOtp"], descendants: true }], ngImport: i0, template: "<div class=\"auth-container\">\n <div class=\"messages\">\n <div class=\"sign-in-msg main\">{{ heading }}</div>\n <div class=\"sign-in-msg secondary\" [innerHTML]=\"welcome\"></div>\n </div>\n <div class=\"sign-in-row row\"></div>\n\n <div class=\"sign-in-row column confirmation-code\">\n <label class=\"sign-in-label\">Confirmation code</label>\n <div class=\"center-in-dialog\">\n <ngx-otp-input\n [length]=\"6\"\n [autoFocus]=\"true\"\n [autoBlur]=\"true\"\n [mask]=\"false\"\n [charPattern]=\"/^[0-9]+$/\"\n inputMode=\"numeric\"\n (otpChange)=\"onOtpChange($event)\"\n (otpComplete)=\"onOtpComplete($event)\"\n #confirmResetOtp\n ></ngx-otp-input>\n </div>\n </div>\n\n <div class=\"sign-in-row column\">\n <label for=\"username\" class=\"sign-in-label\">Username</label>\n <input\n class=\"sign-in-input\"\n id=\"username\"\n name=\"username\"\n [(ngModel)]=\"username\"\n spellcheck=\"false\"\n autocomplete=\"username\"\n autocorrect=\"off\"\n autocapitalize=\"none\"\n placeholder=\"Enter your username\"\n />\n </div>\n\n <div class=\"sign-in-row column\">\n <label for=\"password\" class=\"sign-in-label\">New password</label>\n <input\n class=\"sign-in-input\"\n id=\"password\"\n name=\"password\"\n [(ngModel)]=\"password\"\n type=\"password\"\n spellcheck=\"false\"\n autocomplete=\"new-password\"\n autocapitalize=\"none\"\n placeholder=\"Enter your new password\"\n #passwordInput\n />\n </div>\n\n <div class=\"buttons-bottom\">\n <button class=\"btn btn-secondary\" (click)=\"notifyClose()\">Cancel</button>\n <button class=\"btn btn-important\" (click)=\"onConfirmPasswordReset()\">Reset Password</button>\n </div>\n\n <div class=\"sign-in-row switch\">\n Didn't receive a reset code?&nbsp;\n <span class=\"switch-mode\" (click)=\"onSwitchPasswordReset()\">Try again</span>\n </div>\n</div>\n", styles: ["", ".auth-container{display:flex;flex-direction:column;gap:16px}@media screen and (max-width:767px){.auth-container{gap:8px}}.sign-in-row{width:100%;display:flex;justify-content:center;font-size:14px}.sign-in-row.column{flex-direction:column;gap:4px}.sign-in-row.row{flex-direction:row;gap:12px}.sign-in-row>.switch-mode{font-weight:500;color:#6c47ff;cursor:pointer}.sign-in-row.switch{padding-bottom:1rem}.sign-in-label{width:100%;font-size:.8rem}.sign-in-input{width:calc(100% - 12px);padding:8px 6px;font-size:16px;border:1px solid #b0b0b0;border-radius:4px}.readonly-input{color:#b0b0b0;background:#efefef}.messages{display:flex;flex-direction:column;gap:4px}.sign-in-msg{text-align:center}.sign-in-msg.main{font-size:18px;font-weight:600}.sign-in-msg.secondary{font-size:14px;font-weight:400}.social-buttons-container{width:100%;padding:12px 0;display:flex;gap:.8rem;justify-content:center}.social-button{padding:11px;border-radius:8px;border:1px solid #e0e0e0;background:#fff;color:#6c6c6c;box-shadow:none;font-weight:500;cursor:pointer}.social-button-inner{display:flex;gap:.75rem}.social-logo{width:1rem;height:1rem;background-position:center;background-size:contain;background-repeat:no-repeat}.social-logo.amazon{background-image:url(https://static.foodmarketmaker.com/social/amazon.png)}.social-logo.google{background-image:url(https://static.foodmarketmaker.com/social/google.png)}.social-logo.facebook{background-image:url(https://static.foodmarketmaker.com/social/facebook.png)}.method-divider{width:100%;padding:8px 0;display:flex;flex-direction:row;align-items:center;justify-content:space-between;gap:.5rem;color:#6c6c6c;font-size:.8rem}.divider{width:45%;height:1px;border-top:1px solid #e0e0e0}.buttons-bottom{width:100%;padding:12px 0;display:flex;gap:16px;justify-content:center}.buttons-bottom>.btn-important{background:#f58220}.buttons-bottom>.btn{padding:.5rem 1rem}.buttons-bottom>button:focus{outline:none}.center-in-dialog{width:100%;display:flex;flex-direction:row;justify-content:center}.confirmation-code .ngx-otp-input-box{width:35px;height:38px;font-size:1.35rem}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox])[formControlName],textarea[formControlName],input:not([type=checkbox])[formControl],textarea[formControl],input:not([type=checkbox])[ngModel],textarea[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: NgxOtpInputComponent, selector: "ngx-otp-input", inputs: ["length", "autoFocus", "autoBlur", "mask", "charPattern", "inputMode", "ariaLabel", "status", "statusMessages"], outputs: ["otpChange", "otpComplete", "otpInvalid"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
758
+ }
759
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: AuthConfirmResetComponent, decorators: [{
760
+ type: Component,
761
+ args: [{ selector: 'mma-auth-confirm-reset', standalone: true, imports: [FormsModule, NgxOtpInputComponent], changeDetection: ChangeDetectionStrategy.OnPush, template: "<div class=\"auth-container\">\n <div class=\"messages\">\n <div class=\"sign-in-msg main\">{{ heading }}</div>\n <div class=\"sign-in-msg secondary\" [innerHTML]=\"welcome\"></div>\n </div>\n <div class=\"sign-in-row row\"></div>\n\n <div class=\"sign-in-row column confirmation-code\">\n <label class=\"sign-in-label\">Confirmation code</label>\n <div class=\"center-in-dialog\">\n <ngx-otp-input\n [length]=\"6\"\n [autoFocus]=\"true\"\n [autoBlur]=\"true\"\n [mask]=\"false\"\n [charPattern]=\"/^[0-9]+$/\"\n inputMode=\"numeric\"\n (otpChange)=\"onOtpChange($event)\"\n (otpComplete)=\"onOtpComplete($event)\"\n #confirmResetOtp\n ></ngx-otp-input>\n </div>\n </div>\n\n <div class=\"sign-in-row column\">\n <label for=\"username\" class=\"sign-in-label\">Username</label>\n <input\n class=\"sign-in-input\"\n id=\"username\"\n name=\"username\"\n [(ngModel)]=\"username\"\n spellcheck=\"false\"\n autocomplete=\"username\"\n autocorrect=\"off\"\n autocapitalize=\"none\"\n placeholder=\"Enter your username\"\n />\n </div>\n\n <div class=\"sign-in-row column\">\n <label for=\"password\" class=\"sign-in-label\">New password</label>\n <input\n class=\"sign-in-input\"\n id=\"password\"\n name=\"password\"\n [(ngModel)]=\"password\"\n type=\"password\"\n spellcheck=\"false\"\n autocomplete=\"new-password\"\n autocapitalize=\"none\"\n placeholder=\"Enter your new password\"\n #passwordInput\n />\n </div>\n\n <div class=\"buttons-bottom\">\n <button class=\"btn btn-secondary\" (click)=\"notifyClose()\">Cancel</button>\n <button class=\"btn btn-important\" (click)=\"onConfirmPasswordReset()\">Reset Password</button>\n </div>\n\n <div class=\"sign-in-row switch\">\n Didn't receive a reset code?&nbsp;\n <span class=\"switch-mode\" (click)=\"onSwitchPasswordReset()\">Try again</span>\n </div>\n</div>\n", styles: [".auth-container{display:flex;flex-direction:column;gap:16px}@media screen and (max-width:767px){.auth-container{gap:8px}}.sign-in-row{width:100%;display:flex;justify-content:center;font-size:14px}.sign-in-row.column{flex-direction:column;gap:4px}.sign-in-row.row{flex-direction:row;gap:12px}.sign-in-row>.switch-mode{font-weight:500;color:#6c47ff;cursor:pointer}.sign-in-row.switch{padding-bottom:1rem}.sign-in-label{width:100%;font-size:.8rem}.sign-in-input{width:calc(100% - 12px);padding:8px 6px;font-size:16px;border:1px solid #b0b0b0;border-radius:4px}.readonly-input{color:#b0b0b0;background:#efefef}.messages{display:flex;flex-direction:column;gap:4px}.sign-in-msg{text-align:center}.sign-in-msg.main{font-size:18px;font-weight:600}.sign-in-msg.secondary{font-size:14px;font-weight:400}.social-buttons-container{width:100%;padding:12px 0;display:flex;gap:.8rem;justify-content:center}.social-button{padding:11px;border-radius:8px;border:1px solid #e0e0e0;background:#fff;color:#6c6c6c;box-shadow:none;font-weight:500;cursor:pointer}.social-button-inner{display:flex;gap:.75rem}.social-logo{width:1rem;height:1rem;background-position:center;background-size:contain;background-repeat:no-repeat}.social-logo.amazon{background-image:url(https://static.foodmarketmaker.com/social/amazon.png)}.social-logo.google{background-image:url(https://static.foodmarketmaker.com/social/google.png)}.social-logo.facebook{background-image:url(https://static.foodmarketmaker.com/social/facebook.png)}.method-divider{width:100%;padding:8px 0;display:flex;flex-direction:row;align-items:center;justify-content:space-between;gap:.5rem;color:#6c6c6c;font-size:.8rem}.divider{width:45%;height:1px;border-top:1px solid #e0e0e0}.buttons-bottom{width:100%;padding:12px 0;display:flex;gap:16px;justify-content:center}.buttons-bottom>.btn-important{background:#f58220}.buttons-bottom>.btn{padding:.5rem 1rem}.buttons-bottom>button:focus{outline:none}.center-in-dialog{width:100%;display:flex;flex-direction:row;justify-content:center}.confirmation-code .ngx-otp-input-box{width:35px;height:38px;font-size:1.35rem}\n"] }]
762
+ }], ctorParameters: () => [], propDecorators: { passwordInput: [{
763
+ type: ViewChild,
764
+ args: ['passwordInput', { static: false }]
765
+ }], confirmResetOtp: [{
766
+ type: ViewChild,
767
+ args: ['confirmResetOtp']
768
+ }] } });
769
+
770
+ class AuthModalComponent {
771
+ authState = inject(AuthStateService);
772
+ authDialog;
773
+ faTimes = faTimes;
774
+ AuthModes = AuthModes;
775
+ constructor() {
776
+ effect(() => {
777
+ const displaying = this.authState.displayingSignal();
778
+ const dialog = this.authDialog?.nativeElement;
779
+ if (!dialog)
780
+ return;
781
+ if (displaying) {
782
+ dialog.showModal();
783
+ }
784
+ else {
785
+ dialog.close();
786
+ }
787
+ });
788
+ effect(() => {
789
+ if (this.authState.willCloseSignal()) {
790
+ this.authDialog?.nativeElement?.close();
791
+ this.authState.didClose();
792
+ }
793
+ });
794
+ }
795
+ onDialogClose() {
796
+ this.authState.didClose();
797
+ }
798
+ onBackdropClick(event) {
799
+ const target = event.target;
800
+ if (target.tagName === 'DIALOG') {
801
+ this.authState.close();
802
+ }
803
+ }
804
+ dismissError() {
805
+ this.authState.resetError();
806
+ }
807
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: AuthModalComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
808
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.15", type: AuthModalComponent, isStandalone: true, selector: "mma-auth-modal", viewQueries: [{ propertyName: "authDialog", first: true, predicate: ["authDialog"], descendants: true }], ngImport: i0, template: "<dialog class=\"auth-dialog\" #authDialog (close)=\"onDialogClose()\" (click)=\"onBackdropClick($event)\">\n @if (authState.displayingSignal()) {\n <div class=\"auth-dialog-inner\">\n\n <div class=\"auth-brand\">\n <img src=\"https://static.foodmarketmaker.com/brand-logo.png\" alt=\"MarketMaker\" class=\"brand-logo\" />\n </div>\n\n @if (authState.errorSignal()) {\n <div class=\"auth-error-banner\" (click)=\"dismissError()\">\n {{ authState.errorSignal() }}\n <fa-icon [icon]=\"faTimes\" class=\"dismiss-icon\"></fa-icon>\n </div>\n }\n\n @switch (authState.modeSignal()) {\n @case (AuthModes.SIGN_IN) { <mma-auth-signin /> }\n @case (AuthModes.SIGN_UP) { <mma-auth-signup /> }\n @case (AuthModes.FORGOT_PASSWORD) { <mma-auth-forgot /> }\n @case (AuthModes.CONFIRM_ACCOUNT) { <mma-auth-confirm-signup /> }\n @case (AuthModes.CONFIRM_PASSWORD_RESET) { <mma-auth-confirm-reset /> }\n }\n\n </div>\n }\n</dialog>\n", styles: [".auth-dialog{border:none;border-radius:12px;padding:0;max-width:440px;width:100%;box-shadow:0 8px 40px #00000052;background:#fff;overflow:visible}.auth-dialog::backdrop{background:#00000080;-webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px)}.auth-dialog-inner{display:flex;flex-direction:column;padding:28px 32px 24px;gap:0;position:relative}.auth-brand{display:flex;justify-content:center;margin-bottom:16px}.brand-logo{height:40px;width:auto}.auth-error-banner{background:#fdecea;color:#b71c1c;border:1px solid #f5c6cb;border-radius:6px;padding:10px 14px;font-size:.875rem;display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:12px;cursor:pointer}.dismiss-icon{font-size:.9rem;flex-shrink:0;opacity:.7}@media(max-width:480px){.auth-dialog{max-width:100vw;width:100vw;margin:0;position:fixed;bottom:0;top:auto;border-radius:16px 16px 0 0}}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "ngmodule", type: FontAwesomeModule }, { kind: "component", type: i1$1.FaIconComponent, selector: "fa-icon", inputs: ["icon", "title", "animation", "mask", "flip", "size", "pull", "border", "inverse", "symbol", "rotate", "fixedWidth", "transform", "a11yRole"], outputs: ["iconChange", "titleChange", "animationChange", "maskChange", "flipChange", "sizeChange", "pullChange", "borderChange", "inverseChange", "symbolChange", "rotateChange", "fixedWidthChange", "transformChange", "a11yRoleChange"] }, { kind: "component", type: AuthSignInComponent, selector: "mma-auth-signin" }, { kind: "component", type: AuthSignUpComponent, selector: "mma-auth-signup" }, { kind: "component", type: AuthForgotComponent, selector: "mma-auth-forgot" }, { kind: "component", type: AuthConfirmSignupComponent, selector: "mma-auth-confirm-signup" }, { kind: "component", type: AuthConfirmResetComponent, selector: "mma-auth-confirm-reset" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
809
+ }
810
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.15", ngImport: i0, type: AuthModalComponent, decorators: [{
811
+ type: Component,
812
+ args: [{ selector: 'mma-auth-modal', standalone: true, imports: [
813
+ CommonModule,
814
+ FontAwesomeModule,
815
+ AuthSignInComponent,
816
+ AuthSignUpComponent,
817
+ AuthForgotComponent,
818
+ AuthConfirmSignupComponent,
819
+ AuthConfirmResetComponent,
820
+ ], changeDetection: ChangeDetectionStrategy.OnPush, template: "<dialog class=\"auth-dialog\" #authDialog (close)=\"onDialogClose()\" (click)=\"onBackdropClick($event)\">\n @if (authState.displayingSignal()) {\n <div class=\"auth-dialog-inner\">\n\n <div class=\"auth-brand\">\n <img src=\"https://static.foodmarketmaker.com/brand-logo.png\" alt=\"MarketMaker\" class=\"brand-logo\" />\n </div>\n\n @if (authState.errorSignal()) {\n <div class=\"auth-error-banner\" (click)=\"dismissError()\">\n {{ authState.errorSignal() }}\n <fa-icon [icon]=\"faTimes\" class=\"dismiss-icon\"></fa-icon>\n </div>\n }\n\n @switch (authState.modeSignal()) {\n @case (AuthModes.SIGN_IN) { <mma-auth-signin /> }\n @case (AuthModes.SIGN_UP) { <mma-auth-signup /> }\n @case (AuthModes.FORGOT_PASSWORD) { <mma-auth-forgot /> }\n @case (AuthModes.CONFIRM_ACCOUNT) { <mma-auth-confirm-signup /> }\n @case (AuthModes.CONFIRM_PASSWORD_RESET) { <mma-auth-confirm-reset /> }\n }\n\n </div>\n }\n</dialog>\n", styles: [".auth-dialog{border:none;border-radius:12px;padding:0;max-width:440px;width:100%;box-shadow:0 8px 40px #00000052;background:#fff;overflow:visible}.auth-dialog::backdrop{background:#00000080;-webkit-backdrop-filter:blur(3px);backdrop-filter:blur(3px)}.auth-dialog-inner{display:flex;flex-direction:column;padding:28px 32px 24px;gap:0;position:relative}.auth-brand{display:flex;justify-content:center;margin-bottom:16px}.brand-logo{height:40px;width:auto}.auth-error-banner{background:#fdecea;color:#b71c1c;border:1px solid #f5c6cb;border-radius:6px;padding:10px 14px;font-size:.875rem;display:flex;align-items:center;justify-content:space-between;gap:8px;margin-bottom:12px;cursor:pointer}.dismiss-icon{font-size:.9rem;flex-shrink:0;opacity:.7}@media(max-width:480px){.auth-dialog{max-width:100vw;width:100vw;margin:0;position:fixed;bottom:0;top:auto;border-radius:16px 16px 0 0}}\n"] }]
821
+ }], ctorParameters: () => [], propDecorators: { authDialog: [{
822
+ type: ViewChild,
823
+ args: ['authDialog']
824
+ }] } });
825
+
826
+ /*
827
+ * Public API Surface of marketmaker-auth
828
+ */
829
+
830
+ /**
831
+ * Generated bundle index. Do not edit.
832
+ */
833
+
834
+ export { AMAZON, APPLE, AuthModalComponent, AuthModes, AuthService, AuthStateService, FACEBOOK, GOOGLE };
835
+ //# sourceMappingURL=foodmarketmaker-marketmaker-auth.mjs.map