diff --git a/app/Http/Controllers/Auth/AuthenticatedSessionController.php b/app/Http/Controllers/Auth/AuthenticatedSessionController.php
new file mode 100644
index 0000000..d44fe97
--- /dev/null
+++ b/app/Http/Controllers/Auth/AuthenticatedSessionController.php
@@ -0,0 +1,52 @@
+ Route::has('password.request'),
+ 'status' => session('status'),
+ ]);
+ }
+
+ /**
+ * Handle an incoming authentication request.
+ */
+ public function store(LoginRequest $request): RedirectResponse
+ {
+ $request->authenticate();
+
+ $request->session()->regenerate();
+
+ return redirect()->intended(route('dashboard', absolute: false));
+ }
+
+ /**
+ * Destroy an authenticated session.
+ */
+ public function destroy(Request $request): RedirectResponse
+ {
+ Auth::guard('web')->logout();
+
+ $request->session()->invalidate();
+
+ $request->session()->regenerateToken();
+
+ return redirect('/');
+ }
+}
diff --git a/app/Http/Controllers/Auth/ConfirmablePasswordController.php b/app/Http/Controllers/Auth/ConfirmablePasswordController.php
new file mode 100644
index 0000000..d2b1f14
--- /dev/null
+++ b/app/Http/Controllers/Auth/ConfirmablePasswordController.php
@@ -0,0 +1,41 @@
+validate([
+ 'email' => $request->user()->email,
+ 'password' => $request->password,
+ ])) {
+ throw ValidationException::withMessages([
+ 'password' => __('auth.password'),
+ ]);
+ }
+
+ $request->session()->put('auth.password_confirmed_at', time());
+
+ return redirect()->intended(route('dashboard', absolute: false));
+ }
+}
diff --git a/app/Http/Controllers/Auth/EmailVerificationNotificationController.php b/app/Http/Controllers/Auth/EmailVerificationNotificationController.php
new file mode 100644
index 0000000..f64fa9b
--- /dev/null
+++ b/app/Http/Controllers/Auth/EmailVerificationNotificationController.php
@@ -0,0 +1,24 @@
+user()->hasVerifiedEmail()) {
+ return redirect()->intended(route('dashboard', absolute: false));
+ }
+
+ $request->user()->sendEmailVerificationNotification();
+
+ return back()->with('status', 'verification-link-sent');
+ }
+}
diff --git a/app/Http/Controllers/Auth/EmailVerificationPromptController.php b/app/Http/Controllers/Auth/EmailVerificationPromptController.php
new file mode 100644
index 0000000..b42e0d5
--- /dev/null
+++ b/app/Http/Controllers/Auth/EmailVerificationPromptController.php
@@ -0,0 +1,22 @@
+user()->hasVerifiedEmail()
+ ? redirect()->intended(route('dashboard', absolute: false))
+ : Inertia::render('Auth/VerifyEmail', ['status' => session('status')]);
+ }
+}
diff --git a/app/Http/Controllers/Auth/NewPasswordController.php b/app/Http/Controllers/Auth/NewPasswordController.php
new file mode 100644
index 0000000..394cc4a
--- /dev/null
+++ b/app/Http/Controllers/Auth/NewPasswordController.php
@@ -0,0 +1,69 @@
+ $request->email,
+ 'token' => $request->route('token'),
+ ]);
+ }
+
+ /**
+ * Handle an incoming new password request.
+ *
+ * @throws \Illuminate\Validation\ValidationException
+ */
+ public function store(Request $request): RedirectResponse
+ {
+ $request->validate([
+ 'token' => 'required',
+ 'email' => 'required|email',
+ 'password' => ['required', 'confirmed', Rules\Password::defaults()],
+ ]);
+
+ // Here we will attempt to reset the user's password. If it is successful we
+ // will update the password on an actual user model and persist it to the
+ // database. Otherwise we will parse the error and return the response.
+ $status = Password::reset(
+ $request->only('email', 'password', 'password_confirmation', 'token'),
+ function ($user) use ($request) {
+ $user->forceFill([
+ 'password' => Hash::make($request->password),
+ 'remember_token' => Str::random(60),
+ ])->save();
+
+ event(new PasswordReset($user));
+ }
+ );
+
+ // If the password was successfully reset, we will redirect the user back to
+ // the application's home authenticated view. If there is an error we can
+ // redirect them back to where they came from with their error message.
+ if ($status == Password::PASSWORD_RESET) {
+ return redirect()->route('login')->with('status', __($status));
+ }
+
+ throw ValidationException::withMessages([
+ 'email' => [trans($status)],
+ ]);
+ }
+}
diff --git a/app/Http/Controllers/Auth/PasswordController.php b/app/Http/Controllers/Auth/PasswordController.php
new file mode 100644
index 0000000..57a82b5
--- /dev/null
+++ b/app/Http/Controllers/Auth/PasswordController.php
@@ -0,0 +1,29 @@
+validate([
+ 'current_password' => ['required', 'current_password'],
+ 'password' => ['required', Password::defaults(), 'confirmed'],
+ ]);
+
+ $request->user()->update([
+ 'password' => Hash::make($validated['password']),
+ ]);
+
+ return back();
+ }
+}
diff --git a/app/Http/Controllers/Auth/PasswordResetLinkController.php b/app/Http/Controllers/Auth/PasswordResetLinkController.php
new file mode 100644
index 0000000..b22c544
--- /dev/null
+++ b/app/Http/Controllers/Auth/PasswordResetLinkController.php
@@ -0,0 +1,51 @@
+ session('status'),
+ ]);
+ }
+
+ /**
+ * Handle an incoming password reset link request.
+ *
+ * @throws \Illuminate\Validation\ValidationException
+ */
+ public function store(Request $request): RedirectResponse
+ {
+ $request->validate([
+ 'email' => 'required|email',
+ ]);
+
+ // We will send the password reset link to this user. Once we have attempted
+ // to send the link, we will examine the response then see the message we
+ // need to show to the user. Finally, we'll send out a proper response.
+ $status = Password::sendResetLink(
+ $request->only('email')
+ );
+
+ if ($status == Password::RESET_LINK_SENT) {
+ return back()->with('status', __($status));
+ }
+
+ throw ValidationException::withMessages([
+ 'email' => [trans($status)],
+ ]);
+ }
+}
diff --git a/app/Http/Controllers/Auth/RegisteredUserController.php b/app/Http/Controllers/Auth/RegisteredUserController.php
new file mode 100644
index 0000000..53a546b
--- /dev/null
+++ b/app/Http/Controllers/Auth/RegisteredUserController.php
@@ -0,0 +1,51 @@
+validate([
+ 'name' => 'required|string|max:255',
+ 'email' => 'required|string|lowercase|email|max:255|unique:'.User::class,
+ 'password' => ['required', 'confirmed', Rules\Password::defaults()],
+ ]);
+
+ $user = User::create([
+ 'name' => $request->name,
+ 'email' => $request->email,
+ 'password' => Hash::make($request->password),
+ ]);
+
+ event(new Registered($user));
+
+ Auth::login($user);
+
+ return redirect(route('dashboard', absolute: false));
+ }
+}
diff --git a/app/Http/Controllers/Auth/VerifyEmailController.php b/app/Http/Controllers/Auth/VerifyEmailController.php
new file mode 100644
index 0000000..784765e
--- /dev/null
+++ b/app/Http/Controllers/Auth/VerifyEmailController.php
@@ -0,0 +1,27 @@
+user()->hasVerifiedEmail()) {
+ return redirect()->intended(route('dashboard', absolute: false).'?verified=1');
+ }
+
+ if ($request->user()->markEmailAsVerified()) {
+ event(new Verified($request->user()));
+ }
+
+ return redirect()->intended(route('dashboard', absolute: false).'?verified=1');
+ }
+}
diff --git a/app/Http/Controllers/ProfileController.php b/app/Http/Controllers/ProfileController.php
new file mode 100644
index 0000000..873b4f7
--- /dev/null
+++ b/app/Http/Controllers/ProfileController.php
@@ -0,0 +1,63 @@
+ $request->user() instanceof MustVerifyEmail,
+ 'status' => session('status'),
+ ]);
+ }
+
+ /**
+ * Update the user's profile information.
+ */
+ public function update(ProfileUpdateRequest $request): RedirectResponse
+ {
+ $request->user()->fill($request->validated());
+
+ if ($request->user()->isDirty('email')) {
+ $request->user()->email_verified_at = null;
+ }
+
+ $request->user()->save();
+
+ return Redirect::route('profile.edit');
+ }
+
+ /**
+ * Delete the user's account.
+ */
+ public function destroy(Request $request): RedirectResponse
+ {
+ $request->validate([
+ 'password' => ['required', 'current_password'],
+ ]);
+
+ $user = $request->user();
+
+ Auth::logout();
+
+ $user->delete();
+
+ $request->session()->invalidate();
+ $request->session()->regenerateToken();
+
+ return Redirect::to('/');
+ }
+}
diff --git a/app/Http/Middleware/HandleInertiaRequests.php b/app/Http/Middleware/HandleInertiaRequests.php
new file mode 100644
index 0000000..3867f22
--- /dev/null
+++ b/app/Http/Middleware/HandleInertiaRequests.php
@@ -0,0 +1,39 @@
+
+ */
+ public function share(Request $request): array
+ {
+ return [
+ ...parent::share($request),
+ 'auth' => [
+ 'user' => $request->user(),
+ ],
+ ];
+ }
+}
diff --git a/app/Http/Requests/Auth/LoginRequest.php b/app/Http/Requests/Auth/LoginRequest.php
new file mode 100644
index 0000000..2574642
--- /dev/null
+++ b/app/Http/Requests/Auth/LoginRequest.php
@@ -0,0 +1,85 @@
+|string>
+ */
+ public function rules(): array
+ {
+ return [
+ 'email' => ['required', 'string', 'email'],
+ 'password' => ['required', 'string'],
+ ];
+ }
+
+ /**
+ * Attempt to authenticate the request's credentials.
+ *
+ * @throws \Illuminate\Validation\ValidationException
+ */
+ public function authenticate(): void
+ {
+ $this->ensureIsNotRateLimited();
+
+ if (! Auth::attempt($this->only('email', 'password'), $this->boolean('remember'))) {
+ RateLimiter::hit($this->throttleKey());
+
+ throw ValidationException::withMessages([
+ 'email' => trans('auth.failed'),
+ ]);
+ }
+
+ RateLimiter::clear($this->throttleKey());
+ }
+
+ /**
+ * Ensure the login request is not rate limited.
+ *
+ * @throws \Illuminate\Validation\ValidationException
+ */
+ public function ensureIsNotRateLimited(): void
+ {
+ if (! RateLimiter::tooManyAttempts($this->throttleKey(), 5)) {
+ return;
+ }
+
+ event(new Lockout($this));
+
+ $seconds = RateLimiter::availableIn($this->throttleKey());
+
+ throw ValidationException::withMessages([
+ 'email' => trans('auth.throttle', [
+ 'seconds' => $seconds,
+ 'minutes' => ceil($seconds / 60),
+ ]),
+ ]);
+ }
+
+ /**
+ * Get the rate limiting throttle key for the request.
+ */
+ public function throttleKey(): string
+ {
+ return Str::transliterate(Str::lower($this->string('email')).'|'.$this->ip());
+ }
+}
diff --git a/app/Http/Requests/ProfileUpdateRequest.php b/app/Http/Requests/ProfileUpdateRequest.php
new file mode 100644
index 0000000..3622a8f
--- /dev/null
+++ b/app/Http/Requests/ProfileUpdateRequest.php
@@ -0,0 +1,30 @@
+|string>
+ */
+ public function rules(): array
+ {
+ return [
+ 'name' => ['required', 'string', 'max:255'],
+ 'email' => [
+ 'required',
+ 'string',
+ 'lowercase',
+ 'email',
+ 'max:255',
+ Rule::unique(User::class)->ignore($this->user()->id),
+ ],
+ ];
+ }
+}
diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php
index 452e6b6..96e9f6c 100644
--- a/app/Providers/AppServiceProvider.php
+++ b/app/Providers/AppServiceProvider.php
@@ -2,6 +2,7 @@
namespace App\Providers;
+use Illuminate\Support\Facades\Vite;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
@@ -19,6 +20,6 @@ class AppServiceProvider extends ServiceProvider
*/
public function boot(): void
{
- //
+ Vite::prefetch(concurrency: 3);
}
}
diff --git a/bootstrap/app.php b/bootstrap/app.php
index c183276..5c02a59 100644
--- a/bootstrap/app.php
+++ b/bootstrap/app.php
@@ -11,6 +11,11 @@ return Application::configure(basePath: dirname(__DIR__))
health: '/up',
)
->withMiddleware(function (Middleware $middleware): void {
+ $middleware->web(append: [
+ \App\Http\Middleware\HandleInertiaRequests::class,
+ \Illuminate\Http\Middleware\AddLinkHeadersForPreloadedAssets::class,
+ ]);
+
//
})
->withExceptions(function (Exceptions $exceptions): void {
diff --git a/composer.json b/composer.json
index 043298e..3398c73 100644
--- a/composer.json
+++ b/composer.json
@@ -8,11 +8,15 @@
"require": {
"php": "^8.2",
"filament/filament": "^4.0",
+ "inertiajs/inertia-laravel": "^2.0",
"laravel/framework": "^12.0",
- "laravel/tinker": "^2.10.1"
+ "laravel/sanctum": "^4.0",
+ "laravel/tinker": "^2.10.1",
+ "tightenco/ziggy": "^2.0"
},
"require-dev": {
"fakerphp/faker": "^1.23",
+ "laravel/breeze": "^2.3",
"laravel/pail": "^1.2.2",
"laravel/pint": "^1.24",
"laravel/sail": "^1.41",
diff --git a/composer.lock b/composer.lock
index a8933bc..bf47c37 100644
--- a/composer.lock
+++ b/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "d06fce5a31d9da3c576b967652ed06de",
+ "content-hash": "4c4fa1ad6ac32d349ed330af73603676",
"packages": [
{
"name": "anourvalar/eloquent-serialize",
@@ -1973,6 +1973,76 @@
],
"time": "2025-08-22T14:27:06+00:00"
},
+ {
+ "name": "inertiajs/inertia-laravel",
+ "version": "v2.0.8",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/inertiajs/inertia-laravel.git",
+ "reference": "e2ab960c6e02cb374f0ec89b5681df144bb256e9"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/inertiajs/inertia-laravel/zipball/e2ab960c6e02cb374f0ec89b5681df144bb256e9",
+ "reference": "e2ab960c6e02cb374f0ec89b5681df144bb256e9",
+ "shasum": ""
+ },
+ "require": {
+ "ext-json": "*",
+ "laravel/framework": "^10.0|^11.0|^12.0",
+ "php": "^8.1.0",
+ "symfony/console": "^6.2|^7.0"
+ },
+ "require-dev": {
+ "guzzlehttp/guzzle": "^7.2",
+ "larastan/larastan": "^3.0",
+ "laravel/pint": "^1.16",
+ "mockery/mockery": "^1.3.3",
+ "orchestra/testbench": "^8.0|^9.2|^10.0",
+ "phpunit/phpunit": "^10.4|^11.5",
+ "roave/security-advisories": "dev-master"
+ },
+ "suggest": {
+ "ext-pcntl": "Recommended when running the Inertia SSR server via the `inertia:start-ssr` artisan command."
+ },
+ "type": "library",
+ "extra": {
+ "laravel": {
+ "providers": [
+ "Inertia\\ServiceProvider"
+ ]
+ }
+ },
+ "autoload": {
+ "files": [
+ "./helpers.php"
+ ],
+ "psr-4": {
+ "Inertia\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Jonathan Reinink",
+ "email": "jonathan@reinink.ca",
+ "homepage": "https://reinink.ca"
+ }
+ ],
+ "description": "The Laravel adapter for Inertia.js.",
+ "keywords": [
+ "inertia",
+ "laravel"
+ ],
+ "support": {
+ "issues": "https://github.com/inertiajs/inertia-laravel/issues",
+ "source": "https://github.com/inertiajs/inertia-laravel/tree/v2.0.8"
+ },
+ "time": "2025-09-26T15:12:11+00:00"
+ },
{
"name": "kirschbaum-development/eloquent-power-joins",
"version": "4.2.8",
@@ -2315,6 +2385,70 @@
},
"time": "2025-09-19T13:47:56+00:00"
},
+ {
+ "name": "laravel/sanctum",
+ "version": "v4.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/laravel/sanctum.git",
+ "reference": "fd6df4f79f48a72992e8d29a9c0ee25422a0d677"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/laravel/sanctum/zipball/fd6df4f79f48a72992e8d29a9c0ee25422a0d677",
+ "reference": "fd6df4f79f48a72992e8d29a9c0ee25422a0d677",
+ "shasum": ""
+ },
+ "require": {
+ "ext-json": "*",
+ "illuminate/console": "^11.0|^12.0",
+ "illuminate/contracts": "^11.0|^12.0",
+ "illuminate/database": "^11.0|^12.0",
+ "illuminate/support": "^11.0|^12.0",
+ "php": "^8.2",
+ "symfony/console": "^7.0"
+ },
+ "require-dev": {
+ "mockery/mockery": "^1.6",
+ "orchestra/testbench": "^9.0|^10.0",
+ "phpstan/phpstan": "^1.10",
+ "phpunit/phpunit": "^11.3"
+ },
+ "type": "library",
+ "extra": {
+ "laravel": {
+ "providers": [
+ "Laravel\\Sanctum\\SanctumServiceProvider"
+ ]
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Laravel\\Sanctum\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Taylor Otwell",
+ "email": "taylor@laravel.com"
+ }
+ ],
+ "description": "Laravel Sanctum provides a featherweight authentication system for SPAs and simple APIs.",
+ "keywords": [
+ "auth",
+ "laravel",
+ "sanctum"
+ ],
+ "support": {
+ "issues": "https://github.com/laravel/sanctum/issues",
+ "source": "https://github.com/laravel/sanctum"
+ },
+ "time": "2025-07-09T19:45:24+00:00"
+ },
{
"name": "laravel/serializable-closure",
"version": "v2.0.5",
@@ -7892,6 +8026,76 @@
],
"time": "2025-08-13T11:49:31+00:00"
},
+ {
+ "name": "tightenco/ziggy",
+ "version": "v2.6.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/tighten/ziggy.git",
+ "reference": "cccc6035c109daab03a33926b3a8499bedbed01f"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/tighten/ziggy/zipball/cccc6035c109daab03a33926b3a8499bedbed01f",
+ "reference": "cccc6035c109daab03a33926b3a8499bedbed01f",
+ "shasum": ""
+ },
+ "require": {
+ "ext-json": "*",
+ "laravel/framework": ">=9.0",
+ "php": ">=8.1"
+ },
+ "require-dev": {
+ "laravel/folio": "^1.1",
+ "orchestra/testbench": "^7.0 || ^8.0 || ^9.0 || ^10.0",
+ "pestphp/pest": "^2.26|^3.0",
+ "pestphp/pest-plugin-laravel": "^2.4|^3.0"
+ },
+ "type": "library",
+ "extra": {
+ "laravel": {
+ "providers": [
+ "Tighten\\Ziggy\\ZiggyServiceProvider"
+ ]
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Tighten\\Ziggy\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Daniel Coulbourne",
+ "email": "daniel@tighten.co"
+ },
+ {
+ "name": "Jake Bathman",
+ "email": "jake@tighten.co"
+ },
+ {
+ "name": "Jacob Baker-Kretzmar",
+ "email": "jacob@tighten.co"
+ }
+ ],
+ "description": "Use your Laravel named routes in JavaScript.",
+ "homepage": "https://github.com/tighten/ziggy",
+ "keywords": [
+ "Ziggy",
+ "javascript",
+ "laravel",
+ "routes"
+ ],
+ "support": {
+ "issues": "https://github.com/tighten/ziggy/issues",
+ "source": "https://github.com/tighten/ziggy/tree/v2.6.0"
+ },
+ "time": "2025-09-15T00:00:26+00:00"
+ },
{
"name": "tijsverkoyen/css-to-inline-styles",
"version": "v2.3.0",
@@ -8419,6 +8623,67 @@
},
"time": "2025-04-30T06:54:44+00:00"
},
+ {
+ "name": "laravel/breeze",
+ "version": "v2.3.8",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/laravel/breeze.git",
+ "reference": "1a29c5792818bd4cddf70b5f743a227e02fbcfcd"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/laravel/breeze/zipball/1a29c5792818bd4cddf70b5f743a227e02fbcfcd",
+ "reference": "1a29c5792818bd4cddf70b5f743a227e02fbcfcd",
+ "shasum": ""
+ },
+ "require": {
+ "illuminate/console": "^11.0|^12.0",
+ "illuminate/filesystem": "^11.0|^12.0",
+ "illuminate/support": "^11.0|^12.0",
+ "illuminate/validation": "^11.0|^12.0",
+ "php": "^8.2.0",
+ "symfony/console": "^7.0"
+ },
+ "require-dev": {
+ "laravel/framework": "^11.0|^12.0",
+ "orchestra/testbench-core": "^9.0|^10.0",
+ "phpstan/phpstan": "^2.0"
+ },
+ "type": "library",
+ "extra": {
+ "laravel": {
+ "providers": [
+ "Laravel\\Breeze\\BreezeServiceProvider"
+ ]
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Laravel\\Breeze\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Taylor Otwell",
+ "email": "taylor@laravel.com"
+ }
+ ],
+ "description": "Minimal Laravel authentication scaffolding with Blade and Tailwind.",
+ "keywords": [
+ "auth",
+ "laravel"
+ ],
+ "support": {
+ "issues": "https://github.com/laravel/breeze/issues",
+ "source": "https://github.com/laravel/breeze"
+ },
+ "time": "2025-07-18T18:49:59+00:00"
+ },
{
"name": "laravel/pail",
"version": "v1.2.3",
diff --git a/jsconfig.json b/jsconfig.json
new file mode 100644
index 0000000..6269354
--- /dev/null
+++ b/jsconfig.json
@@ -0,0 +1,10 @@
+{
+ "compilerOptions": {
+ "baseUrl": ".",
+ "paths": {
+ "@/*": ["resources/js/*"],
+ "ziggy-js": ["./vendor/tightenco/ziggy"]
+ }
+ },
+ "exclude": ["node_modules", "public"]
+}
diff --git a/package.json b/package.json
index a5707d8..3f8950f 100644
--- a/package.json
+++ b/package.json
@@ -7,11 +7,19 @@
"dev": "vite"
},
"devDependencies": {
+ "@headlessui/react": "^2.0.0",
+ "@inertiajs/react": "^2.0.0",
+ "@tailwindcss/forms": "^0.5.3",
"@tailwindcss/vite": "^4.0.0",
+ "@vitejs/plugin-react": "^4.2.0",
+ "autoprefixer": "^10.4.12",
"axios": "^1.11.0",
"concurrently": "^9.0.1",
"laravel-vite-plugin": "^2.0.0",
- "tailwindcss": "^4.0.0",
+ "postcss": "^8.4.31",
+ "react": "^18.2.0",
+ "react-dom": "^18.2.0",
+ "tailwindcss": "^3.2.1",
"vite": "^7.0.4"
}
}
diff --git a/postcss.config.js b/postcss.config.js
new file mode 100644
index 0000000..49c0612
--- /dev/null
+++ b/postcss.config.js
@@ -0,0 +1,6 @@
+export default {
+ plugins: {
+ tailwindcss: {},
+ autoprefixer: {},
+ },
+};
diff --git a/resources/css/app.css b/resources/css/app.css
index 3e6abea..b5c61c9 100644
--- a/resources/css/app.css
+++ b/resources/css/app.css
@@ -1,11 +1,3 @@
-@import 'tailwindcss';
-
-@source '../../vendor/laravel/framework/src/Illuminate/Pagination/resources/views/*.blade.php';
-@source '../../storage/framework/views/*.php';
-@source '../**/*.blade.php';
-@source '../**/*.js';
-
-@theme {
- --font-sans: 'Instrument Sans', ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji',
- 'Segoe UI Symbol', 'Noto Color Emoji';
-}
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
diff --git a/resources/js/Components/ApplicationLogo.jsx b/resources/js/Components/ApplicationLogo.jsx
new file mode 100644
index 0000000..160fb62
--- /dev/null
+++ b/resources/js/Components/ApplicationLogo.jsx
@@ -0,0 +1,11 @@
+export default function ApplicationLogo(props) {
+ return (
+
+ );
+}
diff --git a/resources/js/Components/Checkbox.jsx b/resources/js/Components/Checkbox.jsx
new file mode 100644
index 0000000..34a31fe
--- /dev/null
+++ b/resources/js/Components/Checkbox.jsx
@@ -0,0 +1,12 @@
+export default function Checkbox({ className = '', ...props }) {
+ return (
+
+ );
+}
diff --git a/resources/js/Components/DangerButton.jsx b/resources/js/Components/DangerButton.jsx
new file mode 100644
index 0000000..482d6eb
--- /dev/null
+++ b/resources/js/Components/DangerButton.jsx
@@ -0,0 +1,20 @@
+export default function DangerButton({
+ className = '',
+ disabled,
+ children,
+ ...props
+}) {
+ return (
+
+ );
+}
diff --git a/resources/js/Components/Dropdown.jsx b/resources/js/Components/Dropdown.jsx
new file mode 100644
index 0000000..5f037ef
--- /dev/null
+++ b/resources/js/Components/Dropdown.jsx
@@ -0,0 +1,107 @@
+import { Transition } from '@headlessui/react';
+import { Link } from '@inertiajs/react';
+import { createContext, useContext, useState } from 'react';
+
+const DropDownContext = createContext();
+
+const Dropdown = ({ children }) => {
+ const [open, setOpen] = useState(false);
+
+ const toggleOpen = () => {
+ setOpen((previousState) => !previousState);
+ };
+
+ return (
+
+ {message} +
+ ) : null; +} diff --git a/resources/js/Components/InputLabel.jsx b/resources/js/Components/InputLabel.jsx new file mode 100644 index 0000000..9364f9d --- /dev/null +++ b/resources/js/Components/InputLabel.jsx @@ -0,0 +1,18 @@ +export default function InputLabel({ + value, + className = '', + children, + ...props +}) { + return ( + + ); +} diff --git a/resources/js/Components/Modal.jsx b/resources/js/Components/Modal.jsx new file mode 100644 index 0000000..7560cff --- /dev/null +++ b/resources/js/Components/Modal.jsx @@ -0,0 +1,65 @@ +import { + Dialog, + DialogPanel, + Transition, + TransitionChild, +} from '@headlessui/react'; + +export default function Modal({ + children, + show = false, + maxWidth = '2xl', + closeable = true, + onClose = () => {}, +}) { + const close = () => { + if (closeable) { + onClose(); + } + }; + + const maxWidthClass = { + sm: 'sm:max-w-sm', + md: 'sm:max-w-md', + lg: 'sm:max-w-lg', + xl: 'sm:max-w-xl', + '2xl': 'sm:max-w-2xl', + }[maxWidth]; + + return ( ++ Once your account is deleted, all of its resources and data + will be permanently deleted. Before deleting your account, + please download any data or information that you wish to + retain. +
++ Ensure your account is using a long, random password to stay + secure. +
++ Update your account's profile information and email address. +
+Laravel has an incredibly rich ecosystem.
We suggest starting with the following.
+
+ Laravel has wonderful + documentation covering every + aspect of the framework. + Whether you are a newcomer + or have prior experience + with Laravel, we recommend + reading our documentation + from beginning to end. +
++ Laracasts offers thousands of video + tutorials on Laravel, PHP, and + JavaScript development. Check them + out, see for yourself, and massively + level up your development skills in + the process. +
++ Laravel News is a community driven + portal and newsletter aggregating + all of the latest and most important + news in the Laravel ecosystem, + including new package releases and + tutorials. +
++ Laravel's robust library of + first-party tools and libraries, + such as{' '} + + Forge + + ,{' '} + + Vapor + + ,{' '} + + Nova + + ,{' '} + + Envoyer + + , and{' '} + + Herd + {' '} + help you take your projects to the + next level. Pair them with powerful + open source libraries like{' '} + + Cashier + + ,{' '} + + Dusk + + ,{' '} + + Echo + + ,{' '} + + Horizon + + ,{' '} + + Sanctum + + ,{' '} + + Telescope + + , and more. +
+
+ >
+ );
+}
diff --git a/resources/js/app.js b/resources/js/app.js
deleted file mode 100644
index e59d6a0..0000000
--- a/resources/js/app.js
+++ /dev/null
@@ -1 +0,0 @@
-import './bootstrap';
diff --git a/resources/js/app.jsx b/resources/js/app.jsx
new file mode 100644
index 0000000..9f00218
--- /dev/null
+++ b/resources/js/app.jsx
@@ -0,0 +1,25 @@
+import '../css/app.css';
+import './bootstrap';
+
+import { createInertiaApp } from '@inertiajs/react';
+import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers';
+import { createRoot } from 'react-dom/client';
+
+const appName = import.meta.env.VITE_APP_NAME || 'Laravel';
+
+createInertiaApp({
+ title: (title) => `${title} - ${appName}`,
+ resolve: (name) =>
+ resolvePageComponent(
+ `./Pages/${name}.jsx`,
+ import.meta.glob('./Pages/**/*.jsx'),
+ ),
+ setup({ el, App, props }) {
+ const root = createRoot(el);
+
+ root.render(
+ + + +
+ + + + + + + @routes + @viteReactRefresh + @vite(['resources/js/app.jsx', "resources/js/Pages/{$page['component']}.jsx"]) + @inertiaHead + +