feat: establish reservation portal foundation
This commit is contained in:
44
app/Http/Controllers/AssetController.php
Normal file
44
app/Http/Controllers/AssetController.php
Normal file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Domain\Inventory\Models\SnipeAsset;
|
||||
use Illuminate\Http\Request;
|
||||
use Inertia\Inertia;
|
||||
use Inertia\Response;
|
||||
|
||||
class AssetController extends Controller
|
||||
{
|
||||
public function index(Request $request): Response
|
||||
{
|
||||
$filters = $request->validate([
|
||||
'search' => ['nullable', 'string', 'max:100'],
|
||||
'category' => ['nullable', 'string', 'max:100'],
|
||||
'location' => ['nullable', 'string', 'max:100'],
|
||||
]);
|
||||
|
||||
$assets = SnipeAsset::query()
|
||||
->where('active', true)
|
||||
->where('requestable', true)
|
||||
->when($filters['search'] ?? null, function ($query, string $search) {
|
||||
$query->where(function ($query) use ($search) {
|
||||
$query->where('name', 'like', "%{$search}%")
|
||||
->orWhere('asset_tag', 'like', "%{$search}%")
|
||||
->orWhere('model_name', 'like', "%{$search}%");
|
||||
});
|
||||
})
|
||||
->when($filters['category'] ?? null, fn ($query, string $category) => $query->where('category_name', $category))
|
||||
->when($filters['location'] ?? null, fn ($query, string $location) => $query->where('location_name', $location))
|
||||
->orderBy('name')
|
||||
->paginate(12)
|
||||
->withQueryString();
|
||||
|
||||
return Inertia::render('assets/index', [
|
||||
'assets' => $assets,
|
||||
'filters' => $filters,
|
||||
'categories' => SnipeAsset::query()->where('active', true)->whereNotNull('category_name')->distinct()->orderBy('category_name')->pluck('category_name'),
|
||||
'locations' => SnipeAsset::query()->where('active', true)->whereNotNull('location_name')->distinct()->orderBy('location_name')->pluck('location_name'),
|
||||
'lastSyncedAt' => SnipeAsset::max('synced_at'),
|
||||
]);
|
||||
}
|
||||
}
|
||||
8
app/Http/Controllers/Controller.php
Normal file
8
app/Http/Controllers/Controller.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
abstract class Controller
|
||||
{
|
||||
//
|
||||
}
|
||||
37
app/Http/Controllers/DashboardController.php
Normal file
37
app/Http/Controllers/DashboardController.php
Normal file
@@ -0,0 +1,37 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Domain\Reservations\Enums\ReservationStatus;
|
||||
use Illuminate\Http\Request;
|
||||
use Inertia\Inertia;
|
||||
use Inertia\Response;
|
||||
|
||||
class DashboardController extends Controller
|
||||
{
|
||||
public function __invoke(Request $request): Response
|
||||
{
|
||||
$query = $request->user()->reservations();
|
||||
|
||||
$next = (clone $query)
|
||||
->with('items.asset:id,name,asset_tag,category_name')
|
||||
->where('ends_at', '>', now())
|
||||
->whereNotIn('status', [ReservationStatus::Rejected, ReservationStatus::Cancelled, ReservationStatus::Expired])
|
||||
->orderBy('starts_at')
|
||||
->first();
|
||||
|
||||
return Inertia::render('dashboard', [
|
||||
'summary' => [
|
||||
'pending' => (clone $query)->where('status', ReservationStatus::PendingApproval)->count(),
|
||||
'approved' => (clone $query)->whereIn('status', [ReservationStatus::Approved, ReservationStatus::Ready])->count(),
|
||||
'inUse' => (clone $query)->whereIn('status', [ReservationStatus::Delivered, ReservationStatus::InUse, ReservationStatus::Overdue])->count(),
|
||||
],
|
||||
'nextReservation' => $next,
|
||||
'recentReservations' => (clone $query)
|
||||
->with('items.asset:id,name,asset_tag')
|
||||
->latest()
|
||||
->limit(5)
|
||||
->get(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
46
app/Http/Controllers/ReservationController.php
Normal file
46
app/Http/Controllers/ReservationController.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Domain\Inventory\Models\SnipeAsset;
|
||||
use App\Domain\Reservations\Services\ReservationService;
|
||||
use App\Http\Requests\StoreReservationRequest;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Inertia\Inertia;
|
||||
use Inertia\Response;
|
||||
|
||||
class ReservationController extends Controller
|
||||
{
|
||||
public function index(Request $request): Response
|
||||
{
|
||||
return Inertia::render('reservations/index', [
|
||||
'reservations' => $request->user()->reservations()
|
||||
->with('items.asset:id,name,asset_tag')
|
||||
->latest()
|
||||
->paginate(15),
|
||||
]);
|
||||
}
|
||||
|
||||
public function create(Request $request): Response
|
||||
{
|
||||
return Inertia::render('reservations/create', [
|
||||
'assets' => SnipeAsset::query()
|
||||
->where('active', true)
|
||||
->where('requestable', true)
|
||||
->whereNull('assigned_id')
|
||||
->orderBy('name')
|
||||
->get(['id', 'name', 'asset_tag', 'model_name', 'category_name', 'location_name']),
|
||||
'selectedAssetId' => $request->integer('asset'),
|
||||
]);
|
||||
}
|
||||
|
||||
public function store(StoreReservationRequest $request, ReservationService $service): RedirectResponse
|
||||
{
|
||||
/** @var array{purpose:string, usage_location:string, starts_at:string, ends_at:string, asset_ids:list<int>} $data */
|
||||
$data = $request->safe()->only(['purpose', 'usage_location', 'starts_at', 'ends_at', 'asset_ids']);
|
||||
$reservation = $service->createPending($request->user(), $data);
|
||||
|
||||
return to_route('dashboard')->with('success', "Requisição {$reservation->reference} submetida para aprovação.");
|
||||
}
|
||||
}
|
||||
62
app/Http/Controllers/Settings/ProfileController.php
Normal file
62
app/Http/Controllers/Settings/ProfileController.php
Normal file
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Settings;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Settings\ProfileDeleteRequest;
|
||||
use App\Http\Requests\Settings\ProfileUpdateRequest;
|
||||
use Illuminate\Contracts\Auth\MustVerifyEmail;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Inertia\Inertia;
|
||||
use Inertia\Response;
|
||||
|
||||
class ProfileController extends Controller
|
||||
{
|
||||
/**
|
||||
* Show the user's profile settings page.
|
||||
*/
|
||||
public function edit(Request $request): Response
|
||||
{
|
||||
return Inertia::render('settings/profile', [
|
||||
'mustVerifyEmail' => $request->user() instanceof MustVerifyEmail,
|
||||
'status' => $request->session()->get('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();
|
||||
|
||||
Inertia::flash('toast', ['type' => 'success', 'message' => __('Profile updated.')]);
|
||||
|
||||
return to_route('profile.edit');
|
||||
}
|
||||
|
||||
/**
|
||||
* Delete the user's profile.
|
||||
*/
|
||||
public function destroy(ProfileDeleteRequest $request): RedirectResponse
|
||||
{
|
||||
$user = $request->user();
|
||||
|
||||
Auth::logout();
|
||||
|
||||
$user->delete();
|
||||
|
||||
$request->session()->invalidate();
|
||||
$request->session()->regenerateToken();
|
||||
|
||||
return redirect('/');
|
||||
}
|
||||
}
|
||||
66
app/Http/Controllers/Settings/SecurityController.php
Normal file
66
app/Http/Controllers/Settings/SecurityController.php
Normal file
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Settings;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\Http\Requests\Settings\PasswordUpdateRequest;
|
||||
use App\Http\Requests\Settings\TwoFactorAuthenticationRequest;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Validation\Rules\Password;
|
||||
use Inertia\Inertia;
|
||||
use Inertia\Response;
|
||||
use Laravel\Fortify\Features;
|
||||
|
||||
class SecurityController extends Controller
|
||||
{
|
||||
/**
|
||||
* Show the user's security settings page.
|
||||
*/
|
||||
public function edit(TwoFactorAuthenticationRequest $request): Response
|
||||
{
|
||||
$props = [
|
||||
'canManageTwoFactor' => Features::canManageTwoFactorAuthentication(),
|
||||
'canManagePasskeys' => Features::canManagePasskeys(),
|
||||
'passkeys' => Features::canManagePasskeys()
|
||||
? $request->user()
|
||||
->passkeys()
|
||||
->select(['id', 'name', 'credential', 'created_at', 'last_used_at'])
|
||||
->latest()
|
||||
->get()
|
||||
->map(fn ($passkey) => [
|
||||
'id' => $passkey->id,
|
||||
'name' => $passkey->name,
|
||||
'authenticator' => $passkey->authenticator,
|
||||
'created_at_diff' => $passkey->created_at->diffForHumans(),
|
||||
'last_used_at_diff' => $passkey->last_used_at?->diffForHumans(),
|
||||
])
|
||||
->values()
|
||||
->all()
|
||||
: [],
|
||||
'passwordRules' => Password::defaults()->toPasswordRulesString(),
|
||||
];
|
||||
|
||||
if (Features::canManageTwoFactorAuthentication()) {
|
||||
$request->ensureStateIsValid();
|
||||
|
||||
$props['twoFactorEnabled'] = $request->user()->hasEnabledTwoFactorAuthentication();
|
||||
$props['requiresConfirmation'] = Features::optionEnabled(Features::twoFactorAuthentication(), 'confirm');
|
||||
}
|
||||
|
||||
return Inertia::render('settings/security', $props);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the user's password.
|
||||
*/
|
||||
public function update(PasswordUpdateRequest $request): RedirectResponse
|
||||
{
|
||||
$request->user()->update([
|
||||
'password' => $request->password,
|
||||
]);
|
||||
|
||||
Inertia::flash('toast', ['type' => 'success', 'message' => __('Password updated.')]);
|
||||
|
||||
return back();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user