| @ -0,0 +1,34 @@ | |||
| <?php | |||
| namespace App\Http\Controllers\Collaborators\Auth; | |||
| use App\Http\Controllers\Controller; | |||
| use Illuminate\Auth\Events\Verified; | |||
| use Illuminate\Support\Facades\Auth; | |||
| use Illuminate\Http\RedirectResponse; | |||
| use App\Providers\RouteServiceProvider; | |||
| use Illuminate\Auth\Access\AuthorizationException; | |||
| class EmailVerificationController extends Controller | |||
| { | |||
| public function __invoke(string $id, string $hash): RedirectResponse | |||
| { | |||
| if (!hash_equals((string) $id, (string) Auth::user()->getKey())) { | |||
| throw new AuthorizationException(); | |||
| } | |||
| if (!hash_equals((string) $hash, sha1(Auth::user()->getEmailForVerification()))) { | |||
| throw new AuthorizationException(); | |||
| } | |||
| if (Auth::user()->hasVerifiedEmail()) { | |||
| return redirect(route('home')); | |||
| } | |||
| if (Auth::user()->markEmailAsVerified()) { | |||
| event(new Verified(Auth::user())); | |||
| } | |||
| return redirect(route('home')); | |||
| } | |||
| } | |||
| @ -0,0 +1,18 @@ | |||
| <?php | |||
| namespace App\Http\Controllers\Collaborators\Auth; | |||
| use App\Http\Controllers\Controller; | |||
| use App\Providers\RouteServiceProvider; | |||
| use Illuminate\Support\Facades\Auth; | |||
| use Illuminate\Http\RedirectResponse; | |||
| class LogoutController extends Controller | |||
| { | |||
| public function __invoke(): RedirectResponse | |||
| { | |||
| Auth::logout(); | |||
| return redirect(route('home')); | |||
| } | |||
| } | |||
| @ -0,0 +1,15 @@ | |||
| <?php | |||
| namespace App\Http\Controllers\Collaborators\Auth; | |||
| use App\Http\Controllers\Controller; | |||
| class PasswordResetController extends Controller | |||
| { | |||
| public function __invoke($token) | |||
| { | |||
| return view('collaborators.auth.passwords.reset', [ | |||
| 'token' => $token, | |||
| ]); | |||
| } | |||
| } | |||
| @ -0,0 +1,40 @@ | |||
| <?php | |||
| namespace App\Http\Livewire\Collaborators\Auth; | |||
| use App\Providers\RouteServiceProvider; | |||
| use Illuminate\Support\Facades\Auth; | |||
| use Livewire\Component; | |||
| class Login extends Component | |||
| { | |||
| /** @var string */ | |||
| public $email = ''; | |||
| /** @var string */ | |||
| public $password = ''; | |||
| /** @var bool */ | |||
| public $remember = false; | |||
| public function authenticate() | |||
| { | |||
| $credentials = $this->validate([ | |||
| 'email' => ['required', 'email'], | |||
| 'password' => ['required'], | |||
| ]); | |||
| if (!Auth::attempt($credentials, $this->remember)) { | |||
| $this->addError('email', trans('auth.failed')); | |||
| return; | |||
| } | |||
| redirect(route('home')); | |||
| } | |||
| public function render() | |||
| { | |||
| return view('livewire.collaborators.auth.login'); | |||
| } | |||
| } | |||
| @ -0,0 +1,47 @@ | |||
| <?php | |||
| namespace App\Http\Livewire\Collaborators\Auth\Passwords; | |||
| use Livewire\Component; | |||
| use Illuminate\Support\Facades\Password; | |||
| class Email extends Component | |||
| { | |||
| /** @var string */ | |||
| public $email; | |||
| /** @var string|null */ | |||
| public $emailSentMessage = false; | |||
| public function sendResetPasswordLink() | |||
| { | |||
| $this->validate([ | |||
| 'email' => ['required', 'email'], | |||
| ]); | |||
| $response = $this->broker()->sendResetLink(['email' => $this->email]); | |||
| if ($response == Password::RESET_LINK_SENT) { | |||
| $this->emailSentMessage = trans($response); | |||
| return; | |||
| } | |||
| $this->addError('email', trans($response)); | |||
| } | |||
| /** | |||
| * Get the broker to be used during password reset. | |||
| * | |||
| * @return \Illuminate\Contracts\Auth\PasswordBroker | |||
| */ | |||
| public function broker() | |||
| { | |||
| return Password::broker(); | |||
| } | |||
| public function render() | |||
| { | |||
| return view('livewire.collaborators.auth.passwords.email'); | |||
| } | |||
| } | |||
| @ -0,0 +1,92 @@ | |||
| <?php | |||
| namespace App\Http\Livewire\Collaborators\Auth\Passwords; | |||
| use App\Providers\RouteServiceProvider; | |||
| use Livewire\Component; | |||
| use Illuminate\Support\Str; | |||
| use Illuminate\Support\Facades\Auth; | |||
| use Illuminate\Support\Facades\Hash; | |||
| use Illuminate\Support\Facades\Password; | |||
| use Illuminate\Auth\Events\PasswordReset; | |||
| class Reset extends Component | |||
| { | |||
| /** @var string */ | |||
| public $token; | |||
| /** @var string */ | |||
| public $email; | |||
| /** @var string */ | |||
| public $password; | |||
| /** @var string */ | |||
| public $passwordConfirmation; | |||
| public function mount($token) | |||
| { | |||
| $this->token = $token; | |||
| } | |||
| public function resetPassword() | |||
| { | |||
| $this->validate([ | |||
| 'token' => 'required', | |||
| 'email' => 'required|email', | |||
| 'password' => 'required|min:8|same:passwordConfirmation', | |||
| ]); | |||
| $response = $this->broker()->reset( | |||
| [ | |||
| 'token' => $this->token, | |||
| 'email' => $this->email, | |||
| 'password' => $this->password | |||
| ], | |||
| function ($user, $password) { | |||
| $user->password = Hash::make($password); | |||
| $user->setRememberToken(Str::random(60)); | |||
| $user->save(); | |||
| event(new PasswordReset($user)); | |||
| $this->guard()->login($user); | |||
| } | |||
| ); | |||
| if ($response == Password::PASSWORD_RESET) { | |||
| session()->flash(trans($response)); | |||
| return redirect(route('home')); | |||
| } | |||
| $this->addError('email', trans($response)); | |||
| } | |||
| /** | |||
| * Get the broker to be used during password reset. | |||
| * | |||
| * @return \Illuminate\Contracts\Auth\PasswordBroker | |||
| */ | |||
| public function broker() | |||
| { | |||
| return Password::broker(); | |||
| } | |||
| /** | |||
| * Get the guard to be used during password reset. | |||
| * | |||
| * @return \Illuminate\Contracts\Auth\StatefulGuard | |||
| */ | |||
| protected function guard() | |||
| { | |||
| return Auth::guard(); | |||
| } | |||
| public function render() | |||
| { | |||
| return view('livewire.collaborators.auth.passwords.reset'); | |||
| } | |||
| } | |||
| @ -0,0 +1,181 @@ | |||
| <?php | |||
| namespace App\Http\Livewire\Collaborators\Auth\Register; | |||
| use App\Providers\RouteServiceProvider; | |||
| use App\Associate; | |||
| use App\AssociateCategory; | |||
| use App\AssociateNature; | |||
| use App\AssociateType; | |||
| use Carbon\Carbon; | |||
| use Illuminate\Support\Facades\Auth; | |||
| use Illuminate\Support\Facades\Http; | |||
| use Illuminate\Support\Str; | |||
| use Livewire\Component; | |||
| class Individual extends Component | |||
| { | |||
| /** @var string */ | |||
| public $name = ''; | |||
| /** @var string */ | |||
| public $birthday = ''; | |||
| /** @var array */ | |||
| public $document = [ | |||
| 'type' => 'cpf', | |||
| 'number' => '', | |||
| ]; | |||
| /** @var array */ | |||
| public $address = [ | |||
| 'city' => '', | |||
| 'complement' => '', | |||
| 'country' => 'BR', | |||
| 'neighbourhood' => '', | |||
| 'number' => '', | |||
| 'postcode' => '', | |||
| 'state' => '', | |||
| 'street' => '', | |||
| ]; | |||
| /** @var array */ | |||
| public $profile = [ | |||
| 'gender' => '', | |||
| 'occupation' => '', | |||
| 'scholarity' => '', | |||
| 'phone' => '', | |||
| 'secondary_emails' => '', | |||
| 'website' => '', | |||
| 'social' => '', | |||
| 'expectation' => '', | |||
| 'bike_use' => '', | |||
| 'org_participation' => '', | |||
| 'bike_activities' => '', | |||
| 'comments' => '', | |||
| 'ucb_comments' => '', | |||
| ]; | |||
| /** @var string */ | |||
| public $email = ''; | |||
| /** @var string */ | |||
| public $discussion = ''; | |||
| /** @var int */ | |||
| public $contribution = null; | |||
| public function updated($field) | |||
| { | |||
| $this->validateOnly($field, [ | |||
| 'address.city' => ['string'], | |||
| 'address.complement' => ['string'], | |||
| 'address.country' => ['string'], | |||
| 'address.neighbourhood' => ['string'], | |||
| 'address.number' => ['string'], | |||
| 'address.postcode' => ['string'], | |||
| 'address.state' => ['string'], | |||
| 'address.street' => ['string'], | |||
| 'birthday' => ['date_format:d/m/Y'], | |||
| 'contribution' => ['numeric'], | |||
| 'discussion' => ['string', 'in:all,daily,occasional'], | |||
| 'document.number' => ['string'], | |||
| 'document.type' => ['string', 'in:cpf,identity,passport'], | |||
| 'email' => ['email', 'unique:associates'], | |||
| 'profile.bike_activities' => ['string'], | |||
| 'profile.bike_use' => ['string'], | |||
| 'profile.comments' => ['string'], | |||
| 'profile.expectation' => ['string'], | |||
| 'profile.gender' => ['string', 'in:male,female,other'], | |||
| 'profile.occupation' => ['string'], | |||
| 'profile.org_participation' => ['string'], | |||
| 'profile.phone' => ['string'], | |||
| 'profile.scholarity' => ['string', 'in:primary-school,high-school,bachelor,master,phd'], | |||
| 'profile.secondary_emails' => ['string'], | |||
| 'profile.social' => ['string'], | |||
| 'profile.ucb_comments' => ['string'], | |||
| 'profile.website' => ['string'], | |||
| ]); | |||
| if ($this->address['country'] === 'BR' && $field === 'address.postcode') { | |||
| $postcode = Str::slug($this->address['postcode'], ''); | |||
| try { | |||
| $response = Http::timeout(5)->get("https://viacep.com.br/ws/$postcode/json"); | |||
| if ($response->ok()) { | |||
| $address = $response->json(); | |||
| $this->address['city'] = $response['localidade']; | |||
| $this->address['neighbourhood'] = $response['bairro']; | |||
| $this->address['state'] = $response['uf']; | |||
| $this->address['street'] = $response['logradouro']; | |||
| $this->dispatchBrowserEvent('address-autofilled'); | |||
| } | |||
| } catch (\Illuminate\Http\Client\ConnectionException $exception) { | |||
| // TODO: show error to associate | |||
| } | |||
| } | |||
| } | |||
| public function register() | |||
| { | |||
| $this->validate([ | |||
| 'address.city' => ['required', 'string'], | |||
| 'address.complement' => ['nullable', 'string'], | |||
| 'address.country' => ['required', 'string'], | |||
| 'address.neighbourhood' => ['required', 'string'], | |||
| 'address.number' => ['required', 'string'], | |||
| 'address.postcode' => ['required', 'string'], | |||
| 'address.state' => ['required', 'string'], | |||
| 'address.street' => ['required', 'string'], | |||
| 'birthday' => ['required', 'date_format:d/m/Y'], | |||
| 'contribution' => ['required', 'numeric'], | |||
| 'discussion' => ['required', 'string', 'in:all,daily,occasional'], | |||
| 'document.number' => ['required', 'string'], | |||
| 'document.type' => ['required', 'string', 'in:cpf,identity,passport'], | |||
| 'email' => ['required', 'email', 'unique:associates'], | |||
| 'name' => ['required'], | |||
| 'profile.bike_activities' => ['nullable', 'string'], | |||
| 'profile.bike_use' => ['nullable', 'string'], | |||
| 'profile.comments' => ['nullable', 'string'], | |||
| 'profile.expectation' => ['nullable', 'string'], | |||
| 'profile.gender' => ['required', 'string', 'in:male,female,other'], | |||
| 'profile.occupation' => ['required', 'string'], | |||
| 'profile.org_participation' => ['nullable', 'string'], | |||
| 'profile.phone' => ['required', 'string'], | |||
| 'profile.scholarity' => ['required', 'string', 'in:primary-school,high-school,bachelor,master,phd'], | |||
| 'profile.secondary_emails' => ['nullable', 'string'], | |||
| 'profile.social' => ['nullable', 'string'], | |||
| 'profile.ucb_comments' => ['nullable', 'string'], | |||
| 'profile.website' => ['nullable', 'string'], | |||
| ]); | |||
| $associate = new Associate([ | |||
| 'address' => $this->address, | |||
| 'birthday' => Carbon::createFromFormat('d/m/Y', $this->birthday), | |||
| 'contribution' => $this->contribution, | |||
| 'discussion' => $this->discussion, | |||
| 'document' => $this->document, | |||
| 'email' => $this->email, | |||
| 'name' => $this->name, | |||
| 'profile' => $this->profile, | |||
| ]); | |||
| $associate->associate_category_id = AssociateCategory::where('key', 'individual')->first()->id; | |||
| $associate->associate_nature_id = AssociateNature::where('key', 'individual')->first()->id; | |||
| $associate->associate_type_id = AssociateType::where('key', 'individual')->first()->id; | |||
| $associate->save(); | |||
| Auth::login($associate, true); | |||
| redirect(route('home')); | |||
| } | |||
| public function render() | |||
| { | |||
| return view('livewire.collaborators.auth.register.individual'); | |||
| } | |||
| } | |||
| @ -0,0 +1,8 @@ | |||
| @extends('layouts.auth') | |||
| @section('title', 'Sign in to your account') | |||
| @section('content') | |||
| <div> | |||
| @livewire('collaborators.auth.login') | |||
| </div> | |||
| @endsection | |||
| @ -0,0 +1,8 @@ | |||
| @extends('layouts.auth') | |||
| @section('title', 'Reset password') | |||
| @section('content') | |||
| <div> | |||
| @livewire('collaborators.auth.passwords.email') | |||
| </div> | |||
| @endsection | |||
| @ -0,0 +1,10 @@ | |||
| @extends('layouts.auth') | |||
| @section('title', 'Reset password') | |||
| @section('content') | |||
| <div> | |||
| @livewire('collaborators.auth.passwords.reset', [ | |||
| 'token' => $token | |||
| ]) | |||
| </div> | |||
| @endsection | |||
| @ -0,0 +1,8 @@ | |||
| @extends('layouts.auth') | |||
| @section('title', __('user_categories.individual')) | |||
| @section('content') | |||
| <div> | |||
| @livewire('collaborators.auth.register.individual') | |||
| </div> | |||
| @endsection | |||
| @ -0,0 +1,74 @@ | |||
| <div> | |||
| <div class="sm:mx-auto sm:w-full sm:max-w-md"> | |||
| <a href="{{ route('home') }}"> | |||
| <x-logo class="w-auto h-16 mx-auto text-green-600" /> | |||
| </a> | |||
| <h2 class="mt-6 text-3xl font-extrabold text-center text-gray-900 leading-9"> | |||
| {{ __('auth.login') }} | |||
| </h2> | |||
| <p class="mt-2 text-sm text-center text-gray-600 leading-5 max-w"> | |||
| Ou | |||
| <a href="{{ route('register') }}" class="font-medium text-green-600 hover:text-green-500 lowercase focus:outline-none focus:underline transition ease-in-out duration-150"> | |||
| {{ __('auth.register') }} | |||
| </a> | |||
| </p> | |||
| </div> | |||
| <div class="mt-8 sm:mx-auto sm:w-full sm:max-w-md"> | |||
| <div class="px-4 py-8 bg-white shadow sm:rounded-lg sm:px-10"> | |||
| <form wire:submit.prevent="authenticate"> | |||
| <div> | |||
| <label for="email" class="block text-sm font-medium text-gray-700 leading-5"> | |||
| {{ __('users.attributes.email') }} | |||
| </label> | |||
| <div class="mt-1 rounded-md shadow-sm"> | |||
| <input wire:model.lazy="email" id="email" name="email" type="email" required autofocus class="appearance-none block w-full px-3 py-2 border border-gray-300 rounded-md placeholder-gray-400 focus:outline-none focus:shadow-outline-blue focus:border-blue-300 transition duration-150 ease-in-out sm:text-sm sm:leading-5 @error('email') border-red-300 text-red-900 placeholder-red-300 focus:border-red-300 focus:shadow-outline-red @enderror" /> | |||
| </div> | |||
| @error('email') | |||
| <p class="mt-2 text-sm text-red-600">{{ $message }}</p> | |||
| @enderror | |||
| </div> | |||
| <div class="mt-6"> | |||
| <label for="password" class="block text-sm font-medium text-gray-700 leading-5"> | |||
| {{ __('users.attributes.password') }} | |||
| </label> | |||
| <div class="mt-1 rounded-md shadow-sm"> | |||
| <input wire:model.lazy="password" id="password" type="password" required class="appearance-none block w-full px-3 py-2 border border-gray-300 rounded-md placeholder-gray-400 focus:outline-none focus:shadow-outline-blue focus:border-blue-300 transition duration-150 ease-in-out sm:text-sm sm:leading-5 @error('password') border-red-300 text-red-900 placeholder-red-300 focus:border-red-300 focus:shadow-outline-red @enderror" /> | |||
| </div> | |||
| @error('password') | |||
| <p class="mt-2 text-sm text-red-600">{{ $message }}</p> | |||
| @enderror | |||
| </div> | |||
| <div class="flex items-center justify-between mt-6"> | |||
| <div class="flex items-center"> | |||
| <input wire:model.lazy="remember" id="remember" type="checkbox" class="form-checkbox w-4 h-4 text-green-600 transition duration-150 ease-in-out" /> | |||
| <label for="remember" class="block ml-2 text-sm text-gray-900 leading-5"> | |||
| {{ __('auth.remember') }} | |||
| </label> | |||
| </div> | |||
| <div class="text-sm leading-5"> | |||
| <a href="{{ route('password.request') }}" class="font-medium text-green-600 hover:text-green-500 focus:outline-none focus:underline transition ease-in-out duration-150"> | |||
| {{ __('auth.forgot-password') }} | |||
| </a> | |||
| </div> | |||
| </div> | |||
| <div class="mt-6"> | |||
| <span class="block w-full rounded-md shadow-sm"> | |||
| <button type="submit" class="flex justify-center w-full px-4 py-2 text-sm font-medium text-white bg-green-600 border border-transparent rounded-md hover:bg-green-500 focus:outline-none focus:border-green-700 focus:shadow-outline-green active:bg-green-700 transition duration-150 ease-in-out"> | |||
| {{ __('auth.sign-in') }} | |||
| </button> | |||
| </span> | |||
| </div> | |||
| </form> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| @ -0,0 +1,57 @@ | |||
| <div> | |||
| <div class="sm:mx-auto sm:w-full sm:max-w-md"> | |||
| <a href="{{ route('home') }}"> | |||
| <x-logo class="w-auto h-16 mx-auto text-green-600" /> | |||
| </a> | |||
| <h2 class="mt-6 text-3xl font-extrabold text-center text-gray-900 leading-9"> | |||
| Reset password | |||
| </h2> | |||
| </div> | |||
| <div class="mt-8 sm:mx-auto sm:w-full sm:max-w-md"> | |||
| <div class="px-4 py-8 bg-white shadow sm:rounded-lg sm:px-10"> | |||
| @if ($emailSentMessage) | |||
| <div class="rounded-md bg-green-50 p-4"> | |||
| <div class="flex"> | |||
| <div class="flex-shrink-0"> | |||
| <svg class="h-5 w-5 text-green-400" fill="currentColor" viewBox="0 0 20 20"> | |||
| <path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"/> | |||
| </svg> | |||
| </div> | |||
| <div class="ml-3"> | |||
| <p class="text-sm leading-5 font-medium text-green-800"> | |||
| {{ $emailSentMessage }} | |||
| </p> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| @else | |||
| <form wire:submit.prevent="sendResetPasswordLink"> | |||
| <div> | |||
| <label for="email" class="block text-sm font-medium text-gray-700 leading-5"> | |||
| Email address | |||
| </label> | |||
| <div class="mt-1 rounded-md shadow-sm"> | |||
| <input wire:model.lazy="email" id="email" name="email" type="email" required autofocus class="appearance-none block w-full px-3 py-2 border border-gray-300 rounded-md placeholder-gray-400 focus:outline-none focus:shadow-outline-blue focus:border-blue-300 transition duration-150 ease-in-out sm:text-sm sm:leading-5 @error('email') border-red-300 text-red-900 placeholder-red-300 focus:border-red-300 focus:shadow-outline-red @enderror" /> | |||
| </div> | |||
| @error('email') | |||
| <p class="mt-2 text-sm text-red-600">{{ $message }}</p> | |||
| @enderror | |||
| </div> | |||
| <div class="mt-6"> | |||
| <span class="block w-full rounded-md shadow-sm"> | |||
| <button type="submit" class="flex justify-center w-full px-4 py-2 text-sm font-medium text-white bg-green-600 border border-transparent rounded-md hover:bg-green-500 focus:outline-none focus:border-green-700 focus:shadow-outline-green active:bg-green-700 transition duration-150 ease-in-out"> | |||
| Send password reset link | |||
| </button> | |||
| </span> | |||
| </div> | |||
| </form> | |||
| @endif | |||
| </div> | |||
| </div> | |||
| </div> | |||
| @ -0,0 +1,65 @@ | |||
| <div> | |||
| <div class="sm:mx-auto sm:w-full sm:max-w-md"> | |||
| <a href="{{ route('home') }}"> | |||
| <x-logo class="w-auto h-16 mx-auto text-green-600" /> | |||
| </a> | |||
| <h2 class="mt-6 text-3xl font-extrabold text-center text-gray-900 leading-9"> | |||
| Reset password | |||
| </h2> | |||
| </div> | |||
| <div class="mt-8 sm:mx-auto sm:w-full sm:max-w-md"> | |||
| <div class="px-4 py-8 bg-white shadow sm:rounded-lg sm:px-10"> | |||
| <form wire:submit.prevent="resetPassword"> | |||
| <input wire:model="token" type="hidden"> | |||
| <div> | |||
| <label for="email" class="block text-sm font-medium text-gray-700 leading-5"> | |||
| Email address | |||
| </label> | |||
| <div class="mt-1 rounded-md shadow-sm"> | |||
| <input wire:model.lazy="email" id="email" type="email" required autofocus class="appearance-none block w-full px-3 py-2 border border-gray-300 rounded-md placeholder-gray-400 focus:outline-none focus:shadow-outline-blue focus:border-blue-300 transition duration-150 ease-in-out sm:text-sm sm:leading-5 @error('email') border-red-300 text-red-900 placeholder-red-300 focus:border-red-300 focus:shadow-outline-red @enderror" /> | |||
| </div> | |||
| @error('email') | |||
| <p class="mt-2 text-sm text-red-600">{{ $message }}</p> | |||
| @enderror | |||
| </div> | |||
| <div class="mt-6"> | |||
| <label for="password" class="block text-sm font-medium text-gray-700 leading-5"> | |||
| Password | |||
| </label> | |||
| <div class="mt-1 rounded-md shadow-sm"> | |||
| <input wire:model.lazy="password" id="password" type="password" required class="appearance-none block w-full px-3 py-2 border border-gray-300 rounded-md placeholder-gray-400 focus:outline-none focus:shadow-outline-blue focus:border-blue-300 transition duration-150 ease-in-out sm:text-sm sm:leading-5 @error('password') border-red-300 text-red-900 placeholder-red-300 focus:border-red-300 focus:shadow-outline-red @enderror" /> | |||
| </div> | |||
| @error('password') | |||
| <p class="mt-2 text-sm text-red-600">{{ $message }}</p> | |||
| @enderror | |||
| </div> | |||
| <div class="mt-6"> | |||
| <label for="password_confirmation" class="block text-sm font-medium text-gray-700 leading-5"> | |||
| Confirm Password | |||
| </label> | |||
| <div class="mt-1 rounded-md shadow-sm"> | |||
| <input wire:model.lazy="passwordConfirmation" id="password_confirmation" type="password" required class="block w-full px-3 py-2 placeholder-gray-400 border border-gray-300 appearance-none rounded-md focus:outline-none focus:shadow-outline-blue focus:border-blue-300 transition duration-150 ease-in-out sm:text-sm sm:leading-5" /> | |||
| </div> | |||
| </div> | |||
| <div class="mt-6"> | |||
| <span class="block w-full rounded-md shadow-sm"> | |||
| <button type="submit" class="flex justify-center w-full px-4 py-2 text-sm font-medium text-white bg-green-600 border border-transparent rounded-md hover:bg-green-500 focus:outline-none focus:border-green-700 focus:shadow-outline-green active:bg-green-700 transition duration-150 ease-in-out"> | |||
| Reset password | |||
| </button> | |||
| </span> | |||
| </div> | |||
| </form> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| @ -0,0 +1,302 @@ | |||
| <div> | |||
| <div class="sm:mx-auto sm:w-full sm:max-w-md"> | |||
| <a href="{{ route('home') }}"> | |||
| <x-logo class="w-32 h-auto mx-auto text-green-600" /> | |||
| </a> | |||
| <h2 class="mt-6 text-3xl font-extrabold text-center text-gray-900 leading-9"> | |||
| {{ __('user_categories.individual') }} | |||
| </h2> | |||
| <p class="mt-2 text-sm text-center text-gray-600 leading-5 max-w"> | |||
| Ou | |||
| <a href="{{ route('login') }}" class="font-medium text-green-600 hover:text-green-500 lowercase focus:outline-none focus:underline transition ease-in-out duration-150"> | |||
| {{ __('auth.login') }} | |||
| </a> | |||
| </p> | |||
| </div> | |||
| <form wire:submit.prevent="register" class="max-w-7xl mx-auto"> | |||
| <div class="flex flex-wrap pt-8 border-t border-gray-200 mt-8"> | |||
| <div class="lg:pr-6 w-full lg:w-1/3"> | |||
| <h2 class="text-2xl font-medium"> | |||
| Informações pessoais | |||
| </h2> | |||
| <p class="text-gray-400 mt-2"> | |||
| Conte-nos um pouco sobre você. | |||
| </p> | |||
| </div> | |||
| <div class="lg:pl-6 w-full lg:w-2/3"> | |||
| <div class="px-4 py-8 bg-white shadow sm:rounded-lg sm:px-10 space-y-6"> | |||
| <x-input.group label="{{ __('users.attributes.name') }}" for="name" :error="$errors->first('name')"> | |||
| <x-input.text wire:model.lazy="name" id="name" :state="$errors->has('name') ? false : null" required autofocus /> | |||
| </x-input.group> | |||
| <x-input.group label="{{ __('users.attributes.birthday') }}" for="birthday" :error="$errors->first('birthday')"> | |||
| <x-input.text wire:model.lazy="birthday" id="birthday" mask="date" placeholder="dd/mm/yyyy" :state="$errors->has('birthday') ? false : null" required /> | |||
| </x-input.group> | |||
| <x-input.radio-group label="{{ __('users.attributes.profile.gender') }}" :inline="true" :error="$errors->first('profile.gender')"> | |||
| <x-input.radio wire:model.lazy="profile.gender" name="profile-gender" label="Homem" value="male" :state="$errors->has('profile.gender') ? false : null" required /> | |||
| <x-input.radio wire:model.lazy="profile.gender" name="profile-gender" label="Mulher" value="female" :state="$errors->has('profile.gender') ? false : null" required /> | |||
| <x-input.radio wire:model.lazy="profile.gender" name="profile-gender" label="Outro" value="other" :state="$errors->has('profile.gender') ? false : null" required /> | |||
| </x-input-radio-group> | |||
| <div class="flex flex-wrap -mx-2"> | |||
| <x-input.group class="px-2 w-full lg:w-1/2 mt-6 lg:mt-0" label="{{ __('users.attributes.document.type') }}" for="document-type" :error="$errors->first('document.type')"> | |||
| <x-input.select wire:model.lazy="document.type" id="document-type"> | |||
| <option value="cpf">CPF</option> | |||
| <option value="identity">Carteira de Identidade</option> | |||
| <option value="passport">Passaporte</option> | |||
| </x-input.select> | |||
| </x-input.group> | |||
| <x-input.group class="px-2 w-full lg:w-1/2 mt-6 lg:mt-0" label="{{ __('users.attributes.document.number') }}" for="document-number" :error="$errors->first('document.number')"> | |||
| <x-input.text wire:model.lazy="document.number" id="document-number" :state="$errors->has('document.number') ? false : null" required /> | |||
| </x-input.group> | |||
| </div> | |||
| <x-input.group label="{{ __('users.attributes.profile.occupation') }}" for="profile-occupation" :error="$errors->first('profile.occupation')"> | |||
| <x-input.text wire:model.lazy="profile.occupation" id="profile-occupation" placeholder="Profissão ou ocupação principal" :state="$errors->has('profile.occupation') ? false : null" required /> | |||
| </x-input.group> | |||
| <x-input.radio-group label="{{ __('users.attributes.profile.scholarity') }}" :error="$errors->first('profile.scholarity')"> | |||
| <x-input.radio wire:model.lazy="profile.scholarity" name="profile-scholarity" label="Ensino fundamental" value="primary-school" :state="$errors->has('profile.scholarity') ? false : null" required /> | |||
| <x-input.radio wire:model.lazy="profile.scholarity" name="profile-scholarity" label="Ensino médio" value="high-school" :state="$errors->has('profile.scholarity') ? false : null" required /> | |||
| <x-input.radio wire:model.lazy="profile.scholarity" name="profile-scholarity" label="Graduação" value="bachelor" :state="$errors->has('profile.scholarity') ? false : null" required /> | |||
| <x-input.radio wire:model.lazy="profile.scholarity" name="profile-scholarity" label="Mestrado" value="master" :state="$errors->has('profile.scholarity') ? false : null" required /> | |||
| <x-input.radio wire:model.lazy="profile.scholarity" name="profile-scholarity" label="Doutorado" value="phd" :state="$errors->has('profile.scholarity') ? false : null" required /> | |||
| </x-input.radio-group> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <div class="flex flex-wrap pt-8 border-t border-gray-200 mt-8"> | |||
| <div class="lg:pr-6 w-full lg:w-1/3"> | |||
| <h2 class="text-2xl font-medium"> | |||
| Dados de contato | |||
| </h2> | |||
| <p class="text-gray-400 mt-2"> | |||
| Endereço e meios de comunicação digitais. | |||
| </p> | |||
| </div> | |||
| <div class="lg:pl-6 w-full lg:w-2/3"> | |||
| <div class="px-4 py-8 bg-white shadow sm:rounded-lg sm:px-10 space-y-6"> | |||
| <div x-data="{}" class="flex flex-wrap -mx-2"> | |||
| <x-input.group class="px-2 w-full lg:w-1/2" label="{{ __('users.attributes.address.country') }}" for="address-country" :error="$errors->first('address.country')"> | |||
| <x-input.select wire:model="address.country" @input="document.getElementById('address-postcode').dispatchEvent(new Event('refreshMask', {detail: {mask: null}}))" id="address-country" :state="$errors->has('address.country') ? false : null" required> | |||
| @foreach (\App\Country::all() as $country) | |||
| <option value="{{ $country->code }}">{{ __('countries.' . $country->code) }}</option> | |||
| @endforeach | |||
| </x-input.select> | |||
| </x-input.group> | |||
| @if ($address['country'] === 'BR') | |||
| <x-input.group class="px-2 w-full lg:w-1/2" label="{{ __('users.attributes.address.postcode') }}" for="address-postcode-masked" :error="$errors->first('address.postcode')"> | |||
| <x-input.text wire:model.lazy="address.postcode" id="address-postcode-masked" mask="postcode" :state="$errors->has('address.postcode') ? false : null" required /> | |||
| </x-input.group> | |||
| @else | |||
| <x-input.group class="px-2 w-full lg:w-1/2" label="{{ __('users.attributes.address.postcode') }}" for="address-postcode" :error="$errors->first('address.postcode')"> | |||
| <x-input.text wire:model.lazy="address.postcode" id="address-postcode" :state="$errors->has('address.postcode') ? false : null" required /> | |||
| </x-input.group> | |||
| @endif | |||
| </div> | |||
| <x-input.group label="{{ __('users.attributes.address.street') }}" for="address-street" :error="$errors->first('address.street')"> | |||
| <x-input.text wire:model.lazy="address.street" wire:target="address.postcode" wire:loading.attr="disabled" id="address-street" :state="$errors->has('address.street') ? false : null" required /> | |||
| </x-input.group> | |||
| <div class="flex flex-wrap -mx-2"> | |||
| <x-input.group x-data="{}" class="px-2 w-full lg:w-1/4 mt-6 lg:mt-0" label="{{ __('users.attributes.address.number') }}" for="address-number" :error="$errors->first('address.number')"> | |||
| <x-input.text wire:model.lazy="address.number" @address-autofilled.window="$refs.addressNumber.focus()" x-ref="addressNumber" id="address-number" :state="$errors->has('address.number') ? false : null" required /> | |||
| </x-input.group> | |||
| <x-input.group class="px-2 w-full lg:w-1/4" label="{{ __('users.attributes.address.complement') }}" for="address-complement" :error="$errors->first('address.complement')"> | |||
| <x-input.text wire:model.lazy="address.complement" id="address-complement" :state="$errors->has('address.complement') ? false : null" /> | |||
| </x-input.group> | |||
| <x-input.group class="px-2 w-full lg:w-1/2 mt-6 lg:mt-0" label="{{ __('users.attributes.address.neighbourhood') }}" for="address-neighbourhood" :error="$errors->first('address.neighbourhood')"> | |||
| <x-input.text wire:model.lazy="address.neighbourhood" wire:target="address.postcode" wire:loading.attr="disabled" id="address-neighbourhood" :state="$errors->has('address.neighbourhood') ? false : null" required /> | |||
| </x-input.group> | |||
| </div> | |||
| <div class="flex flex-wrap -mx-2"> | |||
| <x-input.group class="px-2 w-full lg:w-1/2" label="{{ __('users.attributes.address.city') }}" for="address-city" :error="$errors->first('address.city')"> | |||
| <x-input.text wire:model.lazy="address.city" wire:target="address.postcode" wire:loading.attr="disabled" id="address-city" :state="$errors->has('address.city') ? false : null" required /> | |||
| </x-input.group> | |||
| <x-input.group class="px-2 w-full lg:w-1/2 mt-6 lg:mt-0" label="{{ __('users.attributes.address.state') }}" for="address-state" :error="$errors->first('address.state')"> | |||
| <x-input.text wire:model.lazy="address.state" wire:target="address.postcode" wire:loading.attr="disabled" id="address-state" :state="$errors->has('address.state') ? false : null" required /> | |||
| </x-input.group> | |||
| </div> | |||
| <x-input.group label="{{ __('users.attributes.profile.phone') }}" for="profile-phone" :error="$errors->first('profile.phone')"> | |||
| <x-input.text wire:model.lazy="profile.phone" id="profile-phone" mask="phone" :state="$errors->has('profile.phone') ? false : null" required /> | |||
| </x-input.group> | |||
| <x-input.group label="{{ __('users.attributes.email') }}" for="email" :error="$errors->first('email')"> | |||
| <x-input.email wire:model.lazy="email" id="email" :state="$errors->has('email') ? false : null" required /> | |||
| </x-input.group> | |||
| <x-input.group label="{{ __('users.attributes.profile.secondary_emails') }}" for="profile-secondary_emails" :error="$errors->first('profile.secondary_emails')"> | |||
| <x-input.textarea wire:model.lazy="profile.secondary_emails" id="profile-secondary_emails" :state="$errors->has('profile.secondary_emails') ? false : null" /> | |||
| </x-input.group> | |||
| <x-input.group label="{{ __('users.attributes.profile.website') }}" for="profile-website" :error="$errors->first('profile.website')"> | |||
| <x-input.text wire:model.lazy="profile.website" id="profile-website" :state="$errors->has('profile.website') ? false : null" /> | |||
| </x-input.group> | |||
| <x-input.group label="{{ __('users.attributes.profile.social') }}" for="profile-social" :error="$errors->first('profile.social')"> | |||
| <x-input.text wire:model.lazy="profile.social" id="profile-social" :state="$errors->has('profile.social') ? false : null" /> | |||
| </x-input.group> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <div class="flex flex-wrap pt-8 border-t border-gray-200 mt-8"> | |||
| <div class="lg:pr-6 w-full lg:w-1/3"> | |||
| <h2 class="text-2xl font-medium"> | |||
| Dados de contato | |||
| </h2> | |||
| <p class="text-gray-400 mt-2"> | |||
| Endereço e meios de comunicação digitais. | |||
| </p> | |||
| </div> | |||
| <div class="lg:pl-6 w-full lg:w-2/3"> | |||
| <div class="px-4 py-8 bg-white shadow sm:rounded-lg sm:px-10 space-y-6"> | |||
| <x-input.radio-group label="{{ __('users.attributes.discussion') }}" :error="$errors->first('discussion')"> | |||
| <x-input.radio wire:model.lazy="discussion" name="discussion" label="Participar da discussão (todos os e-mails)" value="all" :state="$errors->has('discussion') ? false : null" required /> | |||
| <x-input.radio wire:model.lazy="discussion" name="discussion" label="Compilação da discussão (1 e-mail por dia)" value="daily" :state="$errors->has('discussion') ? false : null" required /> | |||
| <x-input.radio wire:model.lazy="discussion" name="discussion" label="Notificações ocasionais da diretoria" value="occasional" :state="$errors->has('discussion') ? false : null" required /> | |||
| </x-input.radio-group> | |||
| <x-input.group label="{{ __('users.attributes.profile.expectation') }}" for="profile-expectation" :error="$errors->first('profile.expectation')"> | |||
| <x-input.textarea wire:model.lazy="profile.expectation" id="profile-expectation" placeholder="O que você espera que UCB consiga ou faça: atuação, conquistas, pautas (descreva brevemente)?" :state="$errors->has('profile.expectation') ? false : null" /> | |||
| </x-input.group> | |||
| <x-input.group label="{{ __('users.attributes.profile.bike_use') }}" for="profile-bike_use" :error="$errors->first('profile.bike_use')"> | |||
| <x-input.textarea wire:model.lazy="profile.bike_use" id="profile-bike_use" placeholder="Por quê e para quê usa a bicicleta? Descreva brevemente" :state="$errors->has('profile.bike_use') ? false : null" /> | |||
| </x-input.group> | |||
| <x-input.group label="{{ __('users.attributes.profile.org_participation') }}" for="profile-org_participation" :error="$errors->first('profile.org_participation')"> | |||
| <x-input.textarea wire:model.lazy="profile.bike_use" id="profile-bike_use" placeholder="Você participa em associações ou coletivos de ciclistas que promovem a bicicleta? Quais? " :state="$errors->has('profile.bike_use') ? false : null" /> | |||
| </x-input.group> | |||
| <x-input.group label="{{ __('users.attributes.profile.bike_activities') }}" for="profile-bike_activities" :error="$errors->first('profile.bike_activities')"> | |||
| <x-input.textarea wire:model.lazy="profile.bike_use" id="profile-bike_use" placeholder="Realiza atividades ou participa para promover a bicicleta (descreva brevemente)?" :state="$errors->has('profile.bike_use') ? false : null" /> | |||
| </x-input.group> | |||
| <x-input.radio-group | |||
| x-data="{ custom_contribution: false }" | |||
| @click="custom_contribution = false" | |||
| label="{{ __('users.attributes.contribution') }}" | |||
| for="contribution" | |||
| :error="$errors->first('contribution')" | |||
| > | |||
| <x-input.radio wire:model.lazy="contribution" name="contribution" label="Não desejo contribuir" value="0" :state="$errors->has('contribution') ? false : null" required /> | |||
| <x-input.radio wire:model.lazy="contribution" name="contribution" label="R$ 15,00" value="15" :state="$errors->has('contribution') ? false : null" required /> | |||
| <x-input.radio wire:model.lazy="contribution" name="contribution" label="R$ 30,00" value="30" :state="$errors->has('contribution') ? false : null" required /> | |||
| <x-input.radio wire:model.lazy="contribution" name="contribution" label="R$ 60,00" value="60" :state="$errors->has('contribution') ? false : null" required /> | |||
| <x-input.radio wire:model.lazy="contribution" name="contribution" label="R$ 120,00" value="120" :state="$errors->has('contribution') ? false : null" required /> | |||
| <x-input.radio | |||
| name="contribution" | |||
| @click=" | |||
| $event.stopPropagation(); | |||
| custom_contribution = true; | |||
| $nextTick(() => { $refs.customContributionInput.focus() }); | |||
| " | |||
| :state="$errors->has('contribution') ? false : null" | |||
| required | |||
| > | |||
| <span class="text-sm">Outro</span> | |||
| <x-input.text | |||
| wire:model.lazy="contribution" | |||
| x-ref="customContributionInput" | |||
| @click="$event.stopPropagation();" | |||
| x-show="custom_contribution" | |||
| id="contribution-other" | |||
| mask="currency" | |||
| :state="$errors->has('contribution') ? false : null" | |||
| /> | |||
| </x-input.radio> | |||
| </x-input.radio-group> | |||
| </div> | |||
| </div> | |||
| </div> | |||
| <div class="flex flex-wrap pt-8 border-t border-gray-200 mt-8"> | |||
| <div class="lg:pr-6 w-full lg:w-1/3"> | |||
| <h2 class="text-2xl font-medium"> | |||
| Declaração de consentimento | |||
| </h2> | |||
| </div> | |||
| <div class="lg:pl-6 w-full lg:w-2/3"> | |||
| <p> | |||
| Declaro que: | |||
| </p> | |||
| <ol class="list-decimal list-outside leading-relaxed pl-4 mt-3 space-y-2"> | |||
| <li>Apoio e concordo com as Finalidades, Princípios, Objetivos e demais termos do Estatuto da UCB (http://www.uniaodeciclistas.org.br/sobre-a-ucb/estatuto/);</li> | |||
| <li>Confirmarei e/ou atualizará estes dados cadastrais uma vez por ano;</li> | |||
| <li>Caso solicitado, prestarei informações adicionais e/ou enviará documentos para a Diretoria da UCB.</li> | |||
| </ol> | |||
| </div> | |||
| </div> | |||
| <div class="flex justify-center pt-12"> | |||
| <span class="block w-full lg:w-auto rounded-md shadow-sm"> | |||
| <button type="submit" class="flex justify-center w-full px-16 py-2 text-sm font-medium text-white bg-green-600 border border-transparent rounded-md hover:bg-green-500 focus:outline-none focus:border-green-700 focus:shadow-outline-green active:bg-green-700 transition duration-150 ease-in-out"> | |||
| {{ __('auth.sign-up') }} | |||
| </button> | |||
| </span> | |||
| </div> | |||
| </form> | |||
| </div> | |||