Update
This commit is contained in:
32
app/Helpers/FirstParty/Stripe/StripeHelper.php
Normal file
32
app/Helpers/FirstParty/Stripe/StripeHelper.php
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Helpers\FirstParty\Stripe;
|
||||||
|
|
||||||
|
use Laravel\Cashier\Events\WebhookReceived;
|
||||||
|
|
||||||
|
class StripeHelper
|
||||||
|
{
|
||||||
|
public static function handleSubscriptionWebhookEvents(WebhookReceived $event)
|
||||||
|
{
|
||||||
|
switch ($event->payload['type']) {
|
||||||
|
case 'customer.subscription.created':
|
||||||
|
case 'customer.subscription.updated':
|
||||||
|
self::handleSubscriptionUpsert($event);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'customer.subscription.deleted':
|
||||||
|
self::handleSubscriptionDelete($event);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function handleSubscriptionUpsert(WebhookReceived $event)
|
||||||
|
{
|
||||||
|
///
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function handleSubscriptionDelete(WebhookReceived $event)
|
||||||
|
{
|
||||||
|
///
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -33,7 +33,7 @@ public function store(LoginRequest $request): RedirectResponse
|
|||||||
|
|
||||||
$request->session()->regenerate();
|
$request->session()->regenerate();
|
||||||
|
|
||||||
return redirect()->intended(route('dashboard', absolute: false));
|
return redirect()->intended(route(config('platform.general.authed_route_redirect'), absolute: false));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -46,6 +46,6 @@ public function destroy(Request $request): RedirectResponse
|
|||||||
$request->session()->invalidate();
|
$request->session()->invalidate();
|
||||||
$request->session()->regenerateToken();
|
$request->session()->regenerateToken();
|
||||||
|
|
||||||
return redirect('/');
|
return redirect(route('home'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,6 +36,6 @@ public function store(Request $request): RedirectResponse
|
|||||||
|
|
||||||
$request->session()->put('auth.password_confirmed_at', time());
|
$request->session()->put('auth.password_confirmed_at', time());
|
||||||
|
|
||||||
return redirect()->intended(route('dashboard', absolute: false));
|
return redirect()->intended(route(config('platform.general.authed_route_redirect'), absolute: false));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ class EmailVerificationNotificationController extends Controller
|
|||||||
public function store(Request $request): RedirectResponse
|
public function store(Request $request): RedirectResponse
|
||||||
{
|
{
|
||||||
if ($request->user()->hasVerifiedEmail()) {
|
if ($request->user()->hasVerifiedEmail()) {
|
||||||
return redirect()->intended(route('dashboard', absolute: false));
|
return redirect()->intended(route(config('platform.general.authed_route_redirect'), absolute: false));
|
||||||
}
|
}
|
||||||
|
|
||||||
$request->user()->sendEmailVerificationNotification();
|
$request->user()->sendEmailVerificationNotification();
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ class EmailVerificationPromptController extends Controller
|
|||||||
public function __invoke(Request $request): Response|RedirectResponse
|
public function __invoke(Request $request): Response|RedirectResponse
|
||||||
{
|
{
|
||||||
return $request->user()->hasVerifiedEmail()
|
return $request->user()->hasVerifiedEmail()
|
||||||
? redirect()->intended(route('dashboard', absolute: false))
|
? redirect()->intended(route(config('platform.general.authed_route_redirect'), absolute: false))
|
||||||
: Inertia::render('auth/verify-email', ['status' => $request->session()->get('status')]);
|
: Inertia::render('auth/verify-email', ['status' => $request->session()->get('status')]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,7 +31,7 @@ public function create(): Response
|
|||||||
public function store(Request $request): RedirectResponse
|
public function store(Request $request): RedirectResponse
|
||||||
{
|
{
|
||||||
$request->validate([
|
$request->validate([
|
||||||
'email' => 'required|string|lowercase|email|max:255|unique:'.User::class,
|
'email' => 'required|string|lowercase|email|max:255|unique:' . User::class,
|
||||||
'password' => ['required', 'confirmed', Rules\Password::defaults()],
|
'password' => ['required', 'confirmed', Rules\Password::defaults()],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@@ -44,6 +44,6 @@ public function store(Request $request): RedirectResponse
|
|||||||
|
|
||||||
Auth::login($user);
|
Auth::login($user);
|
||||||
|
|
||||||
return to_route('dashboard');
|
return to_route(config('platform.general.authed_route_redirect'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ class VerifyEmailController extends Controller
|
|||||||
public function __invoke(EmailVerificationRequest $request): RedirectResponse
|
public function __invoke(EmailVerificationRequest $request): RedirectResponse
|
||||||
{
|
{
|
||||||
if ($request->user()->hasVerifiedEmail()) {
|
if ($request->user()->hasVerifiedEmail()) {
|
||||||
return redirect()->intended(route('dashboard', absolute: false).'?verified=1');
|
return redirect()->intended(route(config('platform.general.authed_route_redirect'), absolute: false) . '?verified=1');
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($request->user()->markEmailAsVerified()) {
|
if ($request->user()->markEmailAsVerified()) {
|
||||||
@@ -25,6 +25,6 @@ public function __invoke(EmailVerificationRequest $request): RedirectResponse
|
|||||||
event(new Verified($user));
|
event(new Verified($user));
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirect()->intended(route('dashboard', absolute: false).'?verified=1');
|
return redirect()->intended(route(config('platform.general.authed_route_redirect'), absolute: false) . '?verified=1');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
use App;
|
use App;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
|
use App\Models\UserPlan;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
use Laravel\Socialite\Facades\Socialite;
|
use Laravel\Socialite\Facades\Socialite;
|
||||||
|
|
||||||
@@ -60,7 +61,7 @@ public function handleGoogleCallback()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirect()->intended(route('home'));
|
return redirect()->intended(route('home'))->with('success', "You're now logged in!");
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
|
|
||||||
throw $e;
|
throw $e;
|
||||||
@@ -73,7 +74,17 @@ public function handleGoogleCallback()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private function setupUser($user) {}
|
private function setupUser($user)
|
||||||
|
{
|
||||||
|
$user_plan = UserPlan::where('user_id', $user->id)->first();
|
||||||
|
|
||||||
|
if (!$user_plan) {
|
||||||
|
$user_plan = UserPlan::create([
|
||||||
|
'user_id' => $user->id,
|
||||||
|
'plan_id' => 'free',
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private function getMockGoogleUser()
|
private function getMockGoogleUser()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -32,10 +32,11 @@ public function subscribe(Request $request)
|
|||||||
|
|
||||||
$payload = [
|
$payload = [
|
||||||
'mode' => 'subscription',
|
'mode' => 'subscription',
|
||||||
'success_url' => route('subscribe.success').'?'.'session_id={CHECKOUT_SESSION_ID}',
|
'success_url' => route('subscribe.success') . '?' . 'session_id={CHECKOUT_SESSION_ID}',
|
||||||
'cancel_url' => route('subscribe.cancelled').'?'.'session_id={CHECKOUT_SESSION_ID}',
|
'cancel_url' => route('subscribe.cancelled') . '?' . 'session_id={CHECKOUT_SESSION_ID}',
|
||||||
'line_items' => [[
|
'line_items' => [[
|
||||||
'price' => $price_id,
|
'price' => $price_id,
|
||||||
|
'quantity' => 1,
|
||||||
]],
|
]],
|
||||||
];
|
];
|
||||||
|
|
||||||
@@ -60,10 +61,7 @@ public function subscribeSuccess(Request $request)
|
|||||||
|
|
||||||
Session::forget('checkout_session_id');
|
Session::forget('checkout_session_id');
|
||||||
|
|
||||||
return redirect()->route('home')->with('success', [
|
return redirect()->route('home')->with('success', 'Thank you for subscribing! Your subscription should be active momentarily. Please refresh the page if you do not see your plan.');
|
||||||
'message' => 'Thank you for subscribing! Your subscription should be active momentarily. Please refresh the page if you do not see your plan.',
|
|
||||||
'action' => 'subscription_success',
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function subscribeCancelled(Request $request)
|
public function subscribeCancelled(Request $request)
|
||||||
@@ -73,10 +71,7 @@ public function subscribeCancelled(Request $request)
|
|||||||
Session::forget('checkout_session_id');
|
Session::forget('checkout_session_id');
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirect()->route('home')->with('error', [
|
return redirect()->route('home')->with('error', "You've decided not to complete the payment at this time. No charges have been made to your account.");
|
||||||
'message' => "You've decided not to complete the payment at this time. No charges have been made to your account.",
|
|
||||||
'action' => 'subscription_cancelled',
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// PURCHASE (ONE TIME)
|
// PURCHASE (ONE TIME)
|
||||||
@@ -86,8 +81,8 @@ public function purchase(Request $request)
|
|||||||
$price_id = $request->input('price_id');
|
$price_id = $request->input('price_id');
|
||||||
|
|
||||||
$payload = [
|
$payload = [
|
||||||
'success_url' => route('subscribe.success').'?'.'session_id={CHECKOUT_SESSION_ID}',
|
'success_url' => route('subscribe.success') . '?' . 'session_id={CHECKOUT_SESSION_ID}',
|
||||||
'cancel_url' => route('subscribe.cancelled').'?'.'session_id={CHECKOUT_SESSION_ID}',
|
'cancel_url' => route('subscribe.cancelled') . '?' . 'session_id={CHECKOUT_SESSION_ID}',
|
||||||
];
|
];
|
||||||
|
|
||||||
$checkout_session = Auth::user()->checkout([$price_id => 1], $payload);
|
$checkout_session = Auth::user()->checkout([$price_id => 1], $payload);
|
||||||
@@ -112,10 +107,7 @@ public function purchaseSuccess(Request $request)
|
|||||||
|
|
||||||
Session::forget('checkout_session_id');
|
Session::forget('checkout_session_id');
|
||||||
|
|
||||||
return redirect()->route('home')->with('success', [
|
return redirect()->route('home')->with('success', "Thank you for purchasing! Your purchase should be active momentarily. Please refresh the page if you do not see your plan.");
|
||||||
'message' => 'Thank you for purchasing! Your purchase should be active momentarily. Please refresh the page if you do not see your plan.',
|
|
||||||
'action' => 'purchase_success',
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function purchaseCancelled(Request $request)
|
public function purchaseCancelled(Request $request)
|
||||||
@@ -124,9 +116,6 @@ public function purchaseCancelled(Request $request)
|
|||||||
Session::forget('checkout_session_id');
|
Session::forget('checkout_session_id');
|
||||||
}
|
}
|
||||||
|
|
||||||
return redirect()->route('home')->with('error', [
|
return redirect()->route('home')->with('error', "You've decided not to complete the payment at this time. No charges have been made to your account.");
|
||||||
'message' => "You've decided not to complete the payment at this time. No charges have been made to your account.",
|
|
||||||
'action' => 'purchase_cancelled',
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,11 +47,16 @@ public function share(Request $request): array
|
|||||||
'user' => $request->user(),
|
'user' => $request->user(),
|
||||||
'user_is_admin' => user_is_master_admin($request->user()),
|
'user_is_admin' => user_is_master_admin($request->user()),
|
||||||
],
|
],
|
||||||
'ziggy' => fn (): array => [
|
'ziggy' => fn(): array => [
|
||||||
...(new Ziggy)->toArray(),
|
...(new Ziggy)->toArray(),
|
||||||
'location' => $request->url(),
|
'location' => $request->url(),
|
||||||
],
|
],
|
||||||
'sidebarOpen' => ! $request->hasCookie('sidebar_state') || $request->cookie('sidebar_state') === 'true',
|
'sidebarOpen' => ! $request->hasCookie('sidebar_state') || $request->cookie('sidebar_state') === 'true',
|
||||||
|
'flash' => [
|
||||||
|
'message' => fn() => $request->session()->get('message'),
|
||||||
|
'error' => fn() => $request->session()->get('error'),
|
||||||
|
'success' => fn() => $request->session()->get('success'),
|
||||||
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
28
app/Listeners/StripeEventListener.php
Normal file
28
app/Listeners/StripeEventListener.php
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Listeners;
|
||||||
|
|
||||||
|
use App\Helpers\FirstParty\Stripe\StripeHelper;
|
||||||
|
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||||
|
use Illuminate\Queue\InteractsWithQueue;
|
||||||
|
|
||||||
|
use Laravel\Cashier\Events\WebhookReceived;
|
||||||
|
|
||||||
|
class StripeEventListener
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Create the event listener.
|
||||||
|
*/
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle the event.
|
||||||
|
*/
|
||||||
|
public function handle(WebhookReceived $event): void
|
||||||
|
{
|
||||||
|
StripeHelper::handleSubscriptionWebhookEvents($event);
|
||||||
|
}
|
||||||
|
}
|
||||||
31
app/Models/Plan.php
Normal file
31
app/Models/Plan.php
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Reliese Model.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class Plan
|
||||||
|
*
|
||||||
|
* @property int $id
|
||||||
|
* @property string $name
|
||||||
|
* @property string $tier
|
||||||
|
* @property Carbon|null $created_at
|
||||||
|
* @property Carbon|null $updated_at
|
||||||
|
*
|
||||||
|
* @package App\Models
|
||||||
|
*/
|
||||||
|
class Plan extends Model
|
||||||
|
{
|
||||||
|
protected $table = 'plans';
|
||||||
|
|
||||||
|
protected $fillable = [
|
||||||
|
'name',
|
||||||
|
'tier'
|
||||||
|
];
|
||||||
|
}
|
||||||
@@ -24,6 +24,7 @@ class User extends Authenticatable
|
|||||||
'email',
|
'email',
|
||||||
'password',
|
'password',
|
||||||
'uuid',
|
'uuid',
|
||||||
|
'google_id',
|
||||||
];
|
];
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
45
app/Models/UserPlan.php
Normal file
45
app/Models/UserPlan.php
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Reliese Model.
|
||||||
|
*/
|
||||||
|
|
||||||
|
namespace App\Models;
|
||||||
|
|
||||||
|
use Carbon\Carbon;
|
||||||
|
use Illuminate\Database\Eloquent\Model;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class UserPlan
|
||||||
|
*
|
||||||
|
* @property int $id
|
||||||
|
* @property int $user_id
|
||||||
|
* @property int $plan_id
|
||||||
|
* @property Carbon|null $current_period_end
|
||||||
|
* @property Carbon|null $cancel_at
|
||||||
|
* @property Carbon|null $canceled_at
|
||||||
|
* @property Carbon|null $created_at
|
||||||
|
* @property Carbon|null $updated_at
|
||||||
|
*
|
||||||
|
* @package App\Models
|
||||||
|
*/
|
||||||
|
class UserPlan extends Model
|
||||||
|
{
|
||||||
|
protected $table = 'user_plans';
|
||||||
|
|
||||||
|
protected $casts = [
|
||||||
|
'user_id' => 'int',
|
||||||
|
'plan_id' => 'int',
|
||||||
|
'current_period_end' => 'datetime',
|
||||||
|
'cancel_at' => 'datetime',
|
||||||
|
'canceled_at' => 'datetime'
|
||||||
|
];
|
||||||
|
|
||||||
|
protected $fillable = [
|
||||||
|
'user_id',
|
||||||
|
'plan_id',
|
||||||
|
'current_period_end',
|
||||||
|
'cancel_at',
|
||||||
|
'canceled_at'
|
||||||
|
];
|
||||||
|
}
|
||||||
29
database/migrations/2025_07_01_145515_create_plans_table.php
Normal file
29
database/migrations/2025_07_01_145515_create_plans_table.php
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::create('plans', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
$table->string('name');
|
||||||
|
$table->string('tier');
|
||||||
|
$table->timestamps();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('plans');
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::create('user_plans', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
$table->foreignId('user_id');
|
||||||
|
$table->foreignId('plan_id');
|
||||||
|
$table->dateTime('current_period_end')->nullable();
|
||||||
|
$table->dateTime('cancel_at')->nullable();
|
||||||
|
$table->dateTime('canceled_at')->nullable();
|
||||||
|
$table->timestamps();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('user_plans');
|
||||||
|
}
|
||||||
|
};
|
||||||
43
database/seeders/PlanSeeder.php
Normal file
43
database/seeders/PlanSeeder.php
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Database\Seeders;
|
||||||
|
|
||||||
|
use DB;
|
||||||
|
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
|
||||||
|
use Illuminate\Database\Seeder;
|
||||||
|
|
||||||
|
class PlanSeeder extends Seeder
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the database seeds.
|
||||||
|
*/
|
||||||
|
public function run(): void
|
||||||
|
{
|
||||||
|
$plans = config('platform.purchases.subscriptions');
|
||||||
|
|
||||||
|
foreach ($plans as $plan) {
|
||||||
|
|
||||||
|
if ($plan['type'] !== 'subscription_plans') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if plan with this tier already exists
|
||||||
|
$existingPlan = DB::table('plans')
|
||||||
|
->where('tier', $plan['id'])
|
||||||
|
->first();
|
||||||
|
|
||||||
|
if (! $existingPlan) {
|
||||||
|
DB::table('plans')->insert([
|
||||||
|
'name' => $plan['name'],
|
||||||
|
'tier' => $plan['id'],
|
||||||
|
'created_at' => now(),
|
||||||
|
'updated_at' => now(),
|
||||||
|
]);
|
||||||
|
|
||||||
|
$this->command->info("Inserted plan: {$plan['name']}");
|
||||||
|
} else {
|
||||||
|
$this->command->info("Skipped existing plan tier: {$plan['id']}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,7 +10,7 @@ import AppLogo from './app-logo';
|
|||||||
const mainNavItems: NavItem[] = [
|
const mainNavItems: NavItem[] = [
|
||||||
{
|
{
|
||||||
title: 'Dashboard',
|
title: 'Dashboard',
|
||||||
href: route('dashboard'),
|
href: route(config('platform.general.authed_route_redirect')),
|
||||||
icon: LayoutGrid,
|
icon: LayoutGrid,
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } f
|
|||||||
import { useMitt } from '@/plugins/MittContext';
|
import { useMitt } from '@/plugins/MittContext';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
const AuthDialog = ({ onOpenChange }) => {
|
const AuthDialog = ({}) => {
|
||||||
const emitter = useMitt();
|
const emitter = useMitt();
|
||||||
|
|
||||||
const [isLogin, setIsLogin] = useState(false);
|
const [isLogin, setIsLogin] = useState(false);
|
||||||
@@ -21,13 +21,31 @@ const AuthDialog = ({ onOpenChange }) => {
|
|||||||
setIsOpen(true);
|
setIsOpen(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleOpenLogin = () => {
|
||||||
|
setIsLogin(true);
|
||||||
|
setIsOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleOpenJoin = () => {
|
||||||
|
setIsLogin(false);
|
||||||
|
setIsOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
emitter.on('401', handleOpenAuth);
|
emitter.on('401', handleOpenAuth);
|
||||||
|
emitter.on('login', handleOpenLogin);
|
||||||
|
emitter.on('join', handleOpenJoin);
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
emitter.off('401', handleOpenAuth);
|
emitter.off('401', handleOpenAuth);
|
||||||
|
emitter.off('login', handleOpenLogin);
|
||||||
|
emitter.off('join', handleOpenJoin);
|
||||||
};
|
};
|
||||||
}, [emitter]);
|
}, [emitter]);
|
||||||
|
|
||||||
|
const onOpenChange = (open) => {
|
||||||
|
setIsOpen(open);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={isOpen} onOpenChange={onOpenChange}>
|
<Dialog open={isOpen} onOpenChange={onOpenChange}>
|
||||||
<DialogContent className="sm:max-w-[400px]">
|
<DialogContent className="sm:max-w-[400px]">
|
||||||
|
|||||||
@@ -2,16 +2,28 @@ import { usePage } from '@inertiajs/react';
|
|||||||
|
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Sheet, SheetContent, SheetHeader, SheetTitle } from '@/components/ui/sheet';
|
import { Sheet, SheetContent, SheetHeader, SheetTitle } from '@/components/ui/sheet';
|
||||||
|
import { useMitt } from '@/plugins/MittContext';
|
||||||
import useLocalSettingsStore from '@/stores/localSettingsStore';
|
import useLocalSettingsStore from '@/stores/localSettingsStore';
|
||||||
|
import { Link } from '@inertiajs/react';
|
||||||
|
|
||||||
export default function EditNavSidebar({ isOpen, onClose }) {
|
export default function EditNavSidebar({ isOpen, onClose }) {
|
||||||
const { auth } = usePage().props;
|
const { auth } = usePage().props;
|
||||||
|
|
||||||
|
const emitter = useMitt();
|
||||||
|
|
||||||
const { getSetting, setSetting } = useLocalSettingsStore();
|
const { getSetting, setSetting } = useLocalSettingsStore();
|
||||||
|
|
||||||
|
const openAuth = (isLogin) => {
|
||||||
|
if (isLogin) {
|
||||||
|
emitter.emit('login');
|
||||||
|
} else {
|
||||||
|
emitter.emit('join');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Sheet open={isOpen} onOpenChange={(open) => !open && onClose()}>
|
<Sheet open={isOpen} onOpenChange={(open) => !open && onClose()}>
|
||||||
<SheetContent side="left" className="w-50 overflow-y-auto">
|
<SheetContent side="left" className="w-[220px] overflow-y-auto">
|
||||||
<SheetHeader>
|
<SheetHeader>
|
||||||
<SheetTitle className="flex items-center gap-3">
|
<SheetTitle className="flex items-center gap-3">
|
||||||
<div className="font-display ml-0 text-lg tracking-wide md:ml-3 md:text-xl">MEMEAIGEN</div>
|
<div className="font-display ml-0 text-lg tracking-wide md:ml-3 md:text-xl">MEMEAIGEN</div>
|
||||||
@@ -20,19 +32,42 @@ export default function EditNavSidebar({ isOpen, onClose }) {
|
|||||||
|
|
||||||
<div className="space-y-3">
|
<div className="space-y-3">
|
||||||
<div className="grid px-2">
|
<div className="grid px-2">
|
||||||
{/* {!auth.user && <Button variant="outline">Join Now</Button>}
|
{!auth.user && (
|
||||||
{!auth.user && <Button variant="link">Login</Button>} */}
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
openAuth(false);
|
||||||
|
}}
|
||||||
|
variant="outline"
|
||||||
|
>
|
||||||
|
Sign Up
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
{!auth.user && (
|
||||||
|
<Button
|
||||||
|
onClick={() => {
|
||||||
|
openAuth(true);
|
||||||
|
}}
|
||||||
|
variant="link"
|
||||||
|
>
|
||||||
|
Login
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="grid px-2">
|
<div className="space-y-3 px-2">
|
||||||
<Button
|
<Link className="text-primary block w-full text-sm underline-offset-4 hover:underline" href={route('home')} as="button">
|
||||||
onClick={() => {
|
|
||||||
window.location.href = route('home');
|
|
||||||
}}
|
|
||||||
variant="link"
|
|
||||||
>
|
|
||||||
Home
|
Home
|
||||||
</Button>
|
</Link>
|
||||||
|
{auth.user && (
|
||||||
|
<Link
|
||||||
|
className="text-primary block w-full text-sm underline-offset-4 hover:underline"
|
||||||
|
method="post"
|
||||||
|
href={route('logout')}
|
||||||
|
as="button"
|
||||||
|
>
|
||||||
|
Log out
|
||||||
|
</Link>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</SheetContent>
|
</SheetContent>
|
||||||
|
|||||||
23
resources/js/modules/flash/flash-messages.jsx
Normal file
23
resources/js/modules/flash/flash-messages.jsx
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
import { usePage } from '@inertiajs/react';
|
||||||
|
import { useEffect } from 'react';
|
||||||
|
import { toast } from 'sonner';
|
||||||
|
|
||||||
|
const FlashMessages = () => {
|
||||||
|
const { flash } = usePage().props;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (flash.message) {
|
||||||
|
toast.success(flash.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flash.error) {
|
||||||
|
toast.error(flash.error);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (flash.success) {
|
||||||
|
toast.success(flash.success);
|
||||||
|
}
|
||||||
|
}, [flash]);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default FlashMessages;
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
import Editor from '@/modules/editor/editor.jsx';
|
import Editor from '@/modules/editor/editor.jsx';
|
||||||
|
import FlashMessages from '@/modules/flash/flash-messages';
|
||||||
|
|
||||||
const Home = () => {
|
const Home = () => {
|
||||||
return (
|
return (
|
||||||
<div className="min-h-screen bg-neutral-50 dark:bg-neutral-800">
|
<div className="min-h-screen bg-neutral-50 dark:bg-neutral-800">
|
||||||
<Editor />
|
<Editor />
|
||||||
|
<FlashMessages />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ export default function Welcome() {
|
|||||||
<nav className="flex items-center justify-end gap-4">
|
<nav className="flex items-center justify-end gap-4">
|
||||||
{auth.user ? (
|
{auth.user ? (
|
||||||
<Link
|
<Link
|
||||||
href={route('dashboard')}
|
href={route(config('platform.general.authed_route_redirect'))}
|
||||||
className="inline-block rounded-sm border border-[#19140035] px-5 py-1.5 text-sm leading-normal text-[#1b1b18] hover:border-[#1915014a] dark:border-[#3E3E3A] dark:text-[#EDEDEC] dark:hover:border-[#62605b]"
|
className="inline-block rounded-sm border border-[#19140035] px-5 py-1.5 text-sm leading-normal text-[#1b1b18] hover:border-[#1915014a] dark:border-[#3E3E3A] dark:text-[#EDEDEC] dark:hover:border-[#62605b]"
|
||||||
>
|
>
|
||||||
Dashboard
|
Dashboard
|
||||||
|
|||||||
@@ -19,7 +19,7 @@
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
$this->assertAuthenticated();
|
$this->assertAuthenticated();
|
||||||
$response->assertRedirect(route('dashboard', absolute: false));
|
$response->assertRedirect(route(config('platform.general.authed_route_redirect'), absolute: false));
|
||||||
});
|
});
|
||||||
|
|
||||||
test('users can not authenticate with invalid password', function () {
|
test('users can not authenticate with invalid password', function () {
|
||||||
|
|||||||
@@ -30,7 +30,7 @@
|
|||||||
|
|
||||||
Event::assertDispatched(Verified::class);
|
Event::assertDispatched(Verified::class);
|
||||||
expect($user->fresh()->hasVerifiedEmail())->toBeTrue();
|
expect($user->fresh()->hasVerifiedEmail())->toBeTrue();
|
||||||
$response->assertRedirect(route('dashboard', absolute: false).'?verified=1');
|
$response->assertRedirect(route(config('platform.general.authed_route_redirect'), absolute: false) . '?verified=1');
|
||||||
});
|
});
|
||||||
|
|
||||||
test('email is not verified with invalid hash', function () {
|
test('email is not verified with invalid hash', function () {
|
||||||
|
|||||||
@@ -17,5 +17,5 @@
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
$this->assertAuthenticated();
|
$this->assertAuthenticated();
|
||||||
$response->assertRedirect(route('dashboard', absolute: false));
|
$response->assertRedirect(route(config('platform.general.authed_route_redirect'), absolute: false));
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user