feat: Implement rides management with CRUD functionality

- Added rides index view with search and filter options.
- Created rides show view to display ride details.
- Implemented API routes for rides.
- Developed authentication routes for user registration, login, and email verification.
- Created tests for authentication, email verification, password reset, and user profile management.
- Added feature tests for rides and operators, including creation, updating, deletion, and searching.
- Implemented soft deletes and caching for rides and operators.
- Enhanced manufacturer and operator model tests for various functionalities.
This commit is contained in:
pacnpal
2025-06-19 22:34:10 -04:00
parent 86263db9d9
commit cc33781245
148 changed files with 14026 additions and 2482 deletions

View File

@@ -3,18 +3,18 @@
@if ($message)
<div @class([
'p-4 mb-4 rounded-lg',
'bg-green-100 dark:bg-green-800 text-green-700 dark:text-green-200' => !str_contains($message, 'error'),
'bg-red-100 dark:bg-red-800 text-red-700 dark:text-red-200' => str_contains($message, 'error'),
'bg-green-100 text-green-700' => !str_contains($message, 'error'),
'bg-red-100 text-red-700' => str_contains($message, 'error'),
])>
{{ $message }}
</div>
@endif
{{-- Controls --}}
<div class="bg-white dark:bg-gray-800 rounded-lg shadow">
<div class="bg-white rounded-lg shadow">
<div class="p-4 space-y-4">
{{-- Status Tabs --}}
<div class="border-b border-gray-200 dark:border-gray-700">
<div class="border-b border-gray-200">
<nav class="-mb-px flex space-x-8">
<button
wire:click="filterByStatus('{{ ReviewStatus::PENDING->value }}')"
@@ -60,14 +60,14 @@
<label for="search" class="sr-only">Search reviews</label>
<div class="relative">
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<span class="text-gray-500 dark:text-gray-400">
<span class="text-gray-500">
🔍
</span>
</div>
<input
type="search"
wire:model.live.debounce.300ms="search"
class="block w-full pl-10 pr-3 py-2 border border-gray-300 dark:border-gray-600 rounded-md leading-5 bg-white dark:bg-gray-700 placeholder-gray-500 focus:outline-none focus:placeholder-gray-400 focus:ring-1 focus:ring-primary-500 focus:border-primary-500 sm:text-sm"
class="block w-full pl-10 pr-3 py-2 border border-gray-300 rounded-md leading-5 bg-white placeholder-gray-500 focus:outline-none focus:placeholder-gray-400 focus:ring-1 focus:ring-primary-500 focus:border-primary-500 sm:text-sm"
placeholder="Search reviews..."
>
</div>
@@ -75,7 +75,7 @@
@if (count($selected) > 0)
<div class="flex items-center space-x-3">
<span class="text-sm text-gray-700 dark:text-gray-300">
<span class="text-sm text-gray-700">
{{ count($selected) }} selected
</span>
<button
@@ -97,8 +97,8 @@
</div>
{{-- Reviews List --}}
<div class="bg-white dark:bg-gray-800 shadow rounded-lg">
<ul role="list" class="divide-y divide-gray-200 dark:divide-gray-700">
<div class="bg-white shadow rounded-lg">
<ul role="list" class="divide-y divide-gray-200">
@forelse ($reviews as $review)
<li class="p-4">
<div class="flex items-start space-x-4">
@@ -115,7 +115,7 @@
{{-- Content --}}
<div class="flex-1 min-w-0">
<div class="flex items-center justify-between">
<div class="text-sm font-medium text-gray-900 dark:text-gray-100">
<div class="text-sm font-medium text-gray-900">
{{ $review->user->name }}
</div>
<div class="text-sm text-gray-500">
@@ -130,12 +130,12 @@
@endfor
</div>
@if ($review->title)
<span class="text-gray-900 dark:text-gray-100 font-medium">
<span class="text-gray-900 font-medium">
{{ $review->title }}
</span>
@endif
</div>
<p class="mt-1 text-gray-600 dark:text-gray-400">
<p class="mt-1 text-gray-600">
{{ $review->content }}
</p>
<div class="mt-2 text-sm text-gray-500">
@@ -148,7 +148,7 @@
<div class="flex-shrink-0 flex items-center space-x-2">
<button
wire:click="editReview({{ $review->id }})"
class="inline-flex items-center p-2 border border-gray-300 dark:border-gray-600 rounded-md shadow-sm text-sm font-medium text-gray-700 dark:text-gray-300 bg-white dark:bg-gray-700 hover:bg-gray-50 dark:hover:bg-gray-600 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500"
class="inline-flex items-center p-2 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500"
>
✏️
</button>
@@ -168,14 +168,14 @@
</div>
</li>
@empty
<li class="p-4 text-center text-gray-500 dark:text-gray-400">
<li class="p-4 text-center text-gray-500">
No reviews found.
</li>
@endforelse
</ul>
{{-- Pagination --}}
<div class="p-4 border-t border-gray-200 dark:border-gray-700">
<div class="p-4 border-t border-gray-200">
{{ $reviews->links() }}
</div>
</div>
@@ -196,13 +196,13 @@
></div>
{{-- Modal panel --}}
<div class="inline-block align-bottom bg-white dark:bg-gray-800 rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full">
<div class="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-lg sm:w-full">
<form wire:submit="saveEdit">
<div class="bg-white dark:bg-gray-800 px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
<div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
<div class="space-y-4">
{{-- Rating --}}
<div>
<label class="block text-sm font-medium text-gray-700 dark:text-gray-300">
<label class="block text-sm font-medium text-gray-700">
Rating
</label>
<div class="mt-1 flex items-center space-x-2">
@@ -214,50 +214,50 @@
>
<span @class([
'text-yellow-400' => $i <= $form['rating'],
'text-gray-300 dark:text-gray-600' => $i > $form['rating'],
'text-gray-300' => $i > $form['rating'],
])></span>
</button>
@endfor
</div>
@error('form.rating')
<p class="mt-1 text-sm text-red-600 dark:text-red-400">{{ $message }}</p>
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
@enderror
</div>
{{-- Title --}}
<div>
<label for="title" class="block text-sm font-medium text-gray-700 dark:text-gray-300">
<label for="title" class="block text-sm font-medium text-gray-700">
Title
</label>
<input
type="text"
wire:model="form.title"
class="mt-1 block w-full rounded-md border-gray-300 dark:border-gray-600 shadow-sm focus:border-primary-500 focus:ring-primary-500 sm:text-sm dark:bg-gray-700"
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-primary-500 focus:ring-primary-500 sm:text-sm"
>
@error('form.title')
<p class="mt-1 text-sm text-red-600 dark:text-red-400">{{ $message }}</p>
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
@enderror
</div>
{{-- Content --}}
<div>
<label for="content" class="block text-sm font-medium text-gray-700 dark:text-gray-300">
<label for="content" class="block text-sm font-medium text-gray-700">
Content
</label>
<textarea
wire:model="form.content"
rows="4"
class="mt-1 block w-full rounded-md border-gray-300 dark:border-gray-600 shadow-sm focus:border-primary-500 focus:ring-primary-500 sm:text-sm dark:bg-gray-700"
class="mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-primary-500 focus:ring-primary-500 sm:text-sm"
></textarea>
@error('form.content')
<p class="mt-1 text-sm text-red-600 dark:text-red-400">{{ $message }}</p>
<p class="mt-1 text-sm text-red-600">{{ $message }}</p>
@enderror
</div>
</div>
</div>
{{-- Modal footer --}}
<div class="bg-gray-50 dark:bg-gray-700 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
<div class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse">
<button
type="submit"
class="w-full inline-flex justify-center rounded-md border border-transparent shadow-sm px-4 py-2 bg-primary-600 text-base font-medium text-white hover:bg-primary-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500 sm:ml-3 sm:w-auto sm:text-sm"
@@ -267,7 +267,7 @@
<button
type="button"
wire:click="$set('showEditModal', false)"
class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 dark:border-gray-600 shadow-sm px-4 py-2 bg-white dark:bg-gray-800 text-base font-medium text-gray-700 dark:text-gray-300 hover:bg-gray-50 dark:hover:bg-gray-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm"
class="mt-3 w-full inline-flex justify-center rounded-md border border-gray-300 shadow-sm px-4 py-2 bg-white text-base font-medium text-gray-700 hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-primary-500 sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm"
>
Cancel
</button>