diff --git a/app/Livewire/BillingPortal/Authentication/Login.php b/app/Livewire/BillingPortal/Authentication/Login.php new file mode 100644 index 0000000000..8217da3ca4 --- /dev/null +++ b/app/Livewire/BillingPortal/Authentication/Login.php @@ -0,0 +1,162 @@ + false, // Use as preference. E-mail/password or OTP. + 'login_form' => false, + 'otp_form' => false, + 'initial_completed' => false, + ]; + + public function initial() + { + $this->validateOnly('email', ['email' => 'required|bail|email:rfc|exists:client_contacts,email']); + + $this->state['initial_completed'] = true; + + if ($this->state['otp']) { + return $this->withOtp(); + } + + return $this->withPassword(); + } + + public function withPassword() + { + $contact = ClientContact::where('email', $this->email) + ->where('company_id', $this->subscription->company_id) + ->first(); + + if ($contact) { + return $this->state['login_form'] = true; + } + + $this->state['login_form'] = false; + + $contact = $this->createClientContact(); + + auth()->guard('contact')->loginUsingId($contact->id, true); + + $this->dispatch('purchase.context', property: 'contact', value: $contact); + $this->dispatch('purchase.next'); + } + + public function withOtp() + { + $code = rand(100000, 999999); + $email_hash = "subscriptions:otp:{$this->email}"; + + Cache::put($email_hash, $code, 600); + + $cc = new ClientContact(); + $cc->email = $this->email; + + $nmo = new NinjaMailerObject(); + $nmo->mailable = new OtpCode($this->subscription->company, $this->context['contact'] ?? null, $code); + $nmo->company = $this->subscription->company; + $nmo->settings = $this->subscription->company->settings; + $nmo->to_user = $cc; + + NinjaMailerJob::dispatch($nmo); + + if (app()->environment('local')) { + session()->flash('message', "[dev]: Your OTP is: {$code}"); + } + + $this->state['otp_form'] = true; + } + + public function handleOtp() + { + $this->validate([ + 'otp' => 'required|numeric|digits:6', + 'email' => 'required|bail|email:rfc|exists:client_contacts,email', + ]); + + $code = Cache::get("subscriptions:otp:{$this->email}"); + + if ($this->otp != $code) { //loose comparison prevents edge cases + $errors = $this->getErrorBag(); + $errors->add('otp', ctrans('texts.invalid_code')); + + return; + } + + $contact = ClientContact::where('email', $this->email) + ->where('company_id', $this->subscription->company_id) + ->first(); + + if ($contact) { + auth()->guard('contact')->loginUsingId($contact->id, true); + + $this->dispatch('purchase.context', property: 'contact', value: $contact); + $this->dispatch('purchase.next'); + + return; + } + } + + public function handlePassword() + { + $this->validate([ + 'email' => 'required|bail|email:rfc', + 'password' => 'required', + ]); + + $attempt = auth()->guard('contact')->attempt([ + 'email' => $this->email, + 'password' => $this->password, + 'company_id' => $this->subscription->company_id, + ]); + + if ($attempt) { + $this->dispatch('purchase.next'); + } + + session()->flash('message', 'These credentials do not match our records.'); + } + + public function mount() + { + if (auth()->guard('contact')->check()) { + $this->dispatch('purchase.context', property: 'contact', value: auth()->guard('contact')->user()); + $this->dispatch('purchase.next'); + } + } + + public function render(): \Illuminate\View\View + { + return view('billing-portal.v3.authentication.login'); + } +} \ No newline at end of file diff --git a/resources/views/billing-portal/v3/authentication/login.blade.php b/resources/views/billing-portal/v3/authentication/login.blade.php new file mode 100644 index 0000000000..f1261a1016 --- /dev/null +++ b/resources/views/billing-portal/v3/authentication/login.blade.php @@ -0,0 +1,91 @@ +
+ @if (session()->has('message')) + @component('portal.ninja2020.components.message') + {{ session('message') }} + @endcomponent + @endif + +
+

{{ ctrans('texts.contact') }}

+
+ + @if($state['initial_completed'] === false) +
+ @csrf + + + + +
+ @endif + + @if($state['login_form']) +
+ @csrf + +
+ {{ ctrans('texts.email_address') }} + + + @error('email') + + @enderror +
+ +
+ {{ ctrans('texts.password') }} + + + @error('password') + + @enderror +
+ + +
+ @endif + + @if($state['otp_form']) +
+ @csrf + +
+ {{ ctrans('texts.code') }} + + + @error('otp') + + @enderror +
+ + +
+ @endif +