feat: add City10 platform with Laravel 13, AI agents, 3D viewer, and LGPD compliance
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
This commit is contained in:
@@ -0,0 +1,109 @@
|
||||
<?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'));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user