diff --git a/app/Http/Controllers/InvitedOnboardingController.php b/app/Http/Controllers/InvitedOnboardingController.php new file mode 100644 index 0000000..1ded3b8 --- /dev/null +++ b/app/Http/Controllers/InvitedOnboardingController.php @@ -0,0 +1,37 @@ +hasValidSignature()) { + abort(401, 'Invalid signature'); + } + + // user doesn't have password + if (is_null($user->password)) { + + // route to create password form + return view('auth.invited-onboarding', [ + 'portfolio' => $portfolio, + 'user' => $user + ]); + } + + // redirect user to portfolio + return redirect(route('portfolio.show', ['portfolio' => $portfolio->id])); + } +} diff --git a/app/Notifications/InvitedToPortfolioNotification.php b/app/Notifications/InvitedOnboardingNotification.php similarity index 74% rename from app/Notifications/InvitedToPortfolioNotification.php rename to app/Notifications/InvitedOnboardingNotification.php index 7d2d4db..522d0eb 100644 --- a/app/Notifications/InvitedToPortfolioNotification.php +++ b/app/Notifications/InvitedOnboardingNotification.php @@ -9,7 +9,7 @@ use Illuminate\Notifications\Notification; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Notifications\Messages\MailMessage; -class InvitedToPortfolioNotification extends Notification implements ShouldQueue +class InvitedOnboardingNotification extends Notification implements ShouldQueue { use Queueable; @@ -37,13 +37,15 @@ class InvitedToPortfolioNotification extends Notification implements ShouldQueue public function toMail(object $notifiable): MailMessage { + $url = url()->signedRoute('invited_onboarding', ['portfolio' => $this->portfolio->id, 'user' => $notifiable->id], now()->addDays(90)); + return (new MailMessage) ->replyTo($this->sender->email, $this->sender->name) ->greeting('Hey there! 👋') - ->subject("{$this->sender->name} invited you to {$this->portfolio->title} on Investbrain!") + ->subject("You've been invited to {$this->portfolio->title} on Investbrain!") ->line("{$this->sender->name} has invited you to **{$this->portfolio->title}** on Investbrain, Smart open-source investment tracker that consolidates and monitors market performance across your different brokerages.") - ->line('Once you\'re in, you\'ll be able to see holdings, dividends, market performance and more!') - ->action("Get Started", route('portfolio.show', ['portfolio' => $this->portfolio->id])) + ->line("Once you're in, you'll be able to see all the holdings, dividends, market performance and more for {$this->portfolio->title}!") + ->action("Get Started", $url) ->line("If you have any questions, you can reply to this email.") ->salutation("See you there,\n". e($this->sender->name)); } diff --git a/app/Notifications/VerifyConnectedAccountNotification.php b/app/Notifications/VerifyConnectedAccountNotification.php index 3cb3e67..5164579 100644 --- a/app/Notifications/VerifyConnectedAccountNotification.php +++ b/app/Notifications/VerifyConnectedAccountNotification.php @@ -37,12 +37,14 @@ class VerifyConnectedAccountNotification extends Notification implements ShouldQ $verification = ConnectedAccountVerification::find($this->verification_id); $provider = config("services.$verification->provider.name"); + $url = url()->signedRoute('oauth.verify_connected_account', ['verification_id' => $this->verification_id], now()->days($days = 7)); + return (new MailMessage) ->greeting('Welcome back!') ->subject("Connect your $provider account with Investbrain") ->line("You recently attempted to log into an existing Investbrain account using $provider. To safeguard your Investbrain account, please confirm this was you by pressing the 'Connect $provider' button below:") - ->action("Connect $provider", route('oauth.verify_connected_account', ['verification_id' => $this->verification_id])) - ->line('If you do not recognize this activity, we recommend [changing your password]('.route('profile.show').') as soon as possible. Otherwise, you can disregard this message.'); + ->action("Connect $provider", $url) + ->line("If you do not recognize this activity, we recommend [changing your password](".route('profile.show').") as soon as possible. Otherwise, you can disregard this message. This link will expire in {$days} days."); } /** diff --git a/lang/en.json b/lang/en.json index 287c401..af881b6 100644 --- a/lang/en.json +++ b/lang/en.json @@ -354,5 +354,11 @@ "Allow this user to manage portfolio details and create or update transactions": "Allow this user to manage portfolio details and create or update transactions", "Share": "Share", "Remove Access": "Remove Access", - "By removing this person's access, they will no longer be able to view this portfolio. They will lose access immediately.": "By removing this person's access, they will no longer be able to view this portfolio. They will lose access immediately." + "By removing this person's access, they will no longer be able to view this portfolio. They will lose access immediately.": "By removing this person's access, they will no longer be able to view this portfolio. They will lose access immediately.", + + "Hey again!": "Hey again!", + "Before you can get started with Investbrain, you'll want to create a password:": "Before you can get started with Investbrain, you'll want to create a password:", + "Or login with SSO:": "Or login with SSO:", + "Create Password": "Create Password" + } \ No newline at end of file diff --git a/lang/es.json b/lang/es.json index 1696bc7..d8e9072 100644 --- a/lang/es.json +++ b/lang/es.json @@ -354,5 +354,10 @@ "Allow this user to manage portfolio details and create or update transactions": "Permitir a este usuario administrar detalles de portafolio y crear o actualizar transacciones", "Share": "Compartir", "Remove Access": "Eliminar acceso", - "By removing this person's access, they will no longer be able to view this portfolio. They will lose access immediately.": "Al eliminar el acceso de esta persona, ya no podrá ver este portafolio. Perderán el acceso inmediatamente." + "By removing this person's access, they will no longer be able to view this portfolio. They will lose access immediately.": "Al eliminar el acceso de esta persona, ya no podrá ver este portafolio. Perderán el acceso inmediatamente.", + + "Hey again!": "¡Oye de nuevo!", + "Before you can get started with Investbrain, you'll want to create a password:": "Antes de poder comenzar a utilizar Investbrain, deberá crear una cuenta:", + "Or login with SSO:": "O iniciar sesión mediante SSO:", + "Create Password": "Crear Contraseña" } \ No newline at end of file diff --git a/resources/views/auth/forgot-password.blade.php b/resources/views/auth/forgot-password.blade.php index f225fb4..37629cf 100644 --- a/resources/views/auth/forgot-password.blade.php +++ b/resources/views/auth/forgot-password.blade.php @@ -27,7 +27,7 @@
- + {{ __('Email Password Reset Link') }}
diff --git a/resources/views/auth/invited-onboarding.blade.php b/resources/views/auth/invited-onboarding.blade.php new file mode 100644 index 0000000..7a842b4 --- /dev/null +++ b/resources/views/auth/invited-onboarding.blade.php @@ -0,0 +1,24 @@ + + + +
+ +
+
+ +

