feat: add legal document acceptance and standard project files generation

Implement legal document acceptance tracking requiring users to accept
terms of service and privacy policy before continuing to use the system.
Add automatic generation of standard project files (LICENSE, README.md,
RULES.md, cookies.html, termos-de-uso.html, privacidade.html) with
download functionality. Enhance prompt building with detailed Portuguese
instructions and system requirements. Update seeders with comprehensive
legal content and standard files configuration for all prompt templates.
This commit is contained in:
2026-10-07 21:30:40 -03:00
parent 9b21d11e75
commit f2bca340f9
13 changed files with 883 additions and 75 deletions
@@ -6,9 +6,12 @@ namespace App\Http\Controllers\Legal;
use App\Http\Controllers\Controller;
use App\Models\LegalDocument;
use App\Models\UserDocumentAcceptance;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use Illuminate\Validation\Rule;
use Illuminate\View\View;
class LegalDocumentController extends Controller
{
@@ -76,10 +79,47 @@ class LegalDocumentController extends Controller
/**
* Display the legal document page.
*/
public function show(string $type)
public function show(string $type): View|RedirectResponse
{
$document = LegalDocument::where('type', $type)->latest('version')->firstOrFail();
return view('legal.documents.show', compact('document'));
$pendingAcceptance = false;
if (Auth::check() && Auth::user()->role !== 'superadmin') {
$accepted = UserDocumentAcceptance::where('user_id', Auth::id())
->where('legal_document_id', $document->id)
->exists();
if (!$accepted) {
$pendingAcceptance = true;
}
}
return view('legal.documents.show', compact('document', 'pendingAcceptance'));
}
/**
* Accept a legal document.
*/
public function accept(string $type): RedirectResponse
{
$document = LegalDocument::where('type', $type)->latest('version')->firstOrFail();
if (Auth::check()) {
UserDocumentAcceptance::updateOrCreate(
[
'user_id' => Auth::id(),
'legal_document_id' => $document->id,
],
[
'document_version' => $document->version,
'ip_address' => request()->ip(),
'user_agent' => request()->userAgent(),
'accepted_at' => now(),
]
);
}
return redirect()->route('dashboard')
->with('success', 'Documento aceito com sucesso.');
}
}
@@ -7,8 +7,12 @@ namespace App\Http\Controllers\PromptProject;
use App\Http\Controllers\Controller;
use App\Http\Requests\PromptProject\StorePromptProjectRequest;
use App\Http\Requests\PromptProject\UpdatePromptProjectRequest;
use App\Models\AiModel;
use App\Models\AiProvider;
use App\Models\PromptGeneration;
use App\Models\PromptProject;
use App\Services\PromptProjectService;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
@@ -62,24 +66,26 @@ class PromptProjectController extends Controller
/**
* Display the specified resource.
*/
public function show(\App\Models\PromptProject $promptProject): View
public function show(PromptProject $promptProject): View
{
$this->authorize('view', $promptProject);
$providerModels = \App\Models\AiModel::where('provider_id', $promptProject->provider_id)
$providerModels = AiModel::where('provider_id', $promptProject->provider_id)
->where('is_active', true)
->get();
$models = $providerModels->isNotEmpty()
? $providerModels
: \App\Models\AiModel::where('is_active', true)->get();
: AiModel::where('is_active', true)->get();
$generations = \App\Models\PromptGeneration::where('project_id', $promptProject->id)
$generations = PromptGeneration::where('project_id', $promptProject->id)
->latest()
->take(5)
->get();
return view('prompt-projects.show', compact('promptProject', 'models', 'generations'));
$standardFiles = $this->promptProjectService->getStandardFiles($promptProject);
return view('prompt-projects.show', compact('promptProject', 'models', 'generations', 'standardFiles'));
}
/**
@@ -123,7 +129,7 @@ class PromptProjectController extends Controller
/**
* Generate prompt for the specified project.
*/
public function generate(\App\Models\PromptProject $promptProject, Request $request): RedirectResponse
public function generate(PromptProject $promptProject, Request $request): RedirectResponse
{
$this->authorize('update', $promptProject);
@@ -137,7 +143,7 @@ class PromptProjectController extends Controller
$model = $customModel;
}
} else {
$model = \App\Models\AiModel::where('slug', $modelSlug)
$model = AiModel::where('slug', $modelSlug)
->where('is_active', true)
->first();
}
@@ -152,7 +158,7 @@ class PromptProjectController extends Controller
/**
* Get built prompt for the specified project.
*/
public function getPrompt(\App\Models\PromptProject $promptProject): \Illuminate\Http\JsonResponse
public function getPrompt(PromptProject $promptProject): JsonResponse
{
$this->authorize('view', $promptProject);
@@ -160,4 +166,36 @@ class PromptProjectController extends Controller
return response()->json(['prompt' => $prompt]);
}
/**
* Get standard files for the specified project.
*/
public function getStandardFiles(PromptProject $promptProject): JsonResponse
{
$this->authorize('view', $promptProject);
$files = $this->promptProjectService->getStandardFiles($promptProject);
return response()->json($files);
}
/**
* Download a standard file for the specified project.
*/
public function downloadFile(PromptProject $promptProject, string $filename)
{
$this->authorize('view', $promptProject);
$files = $this->promptProjectService->getStandardFiles($promptProject);
if (!isset($files[$filename])) {
abort(404);
}
$content = $files[$filename];
return response()->streamDownload(function () use ($content) {
echo $content;
}, $filename);
}
}
@@ -58,6 +58,10 @@ class PromptTemplateController extends Controller
$data['template_data'] = json_decode($data['template_data'], true);
}
if (!isset($data['template_data']['standard_files'])) {
$data['template_data']['standard_files'] = true;
}
$this->promptTemplateService->store($data);
return redirect()->route('prompt-templates.index')
@@ -131,6 +135,7 @@ class PromptTemplateController extends Controller
'variables' => $data['variables'] ?? '',
'temperature' => $data['temperature'] ?? 0.7,
'max_tokens' => $data['max_tokens'] ?? 1024,
'standard_files' => $data['standard_files'] ?? true,
];
}