Files
marcocosta10000 d1e8abe428 refactor(prompts): enforce multi-file project structure and remove line constraints
Remove maximum line count restrictions from prompt templates and enhance
the prompt building service to require complete multi-file project outputs.
Update generated prompts to explicitly instruct creation of standard legal
documents (LICENSE, README.md, RULES.md, cookies.html, termos-de-uso.html,
privacidade.html) with full content from scratch. Modify UI text to reflect
the new multi-file requirements.
2026-10-07 22:06:35 -03:00

389 lines
26 KiB
PHP
Executable File

@extends('layouts.app')
@section('header', 'Novo Projeto de Prompt')
@section('breadcrumbs')
@php
$breadcrumbs = [
'Projetos' => route('prompt-projects.index'),
'Novo' => null
];
@endphp
@endsection
@section('content')
<div class="card shadow mb-4">
<div class="card-header py-3">
<h6 class="m-0 fw-bold text-primary">Criar Projeto</h6>
</div>
<div class="card-body">
<form method="POST" action="{{ route('prompt-projects.store') }}">
@csrf
<div class="mb-3">
<label for="template-search-input" class="form-label">Modelo de Projeto</label>
<div style="position: relative;">
<input type="text" class="form-control" id="template-search-input" placeholder="Digite para buscar (ex: clínica, laravel, api)..." autocomplete="off">
<input type="hidden" id="template" name="template" value="">
<div id="template-dropdown" style="display:none; position:absolute; z-index:1050; background:#fff; border:1px solid #dee2e6; border-radius:0.375rem; box-shadow:0 0.5rem 1rem rgba(0,0,0,0.15); max-height:300px; overflow-y:auto; width:100%; top: 100%; left: 0;"></div>
</div>
<select class="form-select d-none" id="template-source">
<option value="">*** Personalizado ***</option>
<optgroup label="Sites & Landing Pages">
<option value="landing-corporativa">Landing Page Corporativa</option>
<option value="landing-saas">Landing Page SaaS</option>
<option value="landing-produto">Landing Page Produto Digital</option>
<option value="landing-evento">Landing Page Evento</option>
<option value="landing-ebook">Landing Page E-book / Lead Magnet</option>
<option value="landing-curso">Landing Page Curso Online</option>
<option value="landing-app">Landing Page App Mobile</option>
<option value="landing-agencia">Landing Page Agência</option>
<option value="landing-restaurante">Landing Page Restaurante</option>
<option value="landing-imobiliaria">Landing Page Imobiliária</option>
<option value="landing-saude">Landing Page Saúde / Bem-estar</option>
<option value="landing-educacao">Landing Page Educação</option>
<option value="landing-restaurante-2">Landing Page Delivery</option>
<option value="site-institucional">Site Institucional</option>
<option value="portfolio-profissional">Portfolio Profissional</option>
<option value="portfolio-criativo">Portfolio Criativo</option>
<option value="blog-pessoal">Blog Pessoal</option>
<option value="ecommerce">E-commerce</option>
</optgroup>
<optgroup label="Frameworks & APIs">
<option value="saas">Sistema SaaS</option>
<option value="dashboard-admin">Dashboard Admin</option>
<option value="laravel-13-app">Laravel 13 + Blade + Tailwind</option>
<option value="laravel-13-api">Laravel 13 + API RESTful</option>
<option value="laravel-vue-fullstack">Laravel 13 + Vue.js 3 Fullstack</option>
<option value="laravel-fullstack-2">Laravel Full Stack 2</option>
<option value="slim-4-api">Slim Framework 4 + API REST</option>
<option value="slim-4-web">Slim Framework 4 + Twig</option>
<option value="vue-spa">Vue.js 3 + Vite SPA</option>
<option value="react-spa">React + Vite SPA</option>
<option value="nextjs-app">Next.js 14 App Router</option>
<option value="node-express">Node.js + Express + TypeScript</option>
<option value="fastapi-python">FastAPI + Python</option>
<option value="api-jwt-auth">API com Autenticação JWT</option>
<option value="microservice">Arquitetura de Microsserviços</option>
<option value="chatbot-ia">Chatbot com IA</option>
<option value="automacao-workflow">Automação de Workflow</option>
<option value="integracao-api">Integração entre APIs</option>
</optgroup>
<optgroup label="Sistemas de Gestão - Serviços">
<option value="gestao-clinica">Sistema de Gestão para Clínica Médica</option>
<option value="gestao-odontologica">Sistema de Gestão para Odontologia</option>
<option value="gestao-advocacia">Sistema de Gestão para Advocacia</option>
<option value="gestao-contabilidade">Sistema de Gestão para Contabilidade</option>
<option value="gestao-consultoria">Sistema de Gestão para Consultoria</option>
<option value="gestao-hotel">Sistema de Gestão para Hotel/Pousada</option>
<option value="gestao-transportadora">Sistema de Gestão para Transportadora</option>
<option value="gestao-oficina">Sistema de Gestão para Oficina Mecânica</option>
<option value="gestao-salao">Sistema de Gestão para Salão de Beleza</option>
<option value="gestao-academia">Sistema de Gestão para Academia</option>
<option value="gestao-veterinaria">Sistema de Gestão para Clínica Veterinária</option>
<option value="gestao-fisioterapia">Sistema de Gestão para Fisioterapia</option>
<option value="gestao-psicologia">Sistema de Gestão para Psicologia</option>
<option value="gestao-seguradora">Sistema de Gestão para Corretora de Seguros</option>
<option value="gestao-agencia-digital">Sistema de Gestão para Agência de Marketing Digital</option>
<option value="gestao-arquitetura">Sistema de Gestão para Escritório de Arquitetura</option>
<option value="gestao-condominio">Sistema de Gestão para Condomínio</option>
<option value="gestao-lavanderia">Sistema de Gestão para Lavanderia</option>
<option value="gestao-buffet">Sistema de Gestão para Buffet / Eventos</option>
<option value="gestao-farmacia">Sistema de Gestão para Farmácia</option>
</optgroup>
<optgroup label="Sistemas de Gestão - Comercial">
<option value="gestao-comercial">Sistema de Gestão Comercial</option>
<option value="gestao-pdv">Sistema de PDV (Ponto de Venda)</option>
<option value="gestao-estoque">Sistema de Controle de Estoque</option>
<option value="gestao-vendas">Sistema de Gestão de Vendas</option>
<option value="gestao-compras">Sistema de Gestão de Compras</option>
<option value="gestao-crm">Sistema de CRM</option>
<option value="gestao-financeira">Sistema de Gestão Financeira</option>
<option value="gestao-faturamento">Sistema de Faturamento/NF-e</option>
<option value="gestao-franquias">Sistema de Gestão de Franquias</option>
<option value="gestao-atacado">Sistema de Gestão de Atacado / Distribuição</option>
<option value="gestao-supermercado">Sistema de Gestão para Supermercado</option>
</optgroup>
<optgroup label="Sistemas de Gestão - Educacional">
<option value="gestao-escolar">Sistema de Gestão Escolar</option>
<option value="gestao-universitaria">Sistema de Gestão Universitária</option>
<option value="gestao-ead">Sistema de EAD</option>
<option value="gestao-cursos">Sistema de Gestão de Cursos</option>
<option value="gestao-biblioteca">Sistema de Biblioteca</option>
</optgroup>
<optgroup label="Sistemas de Gestão - ONGs / Organizações">
<option value="gestao-ong">Sistema de Gestão para ONG</option>
<option value="gestao-projetos-sociais">Sistema de Gestão de Projetos Sociais</option>
<option value="gestao-voluntarios">Sistema de Gestão de Voluntários</option>
<option value="gestao-doacoes">Sistema de Gestão de Doações</option>
</optgroup>
<optgroup label="Sistemas de Gestão - Industrial">
<option value="gestao-industrial">Sistema de Gestão Industrial</option>
<option value="gestao-producao">Sistema de Controle de Produção</option>
<option value="gestao-qualidade">Sistema de Gestão de Qualidade</option>
<option value="gestao-manutencao">Sistema de Manutenção Preventiva</option>
<option value="gestao-seguranca">Sistema de Gestão de Segurança do Trabalho</option>
<option value="gestao-patrimonio">Sistema de Gestão de Patrimônio</option>
<option value="gestao-frota">Sistema de Gestão de Frota</option>
</optgroup>
<optgroup label="Sistemas de Gestão - Outros">
<option value="gestao-petshop">Sistema de Gestão para Pet Shop</option>
<option value="gestao-clube">Sistema de Gestão para Clube Social / Esportivo</option>
<option value="gestao-grafica">Sistema de Gestão para Gráfica</option>
<option value="gestao-padaria">Sistema de Gestão para Padaria / Mercearia</option>
</optgroup>
</select>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="title" class="form-label">Nome do Projeto</label>
<input type="text" class="form-control" id="title" name="title" value="{{ old('title') }}" required>
@error('title') <div class="text-danger small">{{ $message }}</div> @enderror
</div>
<div class="col-md-6 mb-3">
<label for="company_name" class="form-label">Nome da Empresa</label>
<input type="text" class="form-control" id="company_name" name="company_name" value="{{ old('company_name') }}">
@error('company_name') <div class="text-danger small">{{ $message }}</div> @enderror
</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="company_address" class="form-label">Endereço da Empresa</label>
<input type="text" class="form-control" id="company_address" name="company_address" value="{{ old('company_address') }}">
@error('company_address') <div class="text-danger small">{{ $message }}</div> @enderror
</div>
<div class="col-md-6 mb-3">
<label for="company_whatsapp" class="form-label">WhatsApp da Empresa</label>
<input type="text" class="form-control" id="company_whatsapp" name="company_whatsapp" value="{{ old('company_whatsapp') }}">
@error('company_whatsapp') <div class="text-danger small">{{ $message }}</div> @enderror
</div>
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="company_website" class="form-label">Website da Empresa</label>
<input type="url" class="form-control" id="company_website" name="company_website" value="{{ old('company_website') }}">
@error('company_website') <div class="text-danger small">{{ $message }}</div> @enderror
</div>
<div class="col-md-6 mb-3">
<label for="company_social_media" class="form-label">Redes Sociais da Empresa</label>
<input type="text" class="form-control" id="company_social_media" name="company_social_media" value="{{ old('company_social_media') }}" placeholder="Ex: Instagram, LinkedIn, Facebook">
@error('company_social_media') <div class="text-danger small">{{ $message }}</div> @enderror
</div>
</div>
<div class="mb-3">
<label for="company_business_activities" class="form-label">Atividades de Negócio da Empresa</label>
<textarea class="form-control" id="company_business_activities" name="company_business_activities" rows="2">{{ old('company_business_activities') }}</textarea>
@error('company_business_activities') <div class="text-danger small">{{ $message }}</div> @enderror
</div>
<div class="mb-3">
<label for="company_services_products" class="form-label">Serviços e Produtos da Empresa</label>
<textarea class="form-control" id="company_services_products" name="company_services_products" rows="2">{{ old('company_services_products') }}</textarea>
@error('company_services_products') <div class="text-danger small">{{ $message }}</div> @enderror
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="provider_id" class="form-label">Provedor</label>
<select class="form-select" id="provider_id" name="provider_id" required>
<option value="">Selecione...</option>
@foreach($providers ?? [] as $provider)
<option value="{{ $provider->id }}" {{ old('provider_id') == $provider->id ? 'selected' : '' }}>
{{ $provider->name }}
</option>
@endforeach
</select>
@error('provider_id') <div class="text-danger small">{{ $message }}</div> @enderror
</div>
</div>
<div class="mb-3">
<label for="objective" class="form-label">Objetivo</label>
<textarea class="form-control" id="objective" name="objective" rows="2" required>{{ old('objective') }}</textarea>
@error('objective') <div class="text-danger small">{{ $message }}</div> @enderror
</div>
<div class="mb-3">
<label for="context" class="form-label">Contexto</label>
<textarea class="form-control" id="context" name="context" rows="3">{{ old('context') }}</textarea>
@error('context') <div class="text-danger small">{{ $message }}</div> @enderror
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="persona" class="form-label">Persona / Papel</label>
<input type="text" class="form-control" id="persona" name="persona" value="{{ old('persona') }}">
@error('persona') <div class="text-danger small">{{ $message }}</div> @enderror
</div>
<div class="col-md-6 mb-3">
<label for="tone" class="form-label">Tom / Estilo</label>
<input type="text" class="form-control" id="tone" name="tone" value="{{ old('tone') }}">
@error('tone') <div class="text-danger small">{{ $message }}</div> @enderror
</div>
</div>
<div class="mb-3">
<label for="constraints" class="form-label">Restrições</label>
<textarea class="form-control" id="constraints" name="constraints" rows="2">{{ old('constraints') }}</textarea>
@error('constraints') <div class="text-danger small">{{ $message }}</div> @enderror
</div>
<div class="mb-3">
<label for="output_format" class="form-label">Formato de Saída</label>
<input type="text" class="form-control" id="output_format" name="output_format" value="{{ old('output_format') }}">
@error('output_format') <div class="text-danger small">{{ $message }}</div> @enderror
</div>
<div class="mb-3">
<label for="examples" class="form-label">Exemplos</label>
<textarea class="form-control" id="examples" name="examples" rows="3">{{ old('examples') }}</textarea>
@error('examples') <div class="text-danger small">{{ $message }}</div> @enderror
</div>
<div class="mb-3">
<label for="variables" class="form-label">Variáveis</label>
<textarea class="form-control" id="variables" name="variables" rows="2" placeholder="var1, var2">{{ old('variables') }}</textarea>
@error('variables') <div class="text-danger small">{{ $message }}</div> @enderror
</div>
<div class="row">
<div class="col-md-6 mb-3">
<label for="temperature" class="form-label">Temperatura</label>
<input type="number" step="0.1" min="0" max="1" class="form-control" id="temperature" name="temperature" value="{{ old('temperature', 0.7) }}" required>
@error('temperature') <div class="text-danger small">{{ $message }}</div> @enderror
</div>
<div class="col-md-6 mb-3">
<label for="max_tokens" class="form-label">Max Tokens</label>
<input type="number" class="form-control" id="max_tokens" name="max_tokens" value="{{ old('max_tokens', 1024) }}" required>
@error('max_tokens') <div class="text-danger small">{{ $message }}</div> @enderror
</div>
</div>
<button type="submit" class="btn btn-primary"><i class="bi bi-save"></i> Salvar</button>
<a href="{{ route('prompt-projects.index') }}" class="btn btn-secondary">Cancelar</a>
</form>
</div>
</div>
@section('scripts')
<script>
const templatesApiUrl = "{{ route('prompt-templates.api') }}";
window.templates = {};
window.templatesLoaded = false;
function loadTemplates() {
fetch(templatesApiUrl, {
headers: {
'Accept': 'application/json',
'X-Requested-With': 'XMLHttpRequest',
}
})
.then(response => {
if (!response.ok) {
throw new Error('HTTP ' + response.status);
}
return response.json();
})
.then(data => {
window.templates = data;
window.templatesLoaded = true;
console.log('Templates loaded:', Object.keys(data).length);
})
.catch(error => {
console.error('Failed to load templates:', error);
});
}
document.addEventListener('DOMContentLoaded', loadTemplates);
const allOptions = Array.from(document.querySelectorAll('#template-source option')).map(opt => ({
value: opt.value,
text: opt.textContent,
group: opt.parentElement.tagName === 'OPTGROUP' ? opt.parentElement.getAttribute('label') : ''
}));
const searchInput = document.getElementById('template-search-input');
const dropdown = document.getElementById('template-dropdown');
const hiddenInput = document.getElementById('template');
function renderDropdown(filterText = '') {
const term = filterText.toLowerCase();
const filtered = allOptions.filter(opt => opt.value === '' || opt.text.toLowerCase().includes(term));
dropdown.innerHTML = '';
if (filtered.length === 0) {
dropdown.innerHTML = '<div class="dropdown-item disabled" style="padding:0.5rem 1rem; color:#6c757d;">Nenhum modelo encontrado</div>';
dropdown.style.display = 'block';
return;
}
filtered.forEach(opt => {
const item = document.createElement('div');
item.className = 'dropdown-item';
item.textContent = opt.text;
item.style.cssText = 'padding: 0.5rem 1rem; cursor: pointer;';
item.addEventListener('mouseenter', function() {
item.style.backgroundColor = '#f8f9fa';
});
item.addEventListener('mouseleave', function() {
item.style.backgroundColor = '';
});
item.addEventListener('click', function(e) {
e.preventDefault();
searchInput.value = opt.text;
hiddenInput.value = opt.value;
dropdown.style.display = 'none';
const template = window.templates[opt.text] || {};
if (Object.keys(template).length > 0) {
document.getElementById('title').value = template.title || '';
document.getElementById('objective').value = template.objective || '';
document.getElementById('context').value = template.context || '';
document.getElementById('persona').value = template.persona || '';
document.getElementById('tone').value = template.tone || '';
document.getElementById('constraints').value = template.constraints || '';
document.getElementById('output_format').value = template.output_format || '';
document.getElementById('examples').value = template.examples || '';
document.getElementById('variables').value = template.variables || '';
document.getElementById('temperature').value = template.temperature ?? 0.7;
document.getElementById('max_tokens').value = template.max_tokens ?? 1024;
if (template.standard_files !== false) {
const stdFilesNote = "\n\n---\nNOTA: O projeto gerado deve conter MULTIPLOS ARQUIVOS em estrutura de pastas organizada. CRIE cada um dos seguintes arquivos padrao DO ZERO com conteudo completo e pronto para uso: LICENSE (MIT), README.md (documentacao completa), RULES.md (regras de codigo), cookies.html (banner LGPD completo com HTML/CSS/JS), termos-de-uso.html (Termos de Uso completos), privacidade.html (Politica de Privacidade LGPD completa). Nao apenas descreva o conteudo — gere o conteudo completo de cada arquivo.";
const constraintsEl = document.getElementById('constraints');
if (constraintsEl && !constraintsEl.value.includes('Arquivos padrao')) {
constraintsEl.value = constraintsEl.value.trim() + stdFilesNote;
}
}
} else {
const fields = ['title', 'objective', 'context', 'persona', 'tone', 'constraints', 'output_format', 'examples', 'variables', 'temperature', 'max_tokens'];
fields.forEach(field => {
const el = document.getElementById(field);
if (el) {
if (field === 'temperature') el.value = '0.7';
else if (field === 'max_tokens') el.value = '1024';
else el.value = '';
}
});
}
});
dropdown.appendChild(item);
});
if (filterText.length > 0) {
dropdown.style.display = 'block';
}
}
searchInput.addEventListener('input', function() {
renderDropdown(this.value);
});
searchInput.addEventListener('focus', function() {
renderDropdown(this.value);
});
document.addEventListener('click', function(e) {
if (!searchInput.contains(e.target) && !dropdown.contains(e.target)) {
dropdown.style.display = 'none';
}
});
</script>
@endsection('scripts')
@endsection('content')