{{ __('Hey again!') }} 👋

+

{{ __('Before you can get started with Investbrain, you\'ll want to create a password:') }}

+ + @livewire('invited-onboarding-form', [ + 'portfolio' => $portfolio, + 'user' => $user, + ]) + + + +

{{ __('Or login with SSO:') }}

+ + + +
+
diff --git a/resources/views/auth/login.blade.php b/resources/views/auth/login.blade.php index ead8a29..a278a37 100644 --- a/resources/views/auth/login.blade.php +++ b/resources/views/auth/login.blade.php @@ -51,29 +51,15 @@ -
+ + + + {{ __('Sign up with email') }} + - @foreach(explode(',', config('services.enabled_login_providers')) as $provider) - - @include("components.$provider-icon") - - {{ __('Login with') }} {{ config("services.$provider.name") }} - - @endforeach - - - {{ __('Sign up with email') }} - - -
@endif diff --git a/resources/views/auth/reset-password.blade.php b/resources/views/auth/reset-password.blade.php index 5a3b217..3c24705 100644 --- a/resources/views/auth/reset-password.blade.php +++ b/resources/views/auth/reset-password.blade.php @@ -29,7 +29,7 @@
- + {{ __('Reset Password') }}
diff --git a/resources/views/components/connected-accounts-login.blade.php b/resources/views/components/connected-accounts-login.blade.php new file mode 100644 index 0000000..b5149b6 --- /dev/null +++ b/resources/views/components/connected-accounts-login.blade.php @@ -0,0 +1,14 @@ +
+ @foreach(explode(',', config('services.enabled_login_providers')) as $provider) + + @include("components.$provider-icon") + + {{ __('Login with') }} {{ config("services.$provider.name") }} + + @endforeach +
\ No newline at end of file diff --git a/resources/views/dashboard.blade.php b/resources/views/dashboard.blade.php index 5814380..5891a90 100644 --- a/resources/views/dashboard.blade.php +++ b/resources/views/dashboard.blade.php @@ -59,7 +59,7 @@ - @if (!$user->portfolios->isEmpty()) + @if (!$user->transactions->isEmpty()) @livewire('transactions-list', [ diff --git a/resources/views/livewire/invited-onboarding-form.blade.php b/resources/views/livewire/invited-onboarding-form.blade.php new file mode 100644 index 0000000..7bc46d1 --- /dev/null +++ b/resources/views/livewire/invited-onboarding-form.blade.php @@ -0,0 +1,55 @@ +validate(); + + $this->user->password = Hash::make($this->password); + $this->user->save(); + + Auth::login($this->user, true); + + return redirect(route('portfolio.show', ['portfolio' => $this->portfolio->id])); + } + +}; ?> + + + +
+ + +
+ +
+ + +
+ +
+ + + {{ __('Create Password') }} + +
+
\ No newline at end of file diff --git a/resources/views/livewire/share-portfolio-form.blade.php b/resources/views/livewire/share-portfolio-form.blade.php index 12a0e56..ddc4e73 100644 --- a/resources/views/livewire/share-portfolio-form.blade.php +++ b/resources/views/livewire/share-portfolio-form.blade.php @@ -6,7 +6,7 @@ use Livewire\Attributes\Rule; use Livewire\Volt\Component; use Illuminate\Support\Collection; use Mary\Traits\Toast; -use App\Notifications\InvitedToPortfolioNotification; +use App\Notifications\InvitedOnboardingNotification; new class extends Component { @@ -105,7 +105,7 @@ new class extends Component { if (!empty($sync['attached'])) { foreach($sync['attached'] as $newUserId) { - User::find($newUserId)->notify(new InvitedToPortfolioNotification($this->portfolio, auth()->user())); + User::find($newUserId)->notify(new InvitedOnboardingNotification($this->portfolio, auth()->user())); }; } @@ -150,30 +150,38 @@ new class extends Component { + + + {{ $user->name }} + + @if (auth()->user()->id == $user->id) + ({{ __('you') }}) + @endif + {{ $user->email }} + + @if (auth()->user()->id != $user->id) - @if($user->id != auth()->user()->id) + - + @endif - @endforeach diff --git a/routes/web.php b/routes/web.php index e1b604d..5653577 100644 --- a/routes/web.php +++ b/routes/web.php @@ -6,6 +6,7 @@ use App\Http\Controllers\DashboardController; use App\Http\Controllers\PortfolioController; use App\Http\Controllers\ConnectedAccountController; use App\Http\Controllers\TransactionController; +use App\Http\Controllers\InvitedOnboardingController; use Laravel\Jetstream\Http\Controllers\Livewire\PrivacyPolicyController; use Laravel\Jetstream\Http\Controllers\Livewire\TermsOfServiceController; @@ -35,7 +36,10 @@ Route::middleware(['auth:sanctum', config('jetstream.auth_session')])->group(fun Route::get('/transactions', [TransactionController::class, 'index'])->name('transaction.index'); }); -// overwrites jetstream routes +// Invited onboarding +Route::get('invite/{portfolio}/{user}', InvitedOnboardingController::class)->name('invited_onboarding')->scopeBindings(); + +// Overwrites Jetstream routes Route::get('/terms', [TermsOfServiceController::class, 'show'])->name('terms.show'); Route::get('/privacy', [PrivacyPolicyController::class, 'show'])->name('policy.show');