diff --git a/app/Console/Commands/GenerateUserSlugs.php b/app/Console/Commands/GenerateUserSlugs.php new file mode 100644 index 0000000..63c4c10 --- /dev/null +++ b/app/Console/Commands/GenerateUserSlugs.php @@ -0,0 +1,53 @@ +info('Generating slugs for users...'); + + $usersWithoutSlugs = User::whereNull('slug')->get(); + + if ($usersWithoutSlugs->isEmpty()) { + $this->info('All users already have slugs!'); + return Command::SUCCESS; + } + + $bar = $this->output->createProgressBar($usersWithoutSlugs->count()); + $bar->start(); + + foreach ($usersWithoutSlugs as $user) { + $user->slug = User::generateUniqueSlug($user->name); + $user->save(); + $bar->advance(); + } + + $bar->finish(); + $this->newLine(); + $this->info("Successfully generated slugs for {$usersWithoutSlugs->count()} users!"); + + return Command::SUCCESS; + } +} diff --git a/app/Http/Controllers/Auth/RegisterController.php b/app/Http/Controllers/Auth/RegisterController.php index 961ea36..a08bdfe 100644 --- a/app/Http/Controllers/Auth/RegisterController.php +++ b/app/Http/Controllers/Auth/RegisterController.php @@ -67,6 +67,7 @@ class RegisterController extends Controller 'name' => $data['name'], 'email' => $data['email'], 'password' => Hash::make($data['password']), + 'slug' => User::generateUniqueSlug($data['name']), ]); } } diff --git a/app/Http/Controllers/BookmarkController.php b/app/Http/Controllers/BookmarkController.php index 6c09f01..b9e5fb7 100644 --- a/app/Http/Controllers/BookmarkController.php +++ b/app/Http/Controllers/BookmarkController.php @@ -10,6 +10,24 @@ use Illuminate\Support\Facades\Auth; class BookmarkController extends Controller { + /** + * Display the user's bookmarked products. + */ + public function index() + { + $user = Auth::user(); + + $bookmarks = $user->bookmarks() + ->with(['product.category', 'product.user']) + ->whereHas('product', function ($query) { + $query->approved(); + }) + ->latest() + ->paginate(12); + + return view('bookmarks.index', compact('bookmarks')); + } + /** * Toggle bookmark for a product. */ diff --git a/app/Http/Controllers/ProfileController.php b/app/Http/Controllers/ProfileController.php index 67d1fa0..f630335 100644 --- a/app/Http/Controllers/ProfileController.php +++ b/app/Http/Controllers/ProfileController.php @@ -73,6 +73,11 @@ class ProfileController extends Controller $validated['avatar'] = $avatarName; } + // Regenerate slug if name changed + if ($request->name !== $user->name) { + $validated['slug'] = User::generateUniqueSlug($request->name); + } + // Update user profile $user->update($validated); diff --git a/app/Http/Controllers/UpvoteController.php b/app/Http/Controllers/UpvoteController.php index 57aefcd..b39d099 100644 --- a/app/Http/Controllers/UpvoteController.php +++ b/app/Http/Controllers/UpvoteController.php @@ -10,6 +10,24 @@ use Illuminate\Support\Facades\Auth; class UpvoteController extends Controller { + /** + * Display the user's upvoted products. + */ + public function index() + { + $user = Auth::user(); + + $upvotes = $user->upvotes() + ->with(['product.category', 'product.user']) + ->whereHas('product', function ($query) { + $query->approved(); + }) + ->latest() + ->paginate(12); + + return view('upvotes.index', compact('upvotes')); + } + /** * Toggle upvote for a product. */ diff --git a/app/Models/User.php b/app/Models/User.php index 8774939..b0418cb 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -8,6 +8,7 @@ use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Database\Eloquent\Relations\HasMany; use Illuminate\Foundation\Auth\User as Authenticatable; use Illuminate\Notifications\Notifiable; +use Illuminate\Support\Str; class User extends Authenticatable { @@ -23,6 +24,7 @@ class User extends Authenticatable 'name', 'email', 'password', + 'slug', 'avatar', 'bio', 'website', @@ -144,4 +146,30 @@ class User extends Authenticatable { return $this->hasMany(Bookmark::class); } + + /** + * Generate a unique slug for the user based on their name. + */ + public static function generateUniqueSlug(string $name): string + { + $slug = Str::slug($name); + $originalSlug = $slug; + $count = 1; + + // Keep appending numbers until we find a unique slug + while (static::where('slug', $slug)->exists()) { + $slug = $originalSlug . '-' . $count; + $count++; + } + + return $slug; + } + + /** + * Get the route key for the model. + */ + public function getRouteKeyName(): string + { + return 'slug'; + } } diff --git a/database/migrations/2026_02_14_141358_add_slug_to_users_table.php b/database/migrations/2026_02_14_141358_add_slug_to_users_table.php new file mode 100644 index 0000000..5175667 --- /dev/null +++ b/database/migrations/2026_02_14_141358_add_slug_to_users_table.php @@ -0,0 +1,28 @@ +string('slug')->unique()->after('name')->nullable(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::table('users', function (Blueprint $table) { + $table->dropColumn('slug'); + }); + } +}; diff --git a/resources/css/app.css b/resources/css/app.css index e37b815..e27df6a 100644 --- a/resources/css/app.css +++ b/resources/css/app.css @@ -1,7 +1,16 @@ @import "tailwindcss"; +/* Dark mode configuration */ +@media (prefers-color-scheme: dark) { + :root { + color-scheme: dark; + } +} + /* Custom Styles */ @theme { + /* Enable class-based dark mode */ + --color-scheme: class; --color-primary-50: #f0f9ff; --color-primary-100: #e0f2fe; --color-primary-200: #bae6fd; diff --git a/resources/views/bookmarks/index.blade.php b/resources/views/bookmarks/index.blade.php new file mode 100644 index 0000000..a77d36f --- /dev/null +++ b/resources/views/bookmarks/index.blade.php @@ -0,0 +1,60 @@ +@extends('layouts.app') + +@section('content') +
+ Products you've saved for later +
++ You have {{ $bookmarks->total() }} bookmarked {{ Str::plural('product', $bookmarks->total()) }} +
++ Start bookmarking products to save them for later! +
+ + + Browse Products + +