Py-detect / src /app /homepage /sign-up /sign-up.component.html
RajalashmiNagarajan
home-update
6562178
<section class="signup-popup">
<div class="ai-particle-bg"></div>
<div class="signup-header">
<div class="signup-logo">
</div>
</div>
<div class="auth-card">
<div class="card-inner">
<!-- FRONT: sign-up on main panel, image side on left -->
<div class="card-front">
<button class="signin-close" type="button" (click)="closePopup()" aria-label="Close">&times;</button>
<div class="card-content">
<div class="side-panel side-left">
<div class="signup-panel-left">
</div>
<div class="main-panel" [class.pwd-mismatch]="submitted && pwdMismatch">
<h2 class="signup-title center-title">Create An Account</h2>
<!-- aria-live region for screen readers (visually hidden) -->
<div class="sr-only" aria-live="assertive" *ngIf="submitted && invalidFieldsMessage">{{ invalidFieldsMessage }}</div>
<!-- Extend form to include terms, reCAPTCHA, and submit button -->
<form [formGroup]="form" (ngSubmit)="submit()" class="create-form" novalidate autocomplete="off">
<div class="form-row">
<div class="form-field">
<label for="firstName">First Name
<span class="label-cross" *ngIf="submitted && controlHasError('name')" aria-hidden="true"></span>
</label>
<input id="firstName" type="text" placeholder="First Name" formControlName="name" [attr.aria-invalid]="controlHasError('name')" [class.input-invalid]="submitted && (controlHasError('name') || nameHasDigits('name'))" />
<!-- Name errors intentionally hidden until Create Account and per user request no name error texts -->
</div>
<div class="form-field">
<label for="lastName">Last Name
<span class="label-cross" *ngIf="submitted && controlHasError('lastName')" aria-hidden="true"></span>
</label>
<input id="lastName" type="text" placeholder="Last Name" formControlName="lastName" [attr.aria-invalid]="controlHasError('lastName')" [class.input-invalid]="submitted && (controlHasError('lastName') || nameHasDigits('lastName'))" />
<!-- Last name errors intentionally hidden until Create Account and per user request no name error texts -->
</div>
</div>
<div class="form-row">
<div class="form-field email-field" [class.email-invalid]="submitted && controlHasError('email')">
<label for="email">Email
</label>
<input id="email" type="text" placeholder="email@gmail.com" formControlName="email" [attr.aria-invalid]="controlHasError('email')" [class.input-invalid]="submitted && controlHasError('email')" autocomplete="off" autocapitalize="off" spellcheck="false" />
<small *ngIf="submitted && controlHasError('email','required')" class="error">Email is required.</small>
<small *ngIf="submitted && controlHasError('email','pattern')" class="error">Enter a valid email/phone.</small>
<small *ngIf="submitted && controlHasError('email','emailExists')" class="error">Email already exists.</small>
</div>
<div class="form-field role-field-wrapper">
<label for="roleGroup">Role Group
<span class="label-cross" *ngIf="submitted && controlHasError('roleGroup')" aria-hidden="true"></span>
<button type="button" class="info-btn" (click)="showInfo = true" aria-label="Role info">i</button>
</label>
<select id="roleGroup" formControlName="roleGroup" (change)="onRoleGroupChange($any($event.target).value)" aria-label="Role group" [class.input-invalid]="submitted && controlHasError('roleGroup')">
<option value="">-- Select role group --</option>
<option *ngFor="let g of roleGroups" [value]="g.key">{{ g.label }}</option>
</select>
<small *ngIf="submitted && controlHasError('roleGroup','required')" class="error">Please select a role group.</small>
</div>
</div>
<div class="form-row">
<div class="form-field password-field">
<label for="password">Create Password
</label>
<!-- wrap input and eye in a positioned wrapper so the eye is anchored to the input only -->
<div class="input-with-eye">
<input id="password" [type]="showPassword ? 'text' : 'password'" placeholder="••••••••" formControlName="password" (keydown)="onPasswordKey($event)" (keyup)="onPasswordKey($event)" [class.input-invalid]="submitted && controlHasError('password')" autocomplete="new-password" autocapitalize="off" spellcheck="false" />
<!-- local eye toggle button (sign-up only) -->
<button type="button" class="eye-toggle variant-signup" aria-label="Show password" [attr.aria-pressed]="showPassword" (click)="togglePasswordVisibility()">
<i class="fa-solid" [ngClass]="showPassword ? 'fa-eye' : 'fa-eye-slash'" aria-hidden="true"></i>
</button>
</div>
<div *ngIf="capsLockOn" class="caps-lock-warning">Caps Lock is on</div>
<small *ngIf="submitted && controlHasError('password','required')" class="error">Password is required.</small>
<small *ngIf="submitted && form.get('password')?.hasError('passwordPolicy')" class="policy-info">
Required at least 8 characters using letters, numbers, and special character.
</small>
</div>
<div class="form-field password-field">
<label for="confirmPassword">Confirm Password
</label>
<!-- wrap confirm input and eye similarly -->
<div class="input-with-eye" [class.password-mismatch]="submitted && pwdMismatch" [class.confirm-cross]="submitted && pwdMismatch">
<input id="confirmPassword" [type]="showConfirmPassword ? 'text' : 'password'" placeholder="••••••••" formControlName="confirmPassword" (keydown)="onPasswordKey($event)" (keyup)="onPasswordKey($event)" [class.input-invalid]="submitted && (controlHasError('confirmPassword') || showPwdMismatch())" autocomplete="new-password" autocapitalize="off" spellcheck="false" />
<!-- local eye toggle for confirm (sign-up only) -->
<button type="button" class="eye-toggle variant-signup" aria-label="Show confirm password" [attr.aria-pressed]="showConfirmPassword" (click)="toggleConfirmPasswordVisibility()">
<i class="fa-solid" [ngClass]="showConfirmPassword ? 'fa-eye' : 'fa-eye-slash'" aria-hidden="true"></i>
</button>
</div>
<div *ngIf="capsLockOn" class="caps-lock-warning">Caps Lock is on</div>
<small *ngIf="submitted && controlHasError('confirmPassword','required')" class="error">Confirm password is required.</small>
<small *ngIf="submitted && showPwdMismatch()" class="error">Passwords do not match.</small>
</div>
</div>
<!--2FA opt-in and method selection -->
<div class="form-row">
<div class="form-field twofa-field">
<!-- single row: checkbox + methods -->
<div class="twofa-row">
<div class="form-checkbox twofa-checkbox">
<input type="checkbox" id="twoFAOptIn" formControlName="twoFAOptIn" />
<label for="twoFAOptIn">Enable Two-Factor Authentication (2FA)</label>
</div>
<div *ngIf="form.get('twoFAOptIn')?.value" class="twofa-methods">
<label class="inline-control plain-control"><input type="radio" formControlName="twoFAMethod" value="email" /> Email</label>
<label class="inline-control plain-control"><input type="radio" formControlName="twoFAMethod" value="sms" /> SMS</label>
</div>
</div>
<!-- Email-specific display: show account email (plain text) -->
<div *ngIf="form.get('twoFAOptIn')?.value && form.get('twoFAMethod')?.value === 'email'" class="twofa-email-display">
<label class="twofa-email-label">2FA will be sent to</label>
<div class="twofa-email-value">{{ form.get('email')?.value || '—' }}</div>
</div>
<!-- SMS-specific input -->
<div *ngIf="form.get('twoFAOptIn')?.value && form.get('twoFAMethod')?.value === 'sms'" class="twofa-sms-options">
<label for="twoFAPhone">Phone number for SMS2FA</label>
<input id="twoFAPhone" type="tel" placeholder="+1234567890" formControlName="twoFAPhone" (input)="formatPhone($event)" inputmode="tel" autocomplete="tel" [class.input-invalid]="submitted && form.get('twoFAPhone')?.invalid" />
<small *ngIf="submitted && form.get('twoFAPhone')?.invalid" class="error">Enter a valid phone number for SMS 2FA.</small>
</div>
<small *ngIf="twoFAError" class="error twofa-error">{{ twoFAError }}</small>
</div>
</div>
<!-- reCAPTCHA -->
<div id="recaptcha-container" class="recaptcha-container"></div>
<small *ngIf="recaptchaError" class="error">{{ recaptchaError }}</small>
<!-- Submit -->
<button class="create-btn ai-pulse" type="submit" [disabled]="loading || !isFormFilled()">
<ng-container *ngIf="!loading; else creatingAccount">
Create Account
</ng-container>
<ng-template #creatingAccount>
<span class="spinner"></span> Creating Account...
</ng-template>
</button>
</form>
<!-- Terms & Privacy moved outside form group, aligned with2FA -->
<div [formGroup]="form">
<div class="form-checkbox">
<input type="checkbox" id="terms" formControlName="terms" />
<label for="terms">Agree to our <a href="/legal/terms" target="_blank" rel="_noopener noreferrer">Terms &amp; Conditions</a> and <a href="/legal/privacy" target="_blank" rel="_noopener noreferrer">Privacy Policy</a>.</label>
</div>
</div>
<!-- Google Sign-In button -->
<div class="google-signup-row">
<div id="google-signup-btn-div">
<div class="g-signin2" data-width="240" data-height="50" data-longtitle="true"></div>
</div>
</div>
<div class="create-footer"><b>Version 1.0.0 | © Pykara Technologies</b></div>
</div>
</div>
</div>
</div>
</div>
<!-- Detached overlay/modal so layout doesn't shift -->
<div class="role-help-overlay" *ngIf="showRoleInfo" (click)="hideRoleInfo()">
<div class="role-help-modal" role="dialog" aria-label="Role descriptions" (click)="$event.stopPropagation()">
<button type="button" class="role-help-close" aria-label="Close role info" (click)="hideRoleInfo()">×</button>
<h3 class="role-help-title">Role Information</h3>
<ul class="role-help-list">
<li><strong>Law Enforcement</strong><br/>Investigator, Supervisor — for users handling case investigation, interviews, evidence review, and workflow supervision. These roles may require admin approval or identity verification.</li>
<li><strong>Legal</strong><br/>Lawyer — for legal professionals involved in reviewing case summaries, preparing legal notes, analyzing evidence, or assisting in compliance processes.</li>
<li><strong>Administration</strong><br/>Admin — for users who manage system settings, user access, roles, case assignments, and overall application operations.</li>
<li><strong>General Access</strong><br/>Other — for users who require limited or basic access, such as support staff, trainees, or external collaborators.</li>
</ul>
<p class="role-help-tip">If you are not sure which role to choose, select <strong>Other</strong>, or contact: <a href="mailto:support@pykara.ai">support@pykara.ai</a></p>
</div>
</div>
<!-- Floating Info Popup -->
<div *ngIf="showInfo" class="info-popup-bg" (click)="showInfo = false">
<div class="info-popup" (click)="$event.stopPropagation()">
<button class="info-close" type="button" (click)="showInfo = false" aria-label="Close">&times;</button>
<div class="info-title">Role Information</div>
<div class="info-text">
<ul>
<li><strong>Law Enforcement</strong><br/>Investigator, Supervisor — for users handling case investigation, interviews, evidence review, and workflow supervision. These roles may require admin approval or identity verification.</li>
<li><strong>Legal</strong><br/>Lawyer — for legal professionals involved in reviewing case summaries, preparing legal notes, analyzing evidence, or assisting in compliance processes.</li>
<li><strong>Administration</strong><br/>Admin — for users who manage system settings, user access, roles, case assignments, and overall application operations.</li>
<li><strong>General Access</strong><br/>Other — for users who require limited or basic access, such as support staff, trainees, or external collaborators.</li>
</ul>
<p>If you are not sure which role to choose, select <strong>Other</strong>, or contact: <a href="mailto:info@pykara.ai">info@pykara.ai</a></p>
</div>
</div>
</div>