Initial commit of the complete City10 virtual city simulation platform. Backend (Laravel 13): - REST API with Sanctum tokens and session-based web authentication - Role-based access control (admin/user) with middleware protection - AI agent CRUD with OpenRouter.ai integration restricted to free models - Store management with 3D coordinates and AI shopkeeper assignment - Versioned legal documents (Terms, Privacy, Cookies) with LGPD audit trail - Math CAPTCHA on public forms and multi-language support (pt_BR, en, es) Frontend: - Responsive Blade templates with Tailwind CSS v4 - Three.js 3D city viewer with SSE chat streaming to AI agents - Admin panel for store and user administration Infrastructure: - MariaDB/MySQL database with comprehensive migrations - Database seeders for demo content and test accounts - PHPUnit test suite and Laravel Pint code styling - Vite build configuration with hot reload
110 lines
3.2 KiB
PHP
110 lines
3.2 KiB
PHP
<?php
|
|
|
|
namespace App\Http\Controllers\Web\Admin;
|
|
|
|
use App\Http\Controllers\Controller;
|
|
use App\Http\Requests\StoreRequest;
|
|
use App\Models\AiAgent;
|
|
use App\Models\Store;
|
|
use App\Models\User;
|
|
use Illuminate\Http\RedirectResponse;
|
|
use Illuminate\Http\Request;
|
|
use Illuminate\View\View;
|
|
|
|
/**
|
|
* Administrative store management (admin only).
|
|
*
|
|
* Provides the server-rendered CRUD screens where administrators create
|
|
* and edit stores, defining their exact 3D placement in the virtual city
|
|
* through a Three.js position picker.
|
|
*/
|
|
class StoreAdminController extends Controller
|
|
{
|
|
/**
|
|
* Paginated listing of every store with search and type filtering.
|
|
*/
|
|
public function index(Request $request): View
|
|
{
|
|
$query = Store::query()->with(['owner:id,name', 'agent:id,name'])->orderBy('name');
|
|
|
|
if ($search = $request->query('search')) {
|
|
$query->where('name', 'like', "%{$search}%");
|
|
}
|
|
|
|
if ($type = $request->query('type')) {
|
|
$query->where('type', $type);
|
|
}
|
|
|
|
$types = Store::query()->select('type')->distinct()->orderBy('type')->pluck('type');
|
|
|
|
return view('admin.stores.index', [
|
|
'stores' => $query->paginate(12)->withQueryString(),
|
|
'types' => $types,
|
|
'currentSearch' => $search,
|
|
'currentType' => $type,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Show the store creation form with the 3D position picker.
|
|
*/
|
|
public function create(): View
|
|
{
|
|
return view('admin.stores.form', [
|
|
'store' => new Store(['is_open' => true, 'pos_x' => 0, 'pos_y' => 0, 'pos_z' => 0, 'width' => 8, 'height' => 6, 'depth' => 8]),
|
|
'owners' => User::query()->orderBy('name')->get(['id', 'name', 'email']),
|
|
'agents' => AiAgent::query()->orderBy('name')->get(['id', 'name', 'type']),
|
|
'isEdit' => false,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Persist a newly created store.
|
|
*/
|
|
public function store(StoreRequest $request): RedirectResponse
|
|
{
|
|
$store = Store::create($request->validated());
|
|
|
|
return redirect()
|
|
->route('admin.stores.edit', $store)
|
|
->with('status', __('admin.stores.created'));
|
|
}
|
|
|
|
/**
|
|
* Show the store edit form pre-filled with the current 3D placement.
|
|
*/
|
|
public function edit(Store $store): View
|
|
{
|
|
return view('admin.stores.form', [
|
|
'store' => $store,
|
|
'owners' => User::query()->orderBy('name')->get(['id', 'name', 'email']),
|
|
'agents' => AiAgent::query()->orderBy('name')->get(['id', 'name', 'type']),
|
|
'isEdit' => true,
|
|
]);
|
|
}
|
|
|
|
/**
|
|
* Update an existing store.
|
|
*/
|
|
public function update(StoreRequest $request, Store $store): RedirectResponse
|
|
{
|
|
$store->update($request->validated());
|
|
|
|
return redirect()
|
|
->route('admin.stores.index')
|
|
->with('status', __('admin.stores.updated'));
|
|
}
|
|
|
|
/**
|
|
* Delete a store from the city registry.
|
|
*/
|
|
public function destroy(Store $store): RedirectResponse
|
|
{
|
|
$store->delete();
|
|
|
|
return redirect()
|
|
->route('admin.stores.index')
|
|
->with('status', __('stores.deleted'));
|
|
}
|
|
}
|