First Commit
This commit is contained in:
Executable
+3
@@ -0,0 +1,3 @@
|
||||
@tailwind base;
|
||||
@tailwind components;
|
||||
@tailwind utilities;
|
||||
Executable
+7
@@ -0,0 +1,7 @@
|
||||
|
||||
|
||||
import Alpine from 'alpinejs';
|
||||
|
||||
window.Alpine = Alpine;
|
||||
|
||||
Alpine.start();
|
||||
Executable
+6
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'accept_terms' => 'I have read and accept the',
|
||||
'accept_privacy' => 'I have read and accept the',
|
||||
];
|
||||
Executable
+21
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'title' => 'Cookies and Privacy',
|
||||
'description' => 'We use cookies to improve your experience, analyze traffic, and personalize content. By continuing to browse, you agree to our ',
|
||||
'privacy_policy' => 'Privacy Policy',
|
||||
'accept_all' => 'Accept All',
|
||||
'reject' => 'Reject',
|
||||
'customize' => 'Customize',
|
||||
'necessary' => 'Necessary',
|
||||
'necessary_description' => 'Essential for the site to function.',
|
||||
'statistics' => 'Statistics',
|
||||
'statistics_description' => 'Help us understand how the site is used.',
|
||||
'marketing' => 'Marketing',
|
||||
'marketing_description' => 'Used to display relevant ads.',
|
||||
'preferences' => 'Preferences',
|
||||
'preferences_description' => 'Store your settings and preferences.',
|
||||
'save_preferences' => 'Save Preferences',
|
||||
'cancel' => 'Cancel',
|
||||
'select_types' => 'Select the types of cookies you authorize:',
|
||||
];
|
||||
Executable
+9
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'copyright' => 'All rights reserved',
|
||||
'powered_by' => 'Powered by',
|
||||
'terms_of_service' => 'Terms of Service',
|
||||
'privacy_policy' => 'Privacy Policy',
|
||||
'cookie_preferences' => 'Cookie Preferences',
|
||||
];
|
||||
Executable
+21
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'terms_of_service' => 'Terms of Service',
|
||||
'privacy_policy' => 'Privacy Policy',
|
||||
'documents_management' => 'Manage Documents',
|
||||
'title' => 'Title',
|
||||
'version' => 'Version',
|
||||
'published_at' => 'Published at',
|
||||
'actions' => 'Actions',
|
||||
'edit' => 'Edit',
|
||||
'new_document' => 'New Document',
|
||||
'view' => 'View',
|
||||
'save' => 'Save',
|
||||
'back' => 'Back',
|
||||
'not_published' => 'Not published',
|
||||
'no_documents' => 'No documents found.',
|
||||
'content' => 'Content',
|
||||
'publish_now' => 'Leave blank to publish immediately.',
|
||||
'type' => 'Type',
|
||||
];
|
||||
Executable
+6
@@ -0,0 +1,6 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'accept_terms' => 'Li e aceito os',
|
||||
'accept_privacy' => 'Li e aceito a',
|
||||
];
|
||||
Executable
+21
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'title' => 'Cookies e Privacidade',
|
||||
'description' => 'Utilizamos cookies para melhorar sua experiência, analisar tráfego e personalizar conteúdo. Ao continuar navegando, você concorda com nossa ',
|
||||
'privacy_policy' => 'Política de Privacidade',
|
||||
'accept_all' => 'Aceitar Todos',
|
||||
'reject' => 'Recusar',
|
||||
'customize' => 'Personalizar',
|
||||
'necessary' => 'Necessários',
|
||||
'necessary_description' => 'Essenciais para o funcionamento do site.',
|
||||
'statistics' => 'Estatísticas',
|
||||
'statistics_description' => 'Nos ajudam a entender como o site é utilizado.',
|
||||
'marketing' => 'Marketing',
|
||||
'marketing_description' => 'Utilizados para exibir anúncios relevantes.',
|
||||
'preferences' => 'Preferências',
|
||||
'preferences_description' => 'Armazenam suas configurações e preferências.',
|
||||
'save_preferences' => 'Salvar Preferências',
|
||||
'cancel' => 'Cancelar',
|
||||
'select_types' => 'Selecione os tipos de cookies que você autoriza:',
|
||||
];
|
||||
Executable
+9
@@ -0,0 +1,9 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'copyright' => 'Todos os direitos reservados',
|
||||
'powered_by' => 'Powered by',
|
||||
'terms_of_service' => 'Termos de Uso',
|
||||
'privacy_policy' => 'Política de Privacidade',
|
||||
'cookie_preferences' => 'Preferências de Cookies',
|
||||
];
|
||||
Executable
+21
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
'terms_of_service' => 'Termos de Uso',
|
||||
'privacy_policy' => 'Política de Privacidade',
|
||||
'documents_management' => 'Gerenciar Documentos',
|
||||
'title' => 'Título',
|
||||
'version' => 'Versão',
|
||||
'published_at' => 'Data de Publicação',
|
||||
'actions' => 'Ações',
|
||||
'edit' => 'Editar',
|
||||
'new_document' => 'Novo Documento',
|
||||
'view' => 'Visualizar',
|
||||
'save' => 'Salvar',
|
||||
'back' => 'Voltar',
|
||||
'not_published' => 'Não publicado',
|
||||
'no_documents' => 'Nenhum documento encontrado.',
|
||||
'content' => 'Conteúdo',
|
||||
'publish_now' => 'Deixe em branco para publicar imediatamente.',
|
||||
'type' => 'Tipo',
|
||||
];
|
||||
Executable
+198
@@ -0,0 +1,198 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('header', 'Agente de IA')
|
||||
|
||||
@section('breadcrumbs')
|
||||
@php
|
||||
$breadcrumbs = [
|
||||
'Agente de IA' => null
|
||||
];
|
||||
@endphp
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="card shadow mb-4">
|
||||
<div class="card-header py-3">
|
||||
<h6 class="m-0 fw-bold text-primary">Executar Prompt</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="alert alert-info">
|
||||
<i class="bi bi-info-circle me-2"></i> Para fazer perguntas, selecione o provider e o modelo e pergunte e aumente o Max Tokens para 8192.
|
||||
</div>
|
||||
<form method="POST" action="{{ route('ai-agent.execute') }}">
|
||||
@csrf
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="project_id" class="form-label">Projeto de Prompt (opcional)</label>
|
||||
<select class="form-select" id="project_id" name="project_id">
|
||||
<option value="">-- Selecione um projeto --</option>
|
||||
@foreach($projects as $project)
|
||||
<option value="{{ $project->id }}" {{ old('project_id') == $project->id ? 'selected' : '' }}>
|
||||
{{ $project->title }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="provider_id" class="form-label">Provider</label>
|
||||
<select class="form-select" id="provider_id" name="provider_id" required>
|
||||
<option value="">-- Selecione um provider --</option>
|
||||
@foreach($providers as $provider)
|
||||
<option value="{{ $provider->id }}" {{ old('provider_id', $defaultProviderId) == $provider->id ? 'selected' : '' }}>
|
||||
{{ $provider->name }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-6 mb-3">
|
||||
<label for="model_id" class="form-label">Modelo</label>
|
||||
<select class="form-select" id="model_id" name="model_id" required disabled>
|
||||
<option value="">-- Selecione um provider primeiro --</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-3 mb-3">
|
||||
<label for="temperature" class="form-label">Temperatura</label>
|
||||
<input type="number" class="form-control" id="temperature" name="temperature" value="{{ old('temperature', 0.7) }}" step="0.1" min="0" max="1">
|
||||
</div>
|
||||
<div class="col-md-3 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', 8192) }}" min="1" max="128000">
|
||||
</div>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="prompt_text" class="form-label">Prompt</label>
|
||||
<textarea class="form-control font-monospace" id="prompt_text" name="prompt_text" rows="10" required>{{ old('prompt_text') }}</textarea>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary"><i class="bi bi-play-circle me-2"></i>Executar</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if(session('agent_result'))
|
||||
@php $result = session('agent_result') @endphp
|
||||
<div class="card shadow mb-4">
|
||||
<div class="card-header py-3 d-flex justify-content-between align-items-center">
|
||||
<h6 class="m-0 fw-bold text-primary">Resultado</h6>
|
||||
<div class="btn-group">
|
||||
<button class="btn btn-sm btn-outline-secondary" onclick="copyResult()">
|
||||
<i class="bi bi-clipboard me-1"></i> Copiar
|
||||
</button>
|
||||
<button class="btn btn-sm btn-outline-primary" onclick="downloadResult()">
|
||||
<i class="bi bi-download me-1"></i> Download
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
@if($result['success'])
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-4">
|
||||
<strong>Provider:</strong> {{ $result['provider'] }}
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<strong>Modelo:</strong> {{ $result['model'] }}
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<strong>Tempo:</strong> {{ number_format($result['generation_time'], 3) }}s
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-12">
|
||||
<strong>Tokens usados:</strong> {{ $result['tokens_used'] ?? 'N/A' }}
|
||||
</div>
|
||||
</div>
|
||||
<textarea class="form-control font-monospace" id="result_text" rows="15">{{ $result['content'] }}</textarea>
|
||||
@else
|
||||
<div class="alert alert-danger">
|
||||
<strong>Erro:</strong> {{ $result['error'] }}
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@section('scripts')
|
||||
<script>
|
||||
const providerSelect = document.getElementById('provider_id');
|
||||
const modelSelect = document.getElementById('model_id');
|
||||
const projectSelect = document.getElementById('project_id');
|
||||
const promptText = document.getElementById('prompt_text');
|
||||
|
||||
providerSelect.addEventListener('change', function() {
|
||||
const providerId = this.value;
|
||||
|
||||
if (!providerId) {
|
||||
modelSelect.innerHTML = '<option value="">-- Selecione um provider primeiro --</option>';
|
||||
modelSelect.disabled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
fetch(`{{ route('ai-agent.models') }}?provider_id=${providerId}`)
|
||||
.then(response => response.json())
|
||||
.then(models => {
|
||||
modelSelect.innerHTML = '<option value="">-- Selecione um modelo --</option>';
|
||||
models.forEach(model => {
|
||||
const option = document.createElement('option');
|
||||
option.value = model.id;
|
||||
option.textContent = model.name;
|
||||
modelSelect.appendChild(option);
|
||||
});
|
||||
modelSelect.disabled = false;
|
||||
});
|
||||
});
|
||||
|
||||
@if($defaultProviderId && $defaultModelId)
|
||||
window.addEventListener('DOMContentLoaded', function() {
|
||||
providerSelect.value = {{ $defaultProviderId }};
|
||||
providerSelect.dispatchEvent(new Event('change'));
|
||||
setTimeout(() => {
|
||||
modelSelect.value = {{ $defaultModelId }};
|
||||
}, 500);
|
||||
});
|
||||
@endif
|
||||
|
||||
projectSelect.addEventListener('change', function() {
|
||||
const projectId = this.value;
|
||||
|
||||
if (!projectId) {
|
||||
promptText.value = '';
|
||||
return;
|
||||
}
|
||||
|
||||
fetch(`{{ url('prompt-projects') }}/${projectId}/prompt`)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
promptText.value = data.prompt || '';
|
||||
})
|
||||
.catch(() => {
|
||||
promptText.value = '';
|
||||
});
|
||||
});
|
||||
|
||||
function copyResult() {
|
||||
const resultText = document.getElementById('result_text');
|
||||
resultText.select();
|
||||
document.execCommand('copy');
|
||||
alert('Copiado para a área de transferência!');
|
||||
}
|
||||
|
||||
function downloadResult() {
|
||||
const resultText = document.getElementById('result_text');
|
||||
const blob = new Blob([resultText.value], { type: 'text/plain' });
|
||||
const url = URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.download = 'resultado-ia.txt';
|
||||
document.body.appendChild(a);
|
||||
a.click();
|
||||
document.body.removeChild(a);
|
||||
URL.revokeObjectURL(url);
|
||||
}
|
||||
</script>
|
||||
@endsection
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('header', 'Novo Provedor')
|
||||
|
||||
@section('breadcrumbs')
|
||||
@php
|
||||
$breadcrumbs = [
|
||||
'Provedores' => route('ai-providers.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">Cadastrar Provedor</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="POST" action="{{ route('ai-providers.store') }}">
|
||||
@csrf
|
||||
<div class="mb-3">
|
||||
<label for="name" class="form-label">Nome</label>
|
||||
<input type="text" class="form-control" id="name" name="name" value="{{ old('name') }}" required>
|
||||
@error('name') <div class="text-danger small">{{ $message }}</div> @enderror
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="slug" class="form-label">Slug</label>
|
||||
<input type="text" class="form-control" id="slug" name="slug" value="{{ old('slug') }}" required>
|
||||
@error('slug') <div class="text-danger small">{{ $message }}</div> @enderror
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="type" class="form-label">Tipo</label>
|
||||
<select class="form-select" id="type" name="type" required>
|
||||
<option value="">Selecione...</option>
|
||||
<option value="openrouter" {{ old('type') == 'openrouter' ? 'selected' : '' }}>OpenRouter</option>
|
||||
<option value="stepfun" {{ old('type') == 'stepfun' ? 'selected' : '' }}>StepFun</option>
|
||||
<option value="llamacpp" {{ old('type') == 'llamacpp' ? 'selected' : '' }}>Llama.cpp</option>
|
||||
</select>
|
||||
@error('type') <div class="text-danger small">{{ $message }}</div> @enderror
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="api_key" class="form-label">API Key</label>
|
||||
<input type="password" class="form-control" id="api_key" name="api_key" value="{{ old('api_key') }}" required>
|
||||
<i class="bi bi-eye-slash" id="toggleApiKey" style="cursor:pointer; position:relative; top:-30px; left:calc(100% - 30px);"></i>
|
||||
@error('api_key') <div class="text-danger small">{{ $message }}</div> @enderror
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="base_url" class="form-label">Base URL</label>
|
||||
<input type="text" class="form-control" id="base_url" name="base_url" value="{{ old('base_url') }}" required>
|
||||
@error('base_url') <div class="text-danger small">{{ $message }}</div> @enderror
|
||||
</div>
|
||||
<div class="mb-3 form-check">
|
||||
<input type="checkbox" class="form-check-input" id="is_active" name="is_active" value="1" {{ old('is_active', true) ? 'checked' : '' }}>
|
||||
<label class="form-check-label" for="is_active">Ativo</label>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary"><i class="bi bi-save"></i> Salvar</button>
|
||||
<a href="{{ route('ai-providers.index') }}" class="btn btn-secondary">Cancelar</a>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@section('scripts')
|
||||
<script>
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
initApiKeyToggle();
|
||||
initSlugGenerator();
|
||||
});
|
||||
} else {
|
||||
initApiKeyToggle();
|
||||
initSlugGenerator();
|
||||
}
|
||||
|
||||
function initApiKeyToggle() {
|
||||
const toggle = document.getElementById('toggleApiKey');
|
||||
const input = document.getElementById('api_key');
|
||||
if (toggle && input) {
|
||||
toggle.addEventListener('click', function() {
|
||||
const type = input.getAttribute('type') === 'password' ? 'text' : 'password';
|
||||
input.setAttribute('type', type);
|
||||
toggle.classList.toggle('bi-eye');
|
||||
toggle.classList.toggle('bi-eye-slash');
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function initSlugGenerator() {
|
||||
const nameInput = document.getElementById('name');
|
||||
const slugInput = document.getElementById('slug');
|
||||
if (nameInput && slugInput) {
|
||||
nameInput.addEventListener('input', function() {
|
||||
slugInput.value = this.value.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, '');
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@endsection
|
||||
Executable
+82
@@ -0,0 +1,82 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('header', 'Editar Provedor')
|
||||
|
||||
@section('breadcrumbs')
|
||||
@php
|
||||
$breadcrumbs = [
|
||||
'Provedores' => route('ai-providers.index'),
|
||||
'Editar' => 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">Editar Provedor</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="POST" action="{{ route('ai-providers.update', $aiProvider) }}">
|
||||
@csrf @method('PUT')
|
||||
<div class="mb-3">
|
||||
<label for="name" class="form-label">Nome</label>
|
||||
<input type="text" class="form-control" id="name" name="name" value="{{ old('name', $aiProvider->name) }}" required>
|
||||
@error('name') <div class="text-danger small">{{ $message }}</div> @enderror
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="type" class="form-label">Tipo</label>
|
||||
<select class="form-select" id="type" name="type" required>
|
||||
<option value="">Selecione...</option>
|
||||
<option value="openrouter" {{ old('type', $aiProvider->type) == 'openrouter' ? 'selected' : '' }}>OpenRouter</option>
|
||||
<option value="stepfun" {{ old('type', $aiProvider->type) == 'stepfun' ? 'selected' : '' }}>StepFun</option>
|
||||
<option value="llamacpp" {{ old('type', $aiProvider->type) == 'llamacpp' ? 'selected' : '' }}>Llama.cpp</option>
|
||||
</select>
|
||||
@error('type') <div class="text-danger small">{{ $message }}</div> @enderror
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="api_key" class="form-label">API Key</label>
|
||||
<input type="password" class="form-control" id="api_key" name="api_key" value="{{ old('api_key', $aiProvider->api_key) }}" required>
|
||||
<i class="bi bi-eye-slash" id="toggleApiKey" style="cursor:pointer; position:relative; top:-30px; left:calc(100% - 30px);"></i>
|
||||
@error('api_key') <div class="text-danger small">{{ $message }}</div> @enderror
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="base_url" class="form-label">Base URL</label>
|
||||
<input type="text" class="form-control" id="base_url" name="base_url" value="{{ old('base_url', $aiProvider->base_url) }}" required>
|
||||
@error('base_url') <div class="text-danger small">{{ $message }}</div> @enderror
|
||||
</div>
|
||||
<div class="mb-3 form-check">
|
||||
<input type="checkbox" class="form-check-input" id="is_active" name="is_active" value="1" {{ old('is_active', $aiProvider->is_active) ? 'checked' : '' }}>
|
||||
<label class="form-check-label" for="is_active">Ativo</label>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary"><i class="bi bi-save"></i> Atualizar</button>
|
||||
<a href="{{ route('ai-providers.index') }}" class="btn btn-secondary">Cancelar</a>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@section('scripts')
|
||||
<script>
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
initApiKeyToggle();
|
||||
});
|
||||
} else {
|
||||
initApiKeyToggle();
|
||||
}
|
||||
|
||||
function initApiKeyToggle() {
|
||||
const toggle = document.getElementById('toggleApiKey');
|
||||
const input = document.getElementById('api_key');
|
||||
if (toggle && input) {
|
||||
toggle.addEventListener('click', function() {
|
||||
const type = input.getAttribute('type') === 'password' ? 'text' : 'password';
|
||||
input.setAttribute('type', type);
|
||||
toggle.classList.toggle('bi-eye');
|
||||
toggle.classList.toggle('bi-eye-slash');
|
||||
});
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@endsection
|
||||
Executable
+70
@@ -0,0 +1,70 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('header', 'Provedores de IA')
|
||||
|
||||
@section('breadcrumbs')
|
||||
@php
|
||||
$breadcrumbs = [
|
||||
'Provedores' => route('ai-providers.index')
|
||||
];
|
||||
@endphp
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
<div class="card shadow mb-4">
|
||||
<div class="card-header py-3 d-flex justify-content-between align-items-center">
|
||||
<h6 class="m-0 fw-bold text-primary">Lista de Provedores</h6>
|
||||
<a href="{{ route('ai-providers.create') }}" class="btn btn-primary btn-sm">
|
||||
<i class="bi bi-plus-circle"></i> Novo Provedor
|
||||
</a>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
@if(isset($providers) && count($providers) > 0)
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Nome</th>
|
||||
<th>Tipo</th>
|
||||
<th>Status</th>
|
||||
<th>Ações</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($providers as $provider)
|
||||
<tr>
|
||||
<td>{{ $provider->name }}</td>
|
||||
<td>{{ $provider->type ?? 'N/A' }}</td>
|
||||
<td>
|
||||
<span class="badge bg-{{ $provider->is_active ? 'success' : 'secondary' }}">
|
||||
{{ $provider->is_active ? 'Ativo' : 'Inativo' }}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<a href="{{ route('ai-providers.show', $provider) }}" class="btn btn-info btn-sm">
|
||||
<i class="bi bi-eye"></i>
|
||||
</a>
|
||||
<a href="{{ route('ai-providers.edit', $provider) }}" class="btn btn-warning btn-sm">
|
||||
<i class="bi bi-pencil"></i>
|
||||
</a>
|
||||
<form action="{{ route('ai-providers.destroy', $provider) }}" method="POST" class="d-inline" onsubmit="return confirm('Tem certeza?')">
|
||||
@csrf @method('DELETE')
|
||||
<button class="btn btn-danger btn-sm"><i class="bi bi-trash"></i></button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@if(is_object($providers) && method_exists($providers, 'links'))
|
||||
<div class="mt-3">
|
||||
{{ $providers->links() }}
|
||||
</div>
|
||||
@endif
|
||||
@else
|
||||
<p class="text-muted">Nenhum provedor encontrado.</p>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
Executable
+51
@@ -0,0 +1,51 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('header', $aiProvider->name)
|
||||
|
||||
@section('breadcrumbs')
|
||||
@php
|
||||
$breadcrumbs = [
|
||||
'Provedores' => route('ai-providers.index'),
|
||||
$aiProvider->name => null
|
||||
];
|
||||
@endphp
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
<div class="card shadow mb-4">
|
||||
<div class="card-header py-3 d-flex justify-content-between align-items-center">
|
||||
<h6 class="m-0 fw-bold text-primary">Detalhes do Provedor</h6>
|
||||
<div>
|
||||
<a href="{{ route('ai-providers.edit', $aiProvider) }}" class="btn btn-warning btn-sm me-2">
|
||||
<i class="bi bi-pencil"></i> Editar
|
||||
</a>
|
||||
<form action="{{ route('ai-providers.destroy', $aiProvider) }}" method="POST" class="d-inline" onsubmit="return confirm('Tem certeza?')">
|
||||
@csrf @method('DELETE')
|
||||
<button class="btn btn-danger btn-sm"><i class="bi bi-trash"></i> Excluir</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<dl class="row">
|
||||
<dt class="col-sm-3">Nome</dt>
|
||||
<dd class="col-sm-9">{{ $aiProvider->name }}</dd>
|
||||
|
||||
<dt class="col-sm-3">Descrição</dt>
|
||||
<dd class="col-sm-9">{{ $aiProvider->description ?: '-' }}</dd>
|
||||
|
||||
<dt class="col-sm-3">Base URL</dt>
|
||||
<dd class="col-sm-9">{{ $aiProvider->base_url }}</dd>
|
||||
|
||||
<dt class="col-sm-3">Status</dt>
|
||||
<dd class="col-sm-9">
|
||||
<span class="badge bg-{{ $aiProvider->is_active ? 'success' : 'secondary' }}">
|
||||
{{ $aiProvider->is_active ? 'Ativo' : 'Inativo' }}
|
||||
</span>
|
||||
</dd>
|
||||
|
||||
<dt class="col-sm-3">Criado em</dt>
|
||||
<dd class="col-sm-9">{{ $aiProvider->created_at->format('d/m/Y H:i') }}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
+27
@@ -0,0 +1,27 @@
|
||||
<x-guest-layout>
|
||||
<div class="mb-4 text-sm text-gray-600">
|
||||
{{ __('This is a secure area of the application. Please confirm your password before continuing.') }}
|
||||
</div>
|
||||
|
||||
<form method="POST" action="{{ route('password.confirm') }}">
|
||||
@csrf
|
||||
|
||||
<!-- Password -->
|
||||
<div>
|
||||
<x-input-label for="password" :value="__('Password')" />
|
||||
|
||||
<x-text-input id="password" class="block mt-1 w-full"
|
||||
type="password"
|
||||
name="password"
|
||||
required autocomplete="current-password" />
|
||||
|
||||
<x-input-error :messages="$errors->get('password')" class="mt-2" />
|
||||
</div>
|
||||
|
||||
<div class="flex justify-end mt-4">
|
||||
<x-primary-button>
|
||||
{{ __('Confirm') }}
|
||||
</x-primary-button>
|
||||
</div>
|
||||
</form>
|
||||
</x-guest-layout>
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||
<title>Recuperar Senha - GPrompt</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body { font-family: 'Roboto', sans-serif; background-color: #f8f9fc; }
|
||||
.forgot-container { min-height: 100vh; display: flex; align-items: center; justify-content: center; }
|
||||
.forgot-card { border: none; box-shadow: 0 2px 15px rgba(0,0,0,0.1); border-radius: 10px; width: 100%; max-width: 400px; }
|
||||
.forgot-header { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border-radius: 10px 10px 0 0; padding: 2rem; text-align: center; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="forgot-container d-flex flex-column">
|
||||
<div class="forgot-card flex-grow-0">
|
||||
<div class="forgot-header">
|
||||
<i class="bi bi-key" style="font-size: 3rem;"></i>
|
||||
<h2 class="mt-2">Recuperar Senha</h2>
|
||||
<p>Informe seu e-mail para receber o link</p>
|
||||
</div>
|
||||
<div class="card-body p-4">
|
||||
@if(session('status'))
|
||||
<div class="alert alert-success">{{ session('status') }}</div>
|
||||
@endif
|
||||
@if(session('error'))
|
||||
<div class="alert alert-danger">{{ session('error') }}</div>
|
||||
@endif
|
||||
<form method="POST" action="{{ route('password.email') }}">
|
||||
@csrf
|
||||
<div class="mb-3">
|
||||
<label for="email" class="form-label">E-mail</label>
|
||||
<input type="email" class="form-control" id="email" name="email" value="{{ old('email') }}" required autofocus autocomplete="email">
|
||||
@error('email') <div class="text-danger small">{{ $message }}</div> @enderror
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary w-100">Enviar Link</button>
|
||||
</form>
|
||||
<div class="mt-3 text-center">
|
||||
<a href="{{ route('login') }}"><i class="bi bi-arrow-left"></i> Voltar para login</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@include('components.app-footer')
|
||||
</div>
|
||||
|
||||
@include('components.cookie-consent-banner')
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
Executable
+125
@@ -0,0 +1,125 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||
<title>Login - GPrompt</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body { font-family: 'Roboto', sans-serif; background-color: #f8f9fc; }
|
||||
.login-container { min-height: 100vh; display: flex; align-items: center; justify-content: center; }
|
||||
.login-card { border: none; box-shadow: 0 2px 15px rgba(0,0,0,0.1); border-radius: 10px; width: 100%; max-width: 400px; }
|
||||
.login-header { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border-radius: 10px 10px 0 0; padding: 2rem; text-align: center; }
|
||||
.password-toggle { cursor: pointer; position: absolute; right: 10px; top: 38px; }
|
||||
.captcha-refresh { cursor: pointer; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="login-container d-flex flex-column">
|
||||
<div class="login-card flex-grow-0">
|
||||
<div class="login-header">
|
||||
<i class="bi bi-cpu" style="font-size: 3rem;"></i>
|
||||
<h2 class="mt-2">GPrompt</h2>
|
||||
<p>Faça login para continuar</p>
|
||||
</div>
|
||||
<div class="card-body p-4">
|
||||
@if(session('error'))
|
||||
<div class="alert alert-danger">{{ session('error') }}</div>
|
||||
@endif
|
||||
@if(session('success'))
|
||||
<div class="alert alert-success">{{ session('success') }}</div>
|
||||
@endif
|
||||
|
||||
<form method="POST" action="{{ route('login') }}">
|
||||
@csrf
|
||||
<div class="mb-3">
|
||||
<label for="email" class="form-label">E-mail</label>
|
||||
<input type="email" class="form-control" id="email" name="email" value="{{ old('email') }}" required autofocus autocomplete="username">
|
||||
@error('email') <div class="text-danger small">{{ $message }}</div> @enderror
|
||||
</div>
|
||||
<div class="mb-3 position-relative">
|
||||
<label for="password" class="form-label">Senha</label>
|
||||
<input type="password" class="form-control" id="password" name="password" required autocomplete="current-password">
|
||||
<i class="bi bi-eye-slash password-toggle" id="togglePassword"></i>
|
||||
@error('password') <div class="text-danger small">{{ $message }}</div> @enderror
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="captcha" class="form-label">Captcha</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text" id="captchaQuestion">Carregando...</span>
|
||||
<input type="text" class="form-control" id="captcha" name="captcha" required maxlength="4">
|
||||
<button type="button" class="btn btn-outline-secondary captcha-refresh" id="refreshCaptcha">
|
||||
<i class="bi bi-arrow-clockwise"></i>
|
||||
</button>
|
||||
</div>
|
||||
@error('captcha') <div class="text-danger small">{{ $message }}</div> @enderror
|
||||
</div>
|
||||
<div class="mb-3 form-check">
|
||||
<input type="checkbox" class="form-check-input" id="remember_me" name="remember">
|
||||
<label class="form-check-label" for="remember_me">Lembrar de mim</label>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary w-100">Entrar</button>
|
||||
</form>
|
||||
<div class="mt-3 text-center">
|
||||
<a href="{{ route('password.request') }}">Esqueceu a senha?</a>
|
||||
</div>
|
||||
@if(Route::has('register'))
|
||||
<div class="mt-3 text-center">
|
||||
<span>Não tem conta? <a href="{{ route('register') }}">Cadastre-se</a></span>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@include('components.app-footer')
|
||||
</div>
|
||||
|
||||
@include('components.cookie-consent-banner')
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script>
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
initLogin();
|
||||
});
|
||||
} else {
|
||||
initLogin();
|
||||
}
|
||||
|
||||
function initLogin() {
|
||||
const toggle = document.getElementById('togglePassword');
|
||||
const input = document.getElementById('password');
|
||||
if (toggle && input) {
|
||||
toggle.addEventListener('click', function() {
|
||||
const type = input.getAttribute('type') === 'password' ? 'text' : 'password';
|
||||
input.setAttribute('type', type);
|
||||
toggle.classList.toggle('bi-eye');
|
||||
toggle.classList.toggle('bi-eye-slash');
|
||||
});
|
||||
}
|
||||
|
||||
function loadCaptcha() {
|
||||
const question = document.getElementById('captchaQuestion');
|
||||
fetch('{{ route('captcha.generate') }}')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (question) question.textContent = data.expression;
|
||||
})
|
||||
.catch(() => {
|
||||
if (question) question.textContent = 'Erro ao carregar captcha.';
|
||||
});
|
||||
}
|
||||
|
||||
const refresh = document.getElementById('refreshCaptcha');
|
||||
if (refresh) {
|
||||
refresh.addEventListener('click', loadCaptcha);
|
||||
}
|
||||
|
||||
loadCaptcha();
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Executable
+138
@@ -0,0 +1,138 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||
<title>Cadastro - GPrompt</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body { font-family: 'Roboto', sans-serif; background-color: #f8f9fc; }
|
||||
.register-container { min-height: 100vh; display: flex; align-items: center; justify-content: center; }
|
||||
.register-card { border: none; box-shadow: 0 2px 15px rgba(0,0,0,0.1); border-radius: 10px; width: 100%; max-width: 400px; }
|
||||
.register-header { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border-radius: 10px 10px 0 0; padding: 2rem; text-align: center; }
|
||||
.password-toggle { cursor: pointer; position: absolute; right: 10px; top: 38px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="register-container d-flex flex-column">
|
||||
<div class="register-card flex-grow-0">
|
||||
<div class="register-header">
|
||||
<i class="bi bi-cpu" style="font-size: 3rem;"></i>
|
||||
<h2 class="mt-2">Criar Conta</h2>
|
||||
<p>Junte-se ao GPrompt</p>
|
||||
</div>
|
||||
<div class="card-body p-4">
|
||||
@if(session('error'))
|
||||
<div class="alert alert-danger">{{ session('error') }}</div>
|
||||
@endif
|
||||
<form method="POST" action="{{ route('register') }}">
|
||||
@csrf
|
||||
<div class="mb-3">
|
||||
<label for="name" class="form-label">Nome</label>
|
||||
<input type="text" class="form-control" id="name" name="name" value="{{ old('name') }}" required autofocus autocomplete="name">
|
||||
@error('name') <div class="text-danger small">{{ $message }}</div> @enderror
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="email" class="form-label">E-mail</label>
|
||||
<input type="email" class="form-control" id="email" name="email" value="{{ old('email') }}" required autocomplete="email">
|
||||
@error('email') <div class="text-danger small">{{ $message }}</div> @enderror
|
||||
</div>
|
||||
<div class="mb-3 position-relative">
|
||||
<label for="password" class="form-label">Senha</label>
|
||||
<input type="password" class="form-control" id="password" name="password" required autocomplete="new-password">
|
||||
<i class="bi bi-eye-slash password-toggle" id="togglePassword"></i>
|
||||
@error('password') <div class="text-danger small">{{ $message }}</div> @enderror
|
||||
</div>
|
||||
<div class="mb-3 position-relative">
|
||||
<label for="password_confirmation" class="form-label">Confirmar Senha</label>
|
||||
<input type="password" class="form-control" id="password_confirmation" name="password_confirmation" required autocomplete="new-password">
|
||||
<i class="bi bi-eye-slash password-toggle" id="toggleConfirmPassword"></i>
|
||||
@error('password_confirmation') <div class="text-danger small">{{ $message }}</div> @enderror
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="captcha" class="form-label">Captcha</label>
|
||||
<div class="input-group">
|
||||
<span class="input-group-text" id="captchaQuestion">Carregando...</span>
|
||||
<input type="text" class="form-control" id="captcha" name="captcha" required maxlength="4">
|
||||
<button type="button" class="btn btn-outline-secondary captcha-refresh" id="refreshCaptcha">
|
||||
<i class="bi bi-arrow-clockwise"></i>
|
||||
</button>
|
||||
</div>
|
||||
@error('captcha') <div class="text-danger small">{{ $message }}</div> @enderror
|
||||
</div>
|
||||
<div class="mb-3 form-check">
|
||||
<input type="checkbox" class="form-check-input" id="terms_of_service" name="terms_of_service" required>
|
||||
<label class="form-check-label" for="terms_of_service">
|
||||
@lang('auth.accept_terms') <a href="{{ route('legal.document.show', 'terms-of-service') }}" target="_blank">@lang('legal_documents.terms_of_service')</a>
|
||||
</label>
|
||||
</div>
|
||||
<div class="mb-3 form-check">
|
||||
<input type="checkbox" class="form-check-input" id="privacy_policy" name="privacy_policy" required>
|
||||
<label class="form-check-label" for="privacy_policy">
|
||||
@lang('auth.accept_privacy') <a href="{{ route('legal.document.show', 'privacy-policy') }}" target="_blank">@lang('legal_documents.privacy_policy')</a>
|
||||
</label>
|
||||
</div>
|
||||
<div class="mb-3 form-check">
|
||||
<input type="checkbox" class="form-check-input" id="terms" name="terms" required>
|
||||
<label class="form-check-label" for="terms">Conta sujeita a aprovação do SuperAdmin.</label>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary w-100">Cadastrar</button>
|
||||
</form>
|
||||
<div class="mt-3 text-center">
|
||||
<span>Já tem conta? <a href="{{ route('login') }}">Faça login</a></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@include('components.app-footer')
|
||||
</div>
|
||||
|
||||
@include('components.cookie-consent-banner')
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script>
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
initRegister();
|
||||
});
|
||||
} else {
|
||||
initRegister();
|
||||
}
|
||||
|
||||
function initRegister() {
|
||||
const toggles = document.querySelectorAll('.password-toggle');
|
||||
toggles.forEach(toggle => {
|
||||
toggle.addEventListener('click', function() {
|
||||
const input = this.previousElementSibling;
|
||||
const type = input.getAttribute('type') === 'password' ? 'text' : 'password';
|
||||
input.setAttribute('type', type);
|
||||
this.classList.toggle('bi-eye');
|
||||
this.classList.toggle('bi-eye-slash');
|
||||
});
|
||||
});
|
||||
|
||||
function loadCaptcha() {
|
||||
const question = document.getElementById('captchaQuestion');
|
||||
fetch('{{ route('captcha.generate') }}')
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (question) question.textContent = data.expression;
|
||||
})
|
||||
.catch(() => {
|
||||
if (question) question.textContent = 'Erro ao carregar captcha.';
|
||||
});
|
||||
}
|
||||
|
||||
const refresh = document.getElementById('refreshCaptcha');
|
||||
if (refresh) {
|
||||
refresh.addEventListener('click', loadCaptcha);
|
||||
}
|
||||
|
||||
loadCaptcha();
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||
<title>Redefinir Senha - GPrompt</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body { font-family: 'Roboto', sans-serif; background-color: #f8f9fc; }
|
||||
.reset-container { min-height: 100vh; display: flex; align-items: center; justify-content: center; }
|
||||
.reset-card { border: none; box-shadow: 0 2px 15px rgba(0,0,0,0.1); border-radius: 10px; width: 100%; max-width: 400px; }
|
||||
.reset-header { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; border-radius: 10px 10px 0 0; padding: 2rem; text-align: center; }
|
||||
.password-toggle { cursor: pointer; position: absolute; right: 10px; top: 38px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="reset-container d-flex flex-column">
|
||||
<div class="reset-card flex-grow-0">
|
||||
<div class="reset-header">
|
||||
<i class="bi bi-shield-lock" style="font-size: 3rem;"></i>
|
||||
<h2 class="mt-2">Redefinir Senha</h2>
|
||||
<p>Digite sua nova senha</p>
|
||||
</div>
|
||||
<div class="card-body p-4">
|
||||
<form method="POST" action="{{ route('password.update') }}">
|
||||
@csrf
|
||||
<input type="hidden" name="token" value="{{ $token ?? request()->route('token') }}">
|
||||
<div class="mb-3">
|
||||
<label for="email" class="form-label">E-mail</label>
|
||||
<input type="email" class="form-control" id="email" name="email" value="{{ old('email', $email ?? request()->email) }}" required autofocus autocomplete="email">
|
||||
@error('email') <div class="text-danger small">{{ $message }}</div> @enderror
|
||||
</div>
|
||||
<div class="mb-3 position-relative">
|
||||
<label for="password" class="form-label">Nova Senha</label>
|
||||
<input type="password" class="form-control" id="password" name="password" required autocomplete="new-password">
|
||||
<i class="bi bi-eye-slash password-toggle" id="togglePassword"></i>
|
||||
@error('password') <div class="text-danger small">{{ $message }}</div> @enderror
|
||||
</div>
|
||||
<div class="mb-3 position-relative">
|
||||
<label for="password_confirmation" class="form-label">Confirmar Nova Senha</label>
|
||||
<input type="password" class="form-control" id="password_confirmation" name="password_confirmation" required autocomplete="new-password">
|
||||
<i class="bi bi-eye-slash password-toggle" id="toggleConfirmPassword"></i>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary w-100">Redefinir Senha</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@include('components.app-footer')
|
||||
</div>
|
||||
|
||||
@include('components.cookie-consent-banner')
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||
<script>
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
initResetPassword();
|
||||
});
|
||||
} else {
|
||||
initResetPassword();
|
||||
}
|
||||
|
||||
function initResetPassword() {
|
||||
const toggles = document.querySelectorAll('.password-toggle');
|
||||
toggles.forEach(toggle => {
|
||||
toggle.addEventListener('click', function() {
|
||||
const input = this.previousElementSibling;
|
||||
const type = input.getAttribute('type') === 'password' ? 'text' : 'password';
|
||||
input.setAttribute('type', type);
|
||||
this.classList.toggle('bi-eye');
|
||||
this.classList.toggle('bi-eye-slash');
|
||||
});
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Executable
+31
@@ -0,0 +1,31 @@
|
||||
<x-guest-layout>
|
||||
<div class="mb-4 text-sm text-gray-600">
|
||||
{{ __('Thanks for signing up! Before getting started, could you verify your email address by clicking on the link we just emailed to you? If you didn\'t receive the email, we will gladly send you another.') }}
|
||||
</div>
|
||||
|
||||
@if (session('status') == 'verification-link-sent')
|
||||
<div class="mb-4 font-medium text-sm text-green-600">
|
||||
{{ __('A new verification link has been sent to the email address you provided during registration.') }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="mt-4 flex items-center justify-between">
|
||||
<form method="POST" action="{{ route('verification.send') }}">
|
||||
@csrf
|
||||
|
||||
<div>
|
||||
<x-primary-button>
|
||||
{{ __('Resend Verification Email') }}
|
||||
</x-primary-button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<form method="POST" action="{{ route('logout') }}">
|
||||
@csrf
|
||||
|
||||
<button type="submit" class="underline text-sm text-gray-600 hover:text-gray-900 rounded-md focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500">
|
||||
{{ __('Log Out') }}
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</x-guest-layout>
|
||||
Executable
+186
@@ -0,0 +1,186 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('header', 'Chat com IA')
|
||||
|
||||
@section('breadcrumbs')
|
||||
@php
|
||||
$breadcrumbs = [
|
||||
'Chat com IA' => null
|
||||
];
|
||||
@endphp
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="card shadow mb-4">
|
||||
<div class="card-header py-3 d-flex justify-content-between align-items-center">
|
||||
<h6 class="m-0 fw-bold text-primary">Conversa</h6>
|
||||
<div>
|
||||
<form method="POST" action="{{ route('chat.reset') }}" class="d-inline" onsubmit="return confirm('Tem certeza que deseja reiniciar a conversa?')">
|
||||
@csrf
|
||||
<button type="submit" class="btn btn-sm btn-outline-secondary me-2">
|
||||
<i class="bi bi-arrow-clockwise me-1"></i> Reiniciar
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div id="chat-messages" class="border rounded p-3 mb-3 bg-light" style="min-height: 400px; max-height: 600px; overflow-y: auto;">
|
||||
@forelse($history as $msg)
|
||||
<div class="mb-3 {{ $msg['role'] === 'user' ? 'text-end' : 'text-start' }}">
|
||||
<div class="d-inline-block p-2 rounded {{ $msg['role'] === 'user' ? 'bg-primary text-white' : 'bg-white border' }}" style="max-width: 80%; white-space: pre-wrap;">
|
||||
{{ $msg['content'] }}
|
||||
</div>
|
||||
</div>
|
||||
@empty
|
||||
<p class="text-muted text-center">Nenhuma mensagem ainda. Inicie a conversa!</p>
|
||||
@endforelse
|
||||
</div>
|
||||
|
||||
<form id="chat-form" class="mt-3">
|
||||
@csrf
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-6 mb-2">
|
||||
<label for="provider_id" class="form-label">Provider</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', $defaultProviderId) == $provider->id ? 'selected' : '' }}>
|
||||
{{ $provider->name }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-md-6 mb-2">
|
||||
<label for="model_id" class="form-label">Modelo</label>
|
||||
<select class="form-select" id="model_id" name="model_id" required disabled>
|
||||
<option value="">-- Selecione um provider primeiro --</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row mb-3">
|
||||
<div class="col-md-4 mb-2">
|
||||
<label for="temperature" class="form-label">Temperatura</label>
|
||||
<input type="number" class="form-control" id="temperature" name="temperature" value="{{ old('temperature', 0.7) }}" step="0.1" min="0" max="1">
|
||||
</div>
|
||||
<div class="col-md-4 mb-2">
|
||||
<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', 8192) }}" min="1" max="128000">
|
||||
</div>
|
||||
</div>
|
||||
<div class="input-group">
|
||||
<textarea class="form-control" id="message" name="message" rows="1" placeholder="Digite sua mensagem..." required></textarea>
|
||||
<button class="btn btn-primary" type="submit" id="send-btn">
|
||||
<i class="bi bi-send"></i> Enviar
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@section('scripts')
|
||||
<script>
|
||||
const providerSelect = document.getElementById('provider_id');
|
||||
const modelSelect = document.getElementById('model_id');
|
||||
const chatForm = document.getElementById('chat-form');
|
||||
const chatMessages = document.getElementById('chat-messages');
|
||||
const sendBtn = document.getElementById('send-btn');
|
||||
const messageInput = document.getElementById('message');
|
||||
|
||||
providerSelect.addEventListener('change', function() {
|
||||
const providerId = this.value;
|
||||
|
||||
if (!providerId) {
|
||||
modelSelect.innerHTML = '<option value="">-- Selecione um provider primeiro --</option>';
|
||||
modelSelect.disabled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
fetch(`{{ route('chat.models') }}?provider_id=${providerId}`)
|
||||
.then(response => response.json())
|
||||
.then(models => {
|
||||
modelSelect.innerHTML = '<option value="">-- Selecione um modelo --</option>';
|
||||
models.forEach(model => {
|
||||
const option = document.createElement('option');
|
||||
option.value = model.id;
|
||||
option.textContent = model.name;
|
||||
modelSelect.appendChild(option);
|
||||
});
|
||||
modelSelect.disabled = false;
|
||||
});
|
||||
});
|
||||
|
||||
chatForm.addEventListener('submit', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
const message = messageInput.value.trim();
|
||||
if (!message) return;
|
||||
|
||||
appendMessage('user', message);
|
||||
messageInput.value = '';
|
||||
sendBtn.disabled = true;
|
||||
|
||||
const formData = new FormData();
|
||||
formData.append('_token', '{{ csrf_token() }}');
|
||||
formData.append('message', message);
|
||||
formData.append('provider_id', providerSelect.value);
|
||||
formData.append('model_id', modelSelect.value);
|
||||
formData.append('temperature', document.getElementById('temperature').value);
|
||||
formData.append('max_tokens', document.getElementById('max_tokens').value);
|
||||
|
||||
fetch('{{ route('chat.send') }}', {
|
||||
method: 'POST',
|
||||
body: formData,
|
||||
})
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
if (data.success) {
|
||||
appendMessage('assistant', data.content);
|
||||
} else {
|
||||
appendMessage('assistant', 'Erro: ' + data.error);
|
||||
}
|
||||
sendBtn.disabled = false;
|
||||
})
|
||||
.catch(() => {
|
||||
appendMessage('assistant', 'Erro ao enviar mensagem.');
|
||||
sendBtn.disabled = false;
|
||||
});
|
||||
});
|
||||
|
||||
function appendMessage(role, content) {
|
||||
const div = document.createElement('div');
|
||||
div.className = 'mb-3 ' + (role === 'user' ? 'text-end' : 'text-start');
|
||||
div.innerHTML = '<div class="d-inline-block p-2 rounded ' + (role === 'user' ? 'bg-primary text-white' : 'bg-white border') + '" style="max-width: 80%; white-space: pre-wrap;">' + escapeHtml(content) + '</div>';
|
||||
chatMessages.appendChild(div);
|
||||
chatMessages.scrollTop = chatMessages.scrollHeight;
|
||||
}
|
||||
|
||||
function escapeHtml(text) {
|
||||
const map = { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' };
|
||||
return text.replace(/[&<>"']/g, m => map[m]);
|
||||
}
|
||||
|
||||
@if(!empty($history) && old('provider_id'))
|
||||
window.addEventListener('DOMContentLoaded', function() {
|
||||
providerSelect.value = {{ old('provider_id', 1) }};
|
||||
providerSelect.dispatchEvent(new Event('change'));
|
||||
setTimeout(() => {
|
||||
modelSelect.value = {{ old('model_id') }};
|
||||
}, 500);
|
||||
});
|
||||
@else
|
||||
window.addEventListener('DOMContentLoaded', function() {
|
||||
if (providerSelect.value) {
|
||||
providerSelect.dispatchEvent(new Event('change'));
|
||||
setTimeout(() => {
|
||||
modelSelect.value = {{ $defaultModelId }};
|
||||
}, 500);
|
||||
}
|
||||
});
|
||||
@endif
|
||||
</script>
|
||||
@endsection
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
<footer class="app-footer bg-dark text-white py-3">
|
||||
<div class="container">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-md-6 text-center text-md-start mb-2 mb-md-0">
|
||||
<span class="small">© {{ date('Y') }} GPrompt. @lang('footer.copyright') - @lang('footer.powered_by') <a href="https://www.syspanel.com.br" target="_blank" rel="noopener">Syspanel</a></span>
|
||||
</div>
|
||||
<div class="col-md-6 text-center text-md-end">
|
||||
<a href="{{ route('legal.document.show', 'terms-of-service') }}" class="text-white text-decoration-none small me-3">@lang('footer.terms_of_service')</a>
|
||||
<a href="{{ route('legal.document.show', 'privacy-policy') }}" class="text-white text-decoration-none small me-3">@lang('footer.privacy_policy')</a>
|
||||
<a href="javascript:void(0)" onclick="cookieConsent.showPreferences()" class="text-white text-decoration-none small">@lang('footer.cookie_preferences')</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
@@ -0,0 +1,3 @@
|
||||
<svg viewBox="0 0 316 316" xmlns="http://www.w3.org/2000/svg" {{ $attributes }}>
|
||||
<path d="M305.8 81.125C305.77 80.995 305.69 80.885 305.65 80.755C305.56 80.525 305.49 80.285 305.37 80.075C305.29 79.935 305.17 79.815 305.07 79.685C304.94 79.515 304.83 79.325 304.68 79.175C304.55 79.045 304.39 78.955 304.25 78.845C304.09 78.715 303.95 78.575 303.77 78.475L251.32 48.275C249.97 47.495 248.31 47.495 246.96 48.275L194.51 78.475C194.33 78.575 194.19 78.725 194.03 78.845C193.89 78.955 193.73 79.045 193.6 79.175C193.45 79.325 193.34 79.515 193.21 79.685C193.11 79.815 192.99 79.935 192.91 80.075C192.79 80.285 192.71 80.525 192.63 80.755C192.58 80.875 192.51 80.995 192.48 81.125C192.38 81.495 192.33 81.875 192.33 82.265V139.625L148.62 164.795V52.575C148.62 52.185 148.57 51.805 148.47 51.435C148.44 51.305 148.36 51.195 148.32 51.065C148.23 50.835 148.16 50.595 148.04 50.385C147.96 50.245 147.84 50.125 147.74 49.995C147.61 49.825 147.5 49.635 147.35 49.485C147.22 49.355 147.06 49.265 146.92 49.155C146.76 49.025 146.62 48.885 146.44 48.785L93.99 18.585C92.64 17.805 90.98 17.805 89.63 18.585L37.18 48.785C37 48.885 36.86 49.035 36.7 49.155C36.56 49.265 36.4 49.355 36.27 49.485C36.12 49.635 36.01 49.825 35.88 49.995C35.78 50.125 35.66 50.245 35.58 50.385C35.46 50.595 35.38 50.835 35.3 51.065C35.25 51.185 35.18 51.305 35.15 51.435C35.05 51.805 35 52.185 35 52.575V232.235C35 233.795 35.84 235.245 37.19 236.025L142.1 296.425C142.33 296.555 142.58 296.635 142.82 296.725C142.93 296.765 143.04 296.835 143.16 296.865C143.53 296.965 143.9 297.015 144.28 297.015C144.66 297.015 145.03 296.965 145.4 296.865C145.5 296.835 145.59 296.775 145.69 296.745C145.95 296.655 146.21 296.565 146.45 296.435L251.36 236.035C252.72 235.255 253.55 233.815 253.55 232.245V174.885L303.81 145.945C305.17 145.165 306 143.725 306 142.155V82.265C305.95 81.875 305.89 81.495 305.8 81.125ZM144.2 227.205L100.57 202.515L146.39 176.135L196.66 147.195L240.33 172.335L208.29 190.625L144.2 227.205ZM244.75 114.995V164.795L226.39 154.225L201.03 139.625V89.825L219.39 100.395L244.75 114.995ZM249.12 57.105L292.81 82.265L249.12 107.425L205.43 82.265L249.12 57.105ZM114.49 184.425L96.13 194.995V85.305L121.49 70.705L139.85 60.135V169.815L114.49 184.425ZM91.76 27.425L135.45 52.585L91.76 77.745L48.07 52.585L91.76 27.425ZM43.67 60.135L62.03 70.705L87.39 85.305V202.545V202.555V202.565C87.39 202.735 87.44 202.895 87.46 203.055C87.49 203.265 87.49 203.485 87.55 203.695V203.705C87.6 203.875 87.69 204.035 87.76 204.195C87.84 204.375 87.89 204.575 87.99 204.745C87.99 204.745 87.99 204.755 88 204.755C88.09 204.905 88.22 205.035 88.33 205.175C88.45 205.335 88.55 205.495 88.69 205.635L88.7 205.645C88.82 205.765 88.98 205.855 89.12 205.965C89.28 206.085 89.42 206.225 89.59 206.325C89.6 206.325 89.6 206.325 89.61 206.335C89.62 206.335 89.62 206.345 89.63 206.345L139.87 234.775V285.065L43.67 229.705V60.135ZM244.75 229.705L148.58 285.075V234.775L219.8 194.115L244.75 179.875V229.705ZM297.2 139.625L253.49 164.795V114.995L278.85 100.395L297.21 89.825V139.625H297.2Z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.0 KiB |
@@ -0,0 +1,7 @@
|
||||
@props(['status'])
|
||||
|
||||
@if ($status)
|
||||
<div {{ $attributes->merge(['class' => 'font-medium text-sm text-green-600']) }}>
|
||||
{{ $status }}
|
||||
</div>
|
||||
@endif
|
||||
+226
@@ -0,0 +1,226 @@
|
||||
<div id="cookieConsentBanner" class="cookie-consent-banner" style="display: none;">
|
||||
<div class="cookie-consent-card shadow-lg">
|
||||
<div class="cookie-consent-icon">
|
||||
<i class="bi bi-shield-lock"></i>
|
||||
</div>
|
||||
<div class="cookie-consent-body">
|
||||
<h6 class="fw-bold mb-2">@lang('cookie_consent.title')</h6>
|
||||
<p class="mb-3 text-muted small">
|
||||
@lang('cookie_consent.description')
|
||||
<a href="{{ route('legal.document.show', 'privacy-policy') }}" class="text-decoration-none">@lang('cookie_consent.privacy_policy')</a>.
|
||||
</p>
|
||||
<div class="d-flex flex-wrap gap-2">
|
||||
<button type="button" class="btn btn-outline-dark btn-sm cookie-btn-customize" onclick="cookieConsent.showPreferences()">
|
||||
<i class="bi bi-sliders me-1"></i> @lang('cookie_consent.customize')
|
||||
</button>
|
||||
<button type="button" class="btn btn-outline-secondary btn-sm cookie-btn-reject" onclick="cookieConsent.reject()">
|
||||
@lang('cookie_consent.reject')
|
||||
</button>
|
||||
<button type="button" class="btn btn-primary btn-sm cookie-btn-accept" onclick="cookieConsent.acceptAll()">
|
||||
<i class="bi bi-check-lg me-1"></i> @lang('cookie_consent.accept_all')
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="cookieConsentPreferences" class="modal fade" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog modal-dialog-centered">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header border-0 pb-0">
|
||||
<h5 class="modal-title fw-bold">
|
||||
<i class="bi bi-shield-check me-2 text-primary"></i>@lang('cookie_consent.customize')
|
||||
</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Close"></button>
|
||||
</div>
|
||||
<div class="modal-body pt-2">
|
||||
<p class="small text-muted mb-4">@lang('cookie_consent.select_types')</p>
|
||||
|
||||
<div class="cookie-category mb-3 p-3 rounded bg-light">
|
||||
<div class="form-check mb-0">
|
||||
<input class="form-check-input" type="checkbox" id="cookieNecessary" checked disabled>
|
||||
<label class="form-check-label fw-semibold" for="cookieNecessary">
|
||||
<i class="bi bi-lock-fill me-2 text-success"></i>@lang('cookie_consent.necessary')
|
||||
</label>
|
||||
<span class="d-block small text-muted mt-1 ms-4">@lang('cookie_consent.necessary_description')</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="cookie-category mb-3 p-3 rounded bg-light">
|
||||
<div class="form-check mb-0">
|
||||
<input class="form-check-input" type="checkbox" id="cookieStatistics" value="statistics">
|
||||
<label class="form-check-label fw-semibold" for="cookieStatistics">
|
||||
<i class="bi bi-bar-chart-fill me-2 text-info"></i>@lang('cookie_consent.statistics')
|
||||
</label>
|
||||
<span class="d-block small text-muted mt-1 ms-4">@lang('cookie_consent.statistics_description')</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="cookie-category mb-3 p-3 rounded bg-light">
|
||||
<div class="form-check mb-0">
|
||||
<input class="form-check-input" type="checkbox" id="cookieMarketing" value="marketing">
|
||||
<label class="form-check-label fw-semibold" for="cookieMarketing">
|
||||
<i class="bi bi-megaphone-fill me-2 text-warning"></i>@lang('cookie_consent.marketing')
|
||||
</label>
|
||||
<span class="d-block small text-muted mt-1 ms-4">@lang('cookie_consent.marketing_description')</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="cookie-category p-3 rounded bg-light">
|
||||
<div class="form-check mb-0">
|
||||
<input class="form-check-input" type="checkbox" id="cookiePreferences" value="preferences">
|
||||
<label class="form-check-label fw-semibold" for="cookiePreferences">
|
||||
<i class="bi bi-gear-fill me-2 text-secondary"></i>@lang('cookie_consent.preferences')
|
||||
</label>
|
||||
<span class="d-block small text-muted mt-1 ms-4">@lang('cookie_consent.preferences_description')</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer border-0 pt-0">
|
||||
<button type="button" class="btn btn-light btn-sm" data-bs-dismiss="modal">@lang('cookie_consent.cancel')</button>
|
||||
<button type="button" class="btn btn-primary btn-sm px-4" onclick="cookieConsent.savePreferences()">
|
||||
<i class="bi bi-check-lg me-1"></i> @lang('cookie_consent.save_preferences')
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<style>
|
||||
.cookie-consent-banner {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 1080;
|
||||
padding: 1rem;
|
||||
animation: slideUp 0.4s ease-out;
|
||||
}
|
||||
|
||||
.cookie-consent-card {
|
||||
background: white;
|
||||
border-radius: 16px;
|
||||
padding: 1.5rem;
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
gap: 1rem;
|
||||
max-width: 900px;
|
||||
margin: 0 auto;
|
||||
border: 1px solid rgba(0,0,0,0.08);
|
||||
}
|
||||
|
||||
.cookie-consent-icon {
|
||||
flex-shrink: 0;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
background: linear-gradient(135deg, #4e73df 0%, #224abe 100%);
|
||||
border-radius: 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: white;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.cookie-consent-body {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.cookie-consent-body h6 {
|
||||
font-size: 1.1rem;
|
||||
color: #1a1a2e;
|
||||
}
|
||||
|
||||
.cookie-consent-body p {
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.6;
|
||||
color: #6c757d;
|
||||
}
|
||||
|
||||
.cookie-consent-actions {
|
||||
display: flex;
|
||||
gap: 0.5rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.cookie-btn-accept {
|
||||
background: linear-gradient(135deg, #4e73df 0%, #224abe 100%);
|
||||
border: none;
|
||||
padding: 0.5rem 1.25rem;
|
||||
border-radius: 8px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.cookie-btn-accept:hover {
|
||||
background: linear-gradient(135deg, #224abe 0%, #1a3a9e 100%);
|
||||
transform: translateY(-1px);
|
||||
box-shadow: 0 4px 12px rgba(78, 115, 223, 0.3);
|
||||
}
|
||||
|
||||
.cookie-btn-reject {
|
||||
border-radius: 8px;
|
||||
padding: 0.5rem 1.25rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.cookie-btn-customize {
|
||||
border-radius: 8px;
|
||||
padding: 0.5rem 1.25rem;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.cookie-category {
|
||||
transition: all 0.2s;
|
||||
border: 1px solid transparent;
|
||||
}
|
||||
|
||||
.cookie-category:hover {
|
||||
border-color: #dee2e6;
|
||||
background: #f8f9fa !important;
|
||||
}
|
||||
|
||||
.cookie-category .form-check-input:checked {
|
||||
background-color: #4e73df;
|
||||
border-color: #4e73df;
|
||||
}
|
||||
|
||||
.cookie-category .form-check-input:disabled {
|
||||
background-color: #4e73df;
|
||||
border-color: #4e73df;
|
||||
opacity: 0.7;
|
||||
}
|
||||
|
||||
@keyframes slideUp {
|
||||
from {
|
||||
transform: translateY(100%);
|
||||
opacity: 0;
|
||||
}
|
||||
to {
|
||||
transform: translateY(0);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 576px) {
|
||||
.cookie-consent-card {
|
||||
flex-direction: column;
|
||||
padding: 1.25rem;
|
||||
}
|
||||
|
||||
.cookie-consent-icon {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
font-size: 1.25rem;
|
||||
}
|
||||
|
||||
.cookie-consent-actions {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.cookie-consent-actions .btn {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<script src="/js/cookie-consent.js"></script>
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
<button {{ $attributes->merge(['type' => 'submit', 'class' => 'inline-flex items-center px-4 py-2 bg-red-600 border border-transparent rounded-md font-semibold text-xs text-white uppercase tracking-widest hover:bg-red-500 active:bg-red-700 focus:outline-none focus:ring-2 focus:ring-red-500 focus:ring-offset-2 transition ease-in-out duration-150']) }}>
|
||||
{{ $slot }}
|
||||
</button>
|
||||
+1
@@ -0,0 +1 @@
|
||||
<a {{ $attributes->merge(['class' => 'block w-full px-4 py-2 text-start text-sm leading-5 text-gray-700 hover:bg-gray-100 focus:outline-none focus:bg-gray-100 transition duration-150 ease-in-out']) }}>{{ $slot }}</a>
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
@props(['align' => 'right', 'width' => '48', 'contentClasses' => 'py-1 bg-white'])
|
||||
|
||||
@php
|
||||
$alignmentClasses = match ($align) {
|
||||
'left' => 'ltr:origin-top-left rtl:origin-top-right start-0',
|
||||
'top' => 'origin-top',
|
||||
default => 'ltr:origin-top-right rtl:origin-top-left end-0',
|
||||
};
|
||||
|
||||
$width = match ($width) {
|
||||
'48' => 'w-48',
|
||||
default => $width,
|
||||
};
|
||||
@endphp
|
||||
|
||||
<div class="relative" x-data="{ open: false }" @click.outside="open = false" @close.stop="open = false">
|
||||
<div @click="open = ! open">
|
||||
{{ $trigger }}
|
||||
</div>
|
||||
|
||||
<div x-show="open"
|
||||
x-transition:enter="transition ease-out duration-200"
|
||||
x-transition:enter-start="opacity-0 scale-95"
|
||||
x-transition:enter-end="opacity-100 scale-100"
|
||||
x-transition:leave="transition ease-in duration-75"
|
||||
x-transition:leave-start="opacity-100 scale-100"
|
||||
x-transition:leave-end="opacity-0 scale-95"
|
||||
class="absolute z-50 mt-2 {{ $width }} rounded-md shadow-lg {{ $alignmentClasses }}"
|
||||
style="display: none;"
|
||||
@click="open = false">
|
||||
<div class="rounded-md ring-1 ring-black ring-opacity-5 {{ $contentClasses }}">
|
||||
{{ $content }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
@props(['messages'])
|
||||
|
||||
@if ($messages)
|
||||
<ul {{ $attributes->merge(['class' => 'text-sm text-red-600 space-y-1']) }}>
|
||||
@foreach ((array) $messages as $message)
|
||||
<li>{{ $message }}</li>
|
||||
@endforeach
|
||||
</ul>
|
||||
@endif
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
@props(['value'])
|
||||
|
||||
<label {{ $attributes->merge(['class' => 'block font-medium text-sm text-gray-700']) }}>
|
||||
{{ $value ?? $slot }}
|
||||
</label>
|
||||
Executable
+78
@@ -0,0 +1,78 @@
|
||||
@props([
|
||||
'name',
|
||||
'show' => false,
|
||||
'maxWidth' => '2xl'
|
||||
])
|
||||
|
||||
@php
|
||||
$maxWidth = [
|
||||
'sm' => 'sm:max-w-sm',
|
||||
'md' => 'sm:max-w-md',
|
||||
'lg' => 'sm:max-w-lg',
|
||||
'xl' => 'sm:max-w-xl',
|
||||
'2xl' => 'sm:max-w-2xl',
|
||||
][$maxWidth];
|
||||
@endphp
|
||||
|
||||
<div
|
||||
x-data="{
|
||||
show: @js($show),
|
||||
focusables() {
|
||||
// All focusable element types...
|
||||
let selector = 'a, button, input:not([type=\'hidden\']), textarea, select, details, [tabindex]:not([tabindex=\'-1\'])'
|
||||
return [...$el.querySelectorAll(selector)]
|
||||
// All non-disabled elements...
|
||||
.filter(el => ! el.hasAttribute('disabled'))
|
||||
},
|
||||
firstFocusable() { return this.focusables()[0] },
|
||||
lastFocusable() { return this.focusables().slice(-1)[0] },
|
||||
nextFocusable() { return this.focusables()[this.nextFocusableIndex()] || this.firstFocusable() },
|
||||
prevFocusable() { return this.focusables()[this.prevFocusableIndex()] || this.lastFocusable() },
|
||||
nextFocusableIndex() { return (this.focusables().indexOf(document.activeElement) + 1) % (this.focusables().length + 1) },
|
||||
prevFocusableIndex() { return Math.max(0, this.focusables().indexOf(document.activeElement)) -1 },
|
||||
}"
|
||||
x-init="$watch('show', value => {
|
||||
if (value) {
|
||||
document.body.classList.add('overflow-y-hidden');
|
||||
{{ $attributes->has('focusable') ? 'setTimeout(() => firstFocusable().focus(), 100)' : '' }}
|
||||
} else {
|
||||
document.body.classList.remove('overflow-y-hidden');
|
||||
}
|
||||
})"
|
||||
x-on:open-modal.window="$event.detail == '{{ $name }}' ? show = true : null"
|
||||
x-on:close-modal.window="$event.detail == '{{ $name }}' ? show = false : null"
|
||||
x-on:close.stop="show = false"
|
||||
x-on:keydown.escape.window="show = false"
|
||||
x-on:keydown.tab.prevent="$event.shiftKey || nextFocusable().focus()"
|
||||
x-on:keydown.shift.tab.prevent="prevFocusable().focus()"
|
||||
x-show="show"
|
||||
class="fixed inset-0 overflow-y-auto px-4 py-6 sm:px-0 z-50"
|
||||
style="display: {{ $show ? 'block' : 'none' }};"
|
||||
>
|
||||
<div
|
||||
x-show="show"
|
||||
class="fixed inset-0 transform transition-all"
|
||||
x-on:click="show = false"
|
||||
x-transition:enter="ease-out duration-300"
|
||||
x-transition:enter-start="opacity-0"
|
||||
x-transition:enter-end="opacity-100"
|
||||
x-transition:leave="ease-in duration-200"
|
||||
x-transition:leave-start="opacity-100"
|
||||
x-transition:leave-end="opacity-0"
|
||||
>
|
||||
<div class="absolute inset-0 bg-gray-500 opacity-75"></div>
|
||||
</div>
|
||||
|
||||
<div
|
||||
x-show="show"
|
||||
class="mb-6 bg-white rounded-lg overflow-hidden shadow-xl transform transition-all sm:w-full {{ $maxWidth }} sm:mx-auto"
|
||||
x-transition:enter="ease-out duration-300"
|
||||
x-transition:enter-start="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||
x-transition:enter-end="opacity-100 translate-y-0 sm:scale-100"
|
||||
x-transition:leave="ease-in duration-200"
|
||||
x-transition:leave-start="opacity-100 translate-y-0 sm:scale-100"
|
||||
x-transition:leave-end="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
|
||||
>
|
||||
{{ $slot }}
|
||||
</div>
|
||||
</div>
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
@props(['active'])
|
||||
|
||||
@php
|
||||
$classes = ($active ?? false)
|
||||
? 'inline-flex items-center px-1 pt-1 border-b-2 border-indigo-400 text-sm font-medium leading-5 text-gray-900 focus:outline-none focus:border-indigo-700 transition duration-150 ease-in-out'
|
||||
: 'inline-flex items-center px-1 pt-1 border-b-2 border-transparent text-sm font-medium leading-5 text-gray-500 hover:text-gray-700 hover:border-gray-300 focus:outline-none focus:text-gray-700 focus:border-gray-300 transition duration-150 ease-in-out';
|
||||
@endphp
|
||||
|
||||
<a {{ $attributes->merge(['class' => $classes]) }}>
|
||||
{{ $slot }}
|
||||
</a>
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
<button {{ $attributes->merge(['type' => 'submit', 'class' => 'inline-flex items-center px-4 py-2 bg-gray-800 border border-transparent rounded-md font-semibold text-xs text-white uppercase tracking-widest hover:bg-gray-700 focus:bg-gray-700 active:bg-gray-900 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 transition ease-in-out duration-150']) }}>
|
||||
{{ $slot }}
|
||||
</button>
|
||||
@@ -0,0 +1,11 @@
|
||||
@props(['active'])
|
||||
|
||||
@php
|
||||
$classes = ($active ?? false)
|
||||
? 'block w-full ps-3 pe-4 py-2 border-l-4 border-indigo-400 text-start text-base font-medium text-indigo-700 bg-indigo-50 focus:outline-none focus:text-indigo-800 focus:bg-indigo-100 focus:border-indigo-700 transition duration-150 ease-in-out'
|
||||
: 'block w-full ps-3 pe-4 py-2 border-l-4 border-transparent text-start text-base font-medium text-gray-600 hover:text-gray-800 hover:bg-gray-50 hover:border-gray-300 focus:outline-none focus:text-gray-800 focus:bg-gray-50 focus:border-gray-300 transition duration-150 ease-in-out';
|
||||
@endphp
|
||||
|
||||
<a {{ $attributes->merge(['class' => $classes]) }}>
|
||||
{{ $slot }}
|
||||
</a>
|
||||
@@ -0,0 +1,3 @@
|
||||
<button {{ $attributes->merge(['type' => 'button', 'class' => 'inline-flex items-center px-4 py-2 bg-white border border-gray-300 rounded-md font-semibold text-xs text-gray-700 uppercase tracking-widest shadow-sm hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 disabled:opacity-25 transition ease-in-out duration-150']) }}>
|
||||
{{ $slot }}
|
||||
</button>
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
@props(['disabled' => false])
|
||||
|
||||
<input @disabled($disabled) {{ $attributes->merge(['class' => 'border-gray-300 focus:border-indigo-500 focus:ring-indigo-500 rounded-md shadow-sm']) }}>
|
||||
Executable
+130
@@ -0,0 +1,130 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('header', 'Dashboard')
|
||||
|
||||
@section('breadcrumbs')
|
||||
@php
|
||||
$breadcrumbs = [];
|
||||
@endphp
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="col-xl-3 col-md-6 mb-4">
|
||||
<div class="card border-left-primary shadow h-100 py-2">
|
||||
<div class="card-body">
|
||||
<div class="row no-gutters align-items-center">
|
||||
<div class="col mr-2">
|
||||
<div class="text-xs fw-bold text-primary text-uppercase mb-1">Projetos</div>
|
||||
<div class="h5 mb-0 fw-bold text-gray-800">{{ $stats['projects'] ?? 0 }}</div>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<i class="bi bi-folder2-open fa-2x text-gray-300"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xl-3 col-md-6 mb-4">
|
||||
<div class="card border-left-success shadow h-100 py-2">
|
||||
<div class="card-body">
|
||||
<div class="row no-gutters align-items-center">
|
||||
<div class="col mr-2">
|
||||
<div class="text-xs fw-bold text-success text-uppercase mb-1">Gerações</div>
|
||||
<div class="h5 mb-0 fw-bold text-gray-800">{{ $stats['generations'] ?? 0 }}</div>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<i class="bi bi-clock-history fa-2x text-gray-300"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xl-3 col-md-6 mb-4">
|
||||
<div class="card border-left-info shadow h-100 py-2">
|
||||
<div class="card-body">
|
||||
<div class="row no-gutters align-items-center">
|
||||
<div class="col mr-2">
|
||||
<div class="text-xs fw-bold text-info text-uppercase mb-1">Provedores</div>
|
||||
<div class="h5 mb-0 fw-bold text-gray-800">{{ $stats['providers'] ?? 0 }}</div>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<i class="bi bi-cloud fa-2x text-gray-300"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-xl-3 col-md-6 mb-4">
|
||||
<div class="card border-left-warning shadow h-100 py-2">
|
||||
<div class="card-body">
|
||||
<div class="row no-gutters align-items-center">
|
||||
<div class="col mr-2">
|
||||
<div class="text-xs fw-bold text-warning text-uppercase mb-1">Tokens Usados</div>
|
||||
<div class="h5 mb-0 fw-bold text-gray-800">{{ $stats['tokens'] ?? 0 }}</div>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<i class="bi bi-token fa-2x text-gray-300"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-lg-8 mb-4">
|
||||
<div class="card shadow mb-4">
|
||||
<div class="card-header py-3">
|
||||
<h6 class="m-0 fw-bold text-primary">Gerações Recentes</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
@if(isset($recentGenerations) && count($recentGenerations) > 0)
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Projeto</th>
|
||||
<th>Modelo</th>
|
||||
<th>Tokens</th>
|
||||
<th>Data</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($recentGenerations as $generation)
|
||||
<tr>
|
||||
<td>{{ $generation->project->title ?? 'N/A' }}</td>
|
||||
<td>{{ $generation->model_used ?? 'N/A' }}</td>
|
||||
<td>{{ $generation->tokens_used ?? 0 }}</td>
|
||||
<td>{{ $generation->created_at->format('d/m/Y H:i') }}</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@else
|
||||
<p class="text-muted">Nenhuma geração encontrada.</p>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-4 mb-4">
|
||||
<div class="card shadow mb-4">
|
||||
<div class="card-header py-3">
|
||||
<h6 class="m-0 fw-bold text-primary">Ações Rápidas</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<a href="{{ route('prompt-projects.create') }}" class="btn btn-primary w-100 mb-2">
|
||||
<i class="bi bi-plus-circle"></i> Novo Projeto
|
||||
</a>
|
||||
<a href="{{ route('prompt-generations.index') }}" class="btn btn-info w-100 mb-2">
|
||||
<i class="bi bi-clock-history"></i> Ver Histórico
|
||||
</a>
|
||||
<a href="{{ route('ai-providers.index') }}" class="btn btn-warning w-100">
|
||||
<i class="bi bi-cloud"></i> Gerenciar Provedores
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
Executable
+157
@@ -0,0 +1,157 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||
<title>{{ config('app.name', 'GPrompt') }}</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body { font-family: 'Roboto', sans-serif; background-color: #f8f9fc; }
|
||||
#wrapper { display: flex; width: 100%; align-items: stretch; }
|
||||
#sidebar { min-width: 250px; max-width: 250px; min-height: 100vh; transition: all 0.3s; background: #2c3e50; color: #fff; }
|
||||
#sidebar.active { margin-left: -250px; }
|
||||
#sidebar .sidebar-brand { padding: 1.5rem 1rem; font-size: 1.25rem; font-weight: 700; text-align: center; border-bottom: 1px solid rgba(255,255,255,0.1); }
|
||||
#sidebar ul.components { padding: 1rem 0; }
|
||||
#sidebar ul li a { padding: 0.75rem 1.5rem; font-size: 0.95rem; display: block; color: rgba(255,255,255,0.8); text-decoration: none; transition: 0.2s; }
|
||||
#sidebar ul li a:hover, #sidebar ul li a.active { color: #fff; background: rgba(255,255,255,0.05); }
|
||||
#sidebar ul li a i { margin-right: 0.75rem; }
|
||||
#content { width: 100%; min-height: 100vh; display: flex; flex-direction: column; }
|
||||
.navbar { box-shadow: 0 0.15rem 1.75rem 0 rgba(58,59,69,0.15); }
|
||||
.breadcrumb { background-color: transparent; padding: 0; margin-bottom: 0; }
|
||||
.card { box-shadow: 0 0.15rem 1.75rem 0 rgba(58,59,69,0.15); border: none; }
|
||||
.card-header { background-color: #f8f9fc; border-bottom: 1px solid #e3e6f0; }
|
||||
.btn-primary { background-color: #4e73df; border-color: #4e73df; }
|
||||
.btn-primary:hover { background-color: #2e59d9; border-color: #2653d3; }
|
||||
.sidebar-toggle { cursor: pointer; border: none; background: transparent; font-size: 1.2rem; color: #5a5c69; }
|
||||
@media (max-width: 768px) {
|
||||
#sidebar { margin-left: -250px; position: fixed; z-index: 1050; }
|
||||
#sidebar.active { margin-left: 0; }
|
||||
#sidebarCollapse span { display: none; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="wrapper">
|
||||
@include('layouts.sidebar')
|
||||
|
||||
<div id="content">
|
||||
<nav class="navbar navbar-expand navbar-light bg-white topbar mb-4 static-top">
|
||||
<div class="container-fluid">
|
||||
<button id="sidebarToggle" class="btn btn-link sidebar-toggle me-2" type="button" aria-label="Toggle sidebar">
|
||||
<i class="bi bi-list fs-4"></i>
|
||||
</button>
|
||||
<ul class="navbar-nav ms-auto">
|
||||
@auth
|
||||
<li class="nav-item dropdown">
|
||||
<a class="nav-link dropdown-toggle" href="#" id="userDropdown" role="button" data-bs-toggle="dropdown">
|
||||
<span class="me-2 d-none d-lg-inline text-gray-600 small">{{ Auth::user()->name }}</span>
|
||||
<i class="bi bi-person-circle fs-5"></i>
|
||||
</a>
|
||||
<ul class="dropdown-menu dropdown-menu-end" aria-labelledby="userDropdown">
|
||||
<li><a class="dropdown-item" href="{{ route('dashboard') }}"><i class="bi bi-speedometer2 me-2"></i>Dashboard</a></li>
|
||||
<li><a class="dropdown-item" href="{{ route('profile.index') }}"><i class="bi bi-person me-2"></i>Perfil</a></li>
|
||||
<li><hr class="dropdown-divider"></li>
|
||||
<li>
|
||||
<form method="POST" action="{{ route('logout') }}">
|
||||
@csrf
|
||||
<button type="submit" class="dropdown-item"><i class="bi bi-box-arrow-right me-2"></i>Sair</button>
|
||||
</form>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
@endauth
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="container-fluid">
|
||||
@isset($breadcrumbs)
|
||||
<nav aria-label="breadcrumb" class="mb-3">
|
||||
<ol class="breadcrumb">
|
||||
<li class="breadcrumb-item"><a href="{{ route('welcome') }}"><i class="bi bi-house-door"></i> Início</a></li>
|
||||
@foreach($breadcrumbs as $label => $url)
|
||||
<li class="breadcrumb-item"><a href="{{ $url }}">{{ $label }}</a></li>
|
||||
@endforeach
|
||||
@isset($title)
|
||||
<li class="breadcrumb-item active" aria-current="page">{{ $title }}</li>
|
||||
@endisset
|
||||
</ol>
|
||||
</nav>
|
||||
@endisset
|
||||
|
||||
@hasSection('header')
|
||||
<div class="d-sm-flex align-items-center justify-content-between mb-4">
|
||||
<h1 class="h3 mb-0 text-gray-800">@yield('header')</h1>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<main>
|
||||
@if(session('success'))
|
||||
<div class="alert alert-success alert-dismissible fade show" role="alert">
|
||||
{{ session('success') }}
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Fechar"></button>
|
||||
</div>
|
||||
@endif
|
||||
@if(session('error'))
|
||||
<div class="alert alert-danger alert-dismissible fade show" role="alert">
|
||||
{{ session('error') }}
|
||||
<button type="button" class="btn-close" data-bs-dismiss="alert" aria-label="Fechar"></button>
|
||||
</div>
|
||||
@endif
|
||||
@yield('content')
|
||||
</main>
|
||||
</div>
|
||||
|
||||
@include('components.app-footer')
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@include('components.cookie-consent-banner')
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||
@yield('scripts')
|
||||
<script>
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
initSidebar();
|
||||
});
|
||||
} else {
|
||||
initSidebar();
|
||||
}
|
||||
|
||||
function initSidebar() {
|
||||
const sidebar = document.getElementById('sidebar');
|
||||
const sidebarCollapse = document.getElementById('sidebarCollapse');
|
||||
const sidebarToggle = document.getElementById('sidebarToggle');
|
||||
const sidebarToggleIcon = document.getElementById('sidebarToggleIcon');
|
||||
|
||||
function toggleSidebar() {
|
||||
sidebar.classList.toggle('active');
|
||||
|
||||
if (sidebar.classList.contains('active')) {
|
||||
if (sidebarToggleIcon) {
|
||||
sidebarToggleIcon.classList.remove('bi-chevron-left');
|
||||
sidebarToggleIcon.classList.add('bi-chevron-right');
|
||||
}
|
||||
} else {
|
||||
if (sidebarToggleIcon) {
|
||||
sidebarToggleIcon.classList.remove('bi-chevron-right');
|
||||
sidebarToggleIcon.classList.add('bi-chevron-left');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (sidebar && sidebarCollapse) {
|
||||
sidebarCollapse.addEventListener('click', toggleSidebar);
|
||||
}
|
||||
|
||||
if (sidebar && sidebarToggle) {
|
||||
sidebarToggle.addEventListener('click', toggleSidebar);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Executable
+34
@@ -0,0 +1,34 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||
|
||||
<title>{{ config('app.name', 'Laravel') }}</title>
|
||||
|
||||
<!-- Fonts -->
|
||||
<link rel="preconnect" href="https://fonts.bunny.net">
|
||||
<link href="https://fonts.bunny.net/css?family=figtree:400,500,600&display=swap" rel="stylesheet" />
|
||||
|
||||
<!-- Scripts -->
|
||||
@vite(['resources/css/app.css', 'resources/js/app.js'])
|
||||
</head>
|
||||
<body class="font-sans text-gray-900 antialiased">
|
||||
<div class="min-h-screen flex flex-col sm:justify-center items-center pt-6 sm:pt-0 bg-gray-100">
|
||||
<div>
|
||||
<a href="/">
|
||||
<x-application-logo class="w-20 h-20 fill-current text-gray-500" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="w-full sm:max-w-md mt-6 px-6 py-4 bg-white shadow-md overflow-hidden sm:rounded-lg">
|
||||
{{ $slot }}
|
||||
</div>
|
||||
|
||||
@include('components.app-footer')
|
||||
</div>
|
||||
|
||||
@include('components.cookie-consent-banner')
|
||||
</body>
|
||||
</html>
|
||||
Executable
+41
@@ -0,0 +1,41 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||
<title>@yield('header', config('app.name', 'GPrompt'))</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body { font-family: 'Roboto', sans-serif; background-color: #f8f9fc; }
|
||||
.legal-content { line-height: 1.8; }
|
||||
.legal-content h1, .legal-content h2, .legal-content h3 { margin-top: 1.5rem; }
|
||||
.legal-content p { margin-bottom: 1rem; }
|
||||
.legal-content ul, .legal-content ol { margin-bottom: 1rem; padding-left: 2rem; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container py-4">
|
||||
@isset($breadcrumbs)
|
||||
<nav aria-label="breadcrumb" class="mb-3">
|
||||
<ol class="breadcrumb">
|
||||
<li class="breadcrumb-item"><a href="{{ route('welcome') }}"><i class="bi bi-house-door"></i> Início</a></li>
|
||||
@foreach($breadcrumbs as $label => $url)
|
||||
<li class="breadcrumb-item"><a href="{{ $url }}">{{ $label }}</a></li>
|
||||
@endforeach
|
||||
</ol>
|
||||
</nav>
|
||||
@endisset
|
||||
|
||||
@yield('content')
|
||||
</div>
|
||||
|
||||
@include('components.app-footer')
|
||||
|
||||
@include('components.cookie-consent-banner')
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
Executable
+100
@@ -0,0 +1,100 @@
|
||||
<nav x-data="{ open: false }" class="bg-white border-b border-gray-100">
|
||||
<!-- Primary Navigation Menu -->
|
||||
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
|
||||
<div class="flex justify-between h-16">
|
||||
<div class="flex">
|
||||
<!-- Logo -->
|
||||
<div class="shrink-0 flex items-center">
|
||||
<a href="{{ route('dashboard') }}">
|
||||
<x-application-logo class="block h-9 w-auto fill-current text-gray-800" />
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<!-- Navigation Links -->
|
||||
<div class="hidden space-x-8 sm:-my-px sm:ms-10 sm:flex">
|
||||
<x-nav-link :href="route('dashboard')" :active="request()->routeIs('dashboard')">
|
||||
{{ __('Dashboard') }}
|
||||
</x-nav-link>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Settings Dropdown -->
|
||||
<div class="hidden sm:flex sm:items-center sm:ms-6">
|
||||
<x-dropdown align="right" width="48">
|
||||
<x-slot name="trigger">
|
||||
<button class="inline-flex items-center px-3 py-2 border border-transparent text-sm leading-4 font-medium rounded-md text-gray-500 bg-white hover:text-gray-700 focus:outline-none transition ease-in-out duration-150">
|
||||
<div>{{ Auth::user()->name }}</div>
|
||||
|
||||
<div class="ms-1">
|
||||
<svg class="fill-current h-4 w-4" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20">
|
||||
<path fill-rule="evenodd" d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" clip-rule="evenodd" />
|
||||
</svg>
|
||||
</div>
|
||||
</button>
|
||||
</x-slot>
|
||||
|
||||
<x-slot name="content">
|
||||
<x-dropdown-link :href="route('profile.edit')">
|
||||
{{ __('Profile') }}
|
||||
</x-dropdown-link>
|
||||
|
||||
<!-- Authentication -->
|
||||
<form method="POST" action="{{ route('logout') }}">
|
||||
@csrf
|
||||
|
||||
<x-dropdown-link :href="route('logout')"
|
||||
onclick="event.preventDefault();
|
||||
this.closest('form').submit();">
|
||||
{{ __('Log Out') }}
|
||||
</x-dropdown-link>
|
||||
</form>
|
||||
</x-slot>
|
||||
</x-dropdown>
|
||||
</div>
|
||||
|
||||
<!-- Hamburger -->
|
||||
<div class="-me-2 flex items-center sm:hidden">
|
||||
<button @click="open = ! open" class="inline-flex items-center justify-center p-2 rounded-md text-gray-400 hover:text-gray-500 hover:bg-gray-100 focus:outline-none focus:bg-gray-100 focus:text-gray-500 transition duration-150 ease-in-out">
|
||||
<svg class="h-6 w-6" stroke="currentColor" fill="none" viewBox="0 0 24 24">
|
||||
<path :class="{'hidden': open, 'inline-flex': ! open }" class="inline-flex" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16" />
|
||||
<path :class="{'hidden': ! open, 'inline-flex': open }" class="hidden" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Responsive Navigation Menu -->
|
||||
<div :class="{'block': open, 'hidden': ! open}" class="hidden sm:hidden">
|
||||
<div class="pt-2 pb-3 space-y-1">
|
||||
<x-responsive-nav-link :href="route('dashboard')" :active="request()->routeIs('dashboard')">
|
||||
{{ __('Dashboard') }}
|
||||
</x-responsive-nav-link>
|
||||
</div>
|
||||
|
||||
<!-- Responsive Settings Options -->
|
||||
<div class="pt-4 pb-1 border-t border-gray-200">
|
||||
<div class="px-4">
|
||||
<div class="font-medium text-base text-gray-800">{{ Auth::user()->name }}</div>
|
||||
<div class="font-medium text-sm text-gray-500">{{ Auth::user()->email }}</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-3 space-y-1">
|
||||
<x-responsive-nav-link :href="route('profile.edit')">
|
||||
{{ __('Profile') }}
|
||||
</x-responsive-nav-link>
|
||||
|
||||
<!-- Authentication -->
|
||||
<form method="POST" action="{{ route('logout') }}">
|
||||
@csrf
|
||||
|
||||
<x-responsive-nav-link :href="route('logout')"
|
||||
onclick="event.preventDefault();
|
||||
this.closest('form').submit();">
|
||||
{{ __('Log Out') }}
|
||||
</x-responsive-nav-link>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
Executable
+72
@@ -0,0 +1,72 @@
|
||||
<nav id="sidebar">
|
||||
<div class="sidebar-brand d-flex justify-content-between align-items-center">
|
||||
<span><i class="bi bi-cpu me-2"></i>GPrompt</span>
|
||||
<button id="sidebarCollapse" class="sidebar-toggle border-0 bg-transparent text-white">
|
||||
<i class="bi bi-chevron-left" id="sidebarToggleIcon"></i>
|
||||
</button>
|
||||
</div>
|
||||
<ul class="list-unstyled components mt-3">
|
||||
<li>
|
||||
<a href="{{ route('dashboard') }}" class="{{ request()->routeIs('dashboard') ? 'active' : '' }}">
|
||||
<i class="bi bi-speedometer2 me-2"></i> Dashboard
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ route('profile.index') }}" class="{{ request()->routeIs('profile.*') ? 'active' : '' }}">
|
||||
<i class="bi bi-person me-2"></i> Perfil
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ route('prompt-projects.index') }}" class="{{ request()->routeIs('prompt-projects.*') ? 'active' : '' }}">
|
||||
<i class="bi bi-folder2-open me-2"></i> Projetos de Prompt
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ route('prompt-templates.index') }}" class="{{ request()->routeIs('prompt-templates.*') ? 'active' : '' }}">
|
||||
<i class="bi bi-file-earmark-code me-2"></i> Templates
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ route('prompt-generations.index') }}" class="{{ request()->routeIs('prompt-generations.*') ? 'active' : '' }}">
|
||||
<i class="bi bi-clock-history me-2"></i> Histórico
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ route('chat.index') }}" class="{{ request()->routeIs('chat.*') ? 'active' : '' }}">
|
||||
<i class="bi bi-chat-dots me-2"></i> Chat com IA
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ route('ai-agent.index') }}" class="{{ request()->routeIs('ai-agent.*') ? 'active' : '' }}">
|
||||
<i class="bi bi-robot me-2"></i> Agente de IA para Landing Page e Perguntas
|
||||
</a>
|
||||
</li>
|
||||
@auth
|
||||
@if(Auth::user()->role === 'superadmin')
|
||||
<li>
|
||||
<a href="{{ route('ai-providers.index') }}" class="{{ request()->routeIs('ai-providers.*') ? 'active' : '' }}">
|
||||
<i class="bi bi-cloud me-2"></i> Provedores IA
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ route('users.index') }}" class="{{ request()->routeIs('users.*') ? 'active' : '' }}">
|
||||
<i class="bi bi-people me-2"></i> Usuários
|
||||
</a>
|
||||
</li>
|
||||
@endif
|
||||
@endauth
|
||||
<li>
|
||||
<a href="{{ route('tutorial.index') }}" class="{{ request()->routeIs('tutorial.*') ? 'active' : '' }}">
|
||||
<i class="bi bi-book me-2"></i> Curso de Engenharia de Prompts
|
||||
</a>
|
||||
</li>
|
||||
<li class="mt-4">
|
||||
<form method="POST" action="{{ route('logout') }}">
|
||||
@csrf
|
||||
<button type="submit" class="btn btn-link text-decoration-none w-100 text-start ps-4">
|
||||
<i class="bi bi-box-arrow-right me-2"></i> Logout
|
||||
</button>
|
||||
</form>
|
||||
</li>
|
||||
</ul>
|
||||
</nav>
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('header', $document->exists ? __('legal_documents.edit') : __('legal_documents.new_document'))
|
||||
|
||||
@section('breadcrumbs')
|
||||
@php
|
||||
$breadcrumbs = [
|
||||
__('legal_documents.documents_management') => route('legal.documents.index'),
|
||||
($document->type === 'terms-of-service' ? __('legal_documents.terms_of_service') : __('legal_documents.privacy_policy')) => route('legal.document.edit', $document->type),
|
||||
];
|
||||
@endphp
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
<div class="card shadow">
|
||||
<div class="card-header py-3">
|
||||
<h6 class="m-0 fw-bold text-primary">
|
||||
{{ $document->type === 'terms-of-service' ? __('legal_documents.terms_of_service') : __('legal_documents.privacy_policy') }}
|
||||
</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="POST" action="{{ route('legal.document.update', $document->type) }}">
|
||||
@csrf
|
||||
@method('PUT')
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="title" class="form-label">@lang('legal_documents.title')</label>
|
||||
<input type="text" class="form-control" id="title" name="title" value="{{ old('title', $document->title) }}" required>
|
||||
@error('title') <div class="text-danger small">{{ $message }}</div> @enderror
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="version" class="form-label">@lang('legal_documents.version')</label>
|
||||
<input type="text" class="form-control" id="version" name="version" value="{{ old('version', $document->version ?? '1.0') }}" required>
|
||||
@error('version') <div class="text-danger small">{{ $message }}</div> @enderror
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="content" class="form-label">@lang('legal_documents.content')</label>
|
||||
<textarea class="form-control font-monospace" id="content" name="content" rows="20" required>{{ old('content', $document->content) }}</textarea>
|
||||
@error('content') <div class="text-danger small">{{ $message }}</div> @enderror
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="published_at" class="form-label">@lang('legal_documents.published_at')</label>
|
||||
<input type="datetime-local" class="form-control" id="published_at" name="published_at" value="{{ old('published_at', $document->published_at ? $document->published_at->format('Y-m-d\TH:i') : now()->format('Y-m-d\TH:i')) }}">
|
||||
<div class="form-text">{{ __('legal_documents.publish_now') }}</div>
|
||||
@error('published_at') <div class="text-danger small">{{ $message }}</div> @enderror
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-between">
|
||||
<a href="{{ route('legal.documents.index') }}" class="btn btn-secondary">
|
||||
<i class="bi bi-arrow-left"></i> @lang('legal_documents.back')
|
||||
</a>
|
||||
<button type="submit" class="btn btn-primary">
|
||||
<i class="bi bi-save"></i> @lang('legal_documents.save')
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
+66
@@ -0,0 +1,66 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('header', __('legal_documents.documents_management'))
|
||||
|
||||
@section('breadcrumbs')
|
||||
@php
|
||||
$breadcrumbs = [
|
||||
__('legal_documents.documents_management') => route('legal.documents.index'),
|
||||
];
|
||||
@endphp
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
<div class="card shadow">
|
||||
<div class="card-header py-3 d-flex justify-content-between align-items-center">
|
||||
<h6 class="m-0 fw-bold text-primary">@lang('legal_documents.documents_management')</h6>
|
||||
<a href="{{ route('legal.document.edit', 'terms-of-service') }}" class="btn btn-sm btn-primary">
|
||||
<i class="bi bi-file-earmark-text"></i> @lang('legal_documents.edit')
|
||||
</a>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>@lang('legal_documents.type')</th>
|
||||
<th>@lang('legal_documents.title')</th>
|
||||
<th>@lang('legal_documents.version')</th>
|
||||
<th>@lang('legal_documents.published_at')</th>
|
||||
<th>@lang('legal_documents.actions')</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@forelse($documents->groupBy('type') as $type => $docs)
|
||||
@php $latest = $docs->first(); @endphp
|
||||
<tr>
|
||||
<td>
|
||||
@if($latest->type === 'terms-of-service')
|
||||
<span class="badge bg-primary">@lang('legal_documents.terms_of_service')</span>
|
||||
@elseif($latest->type === 'privacy-policy')
|
||||
<span class="badge bg-success">@lang('legal_documents.privacy_policy')</span>
|
||||
@endif
|
||||
</td>
|
||||
<td>{{ $latest->title }}</td>
|
||||
<td><span class="badge bg-secondary">{{ $latest->version }}</span></td>
|
||||
<td>{{ $latest->published_at ? $latest->published_at->format('d/m/Y H:i') : __('legal_documents.not_published') }}</td>
|
||||
<td>
|
||||
<a href="{{ route('legal.document.show', $latest->type) }}" class="btn btn-sm btn-outline-primary" target="_blank">
|
||||
<i class="bi bi-eye"></i>
|
||||
</a>
|
||||
<a href="{{ route('legal.document.edit', $latest->type) }}" class="btn btn-sm btn-outline-secondary">
|
||||
<i class="bi bi-pencil"></i>
|
||||
</a>
|
||||
</td>
|
||||
</tr>
|
||||
@empty
|
||||
<tr>
|
||||
<td colspan="5" class="text-center text-muted">@lang('legal_documents.no_documents')</td>
|
||||
</tr>
|
||||
@endforelse
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
@extends('layouts.legal')
|
||||
|
||||
@section('header', $document->title)
|
||||
|
||||
@section('breadcrumbs')
|
||||
@php
|
||||
$breadcrumbs = [
|
||||
'Início' => route('welcome'),
|
||||
($document->type === 'terms-of-service' ? 'Termos de Uso' : 'Política de Privacidade') => route('legal.document.show', $document->type),
|
||||
];
|
||||
@endphp
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
<div class="card shadow">
|
||||
<div class="card-header py-3">
|
||||
<div>
|
||||
<h5 class="m-0 fw-bold text-primary">{{ $document->title }}</h5>
|
||||
<small class="text-muted">Versão {{ $document->version }} - {{ $document->published_at ? $document->published_at->format('d/m/Y') : 'Não publicado' }}</small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="legal-content">
|
||||
{!! nl2br(e($document->content)) !!}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
Executable
+64
@@ -0,0 +1,64 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('header', 'Editar Perfil')
|
||||
|
||||
@section('breadcrumbs')
|
||||
@php
|
||||
$breadcrumbs = [
|
||||
'Perfil' => route('profile.index'),
|
||||
'Editar' => 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">Editar Perfil</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="POST" action="{{ route('profile.update') }}" enctype="multipart/form-data">
|
||||
@csrf
|
||||
@method('PUT')
|
||||
|
||||
<div class="text-center mb-4">
|
||||
@if($user->avatar)
|
||||
<img src="{{ asset('storage/' . $user->avatar) }}" alt="Avatar" class="rounded-circle" style="width: 120px; height: 120px; object-fit: cover;">
|
||||
@else
|
||||
<i class="bi bi-person-circle" style="font-size: 6rem; color: #5a5c69;"></i>
|
||||
@endif
|
||||
<div class="mt-2">
|
||||
<label for="avatar" class="btn btn-sm btn-outline-primary">
|
||||
<i class="bi bi-camera"></i> Alterar Avatar
|
||||
</label>
|
||||
<input type="file" id="avatar" name="avatar" class="d-none" accept="image/*">
|
||||
</div>
|
||||
@error('avatar')
|
||||
<div class="text-danger small mt-1">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="name" class="form-label">Nome</label>
|
||||
<input type="text" class="form-control" id="name" name="name" value="{{ old('name', $user->name) }}" required>
|
||||
@error('name')
|
||||
<div class="text-danger small">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="email" class="form-label">E-mail</label>
|
||||
<input type="email" class="form-control" id="email" name="email" value="{{ old('email', $user->email) }}" required>
|
||||
@error('email')
|
||||
<div class="text-danger small">{{ $message }}</div>
|
||||
@enderror
|
||||
</div>
|
||||
|
||||
<button type="submit" class="btn btn-primary"><i class="bi bi-check-circle me-2"></i>Salvar</button>
|
||||
<a href="{{ route('profile.index') }}" class="btn btn-secondary">Cancelar</a>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@include('profile.partials.update-password-form')
|
||||
@endsection
|
||||
Executable
+109
@@ -0,0 +1,109 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('header', 'Meu Perfil')
|
||||
|
||||
@section('breadcrumbs')
|
||||
@php
|
||||
$breadcrumbs = [
|
||||
'Perfil' => null
|
||||
];
|
||||
@endphp
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
<div class="row">
|
||||
<div class="col-lg-4 mb-4">
|
||||
<div class="card shadow mb-4">
|
||||
<div class="card-header py-3">
|
||||
<h6 class="m-0 fw-bold text-primary">Informações Pessoais</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="text-center mb-3">
|
||||
@if($user->avatar)
|
||||
<img src="{{ asset('storage/' . $user->avatar) }}" alt="Avatar" class="rounded-circle" style="width: 120px; height: 120px; object-fit: cover;">
|
||||
@else
|
||||
<i class="bi bi-person-circle" style="font-size: 4rem; color: #5a5c69;"></i>
|
||||
@endif
|
||||
<h5 class="mt-2">{{ $user->name }}</h5>
|
||||
<p class="text-muted">{{ $user->email }}</p>
|
||||
@if($user->role === 'superadmin')
|
||||
<span class="badge bg-danger">SuperAdmin</span>
|
||||
@else
|
||||
<span class="badge bg-primary">Usuário</span>
|
||||
@endif
|
||||
@if($user->is_approved)
|
||||
<span class="badge bg-success ms-1">Aprovado</span>
|
||||
@else
|
||||
<span class="badge bg-warning text-dark ms-1">Pendente</span>
|
||||
@endif
|
||||
</div>
|
||||
<hr>
|
||||
<div class="d-grid">
|
||||
<a href="{{ route('profile.edit') }}" class="btn btn-warning">
|
||||
<i class="bi bi-pencil me-2"></i>Editar Perfil
|
||||
</a>
|
||||
</div>
|
||||
<hr>
|
||||
<p class="mb-1"><strong><i class="bi bi-calendar me-2"></i>Cadastro:</strong> {{ $user->created_at->format('d/m/Y H:i') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-8 mb-4">
|
||||
<div class="card shadow mb-4">
|
||||
<div class="card-header py-3">
|
||||
<h6 class="m-0 fw-bold text-primary">Estatísticas</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-md-4 mb-3">
|
||||
<div class="card border-left-primary shadow h-100 py-2">
|
||||
<div class="card-body">
|
||||
<div class="row no-gutters align-items-center">
|
||||
<div class="col mr-2">
|
||||
<div class="text-xs fw-bold text-primary text-uppercase mb-1">Projetos</div>
|
||||
<div class="h5 mb-0 fw-bold text-gray-800">{{ $stats['projects'] }}</div>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<i class="bi bi-folder2-open fa-2x text-gray-300"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4 mb-3">
|
||||
<div class="card border-left-success shadow h-100 py-2">
|
||||
<div class="card-body">
|
||||
<div class="row no-gutters align-items-center">
|
||||
<div class="col mr-2">
|
||||
<div class="text-xs fw-bold text-success text-uppercase mb-1">Gerações</div>
|
||||
<div class="h5 mb-0 fw-bold text-gray-800">{{ $stats['generations'] }}</div>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<i class="bi bi-clock-history fa-2x text-gray-300"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4 mb-3">
|
||||
<div class="card border-left-warning shadow h-100 py-2">
|
||||
<div class="card-body">
|
||||
<div class="row no-gutters align-items-center">
|
||||
<div class="col mr-2">
|
||||
<div class="text-xs fw-bold text-warning text-uppercase mb-1">Tokens Usados</div>
|
||||
<div class="h5 mb-0 fw-bold text-gray-800">{{ number_format($stats['tokens'], 0, ',', '.') }}</div>
|
||||
</div>
|
||||
<div class="col-auto">
|
||||
<i class="bi bi-token fa-2x text-gray-300"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
@@ -0,0 +1,35 @@
|
||||
<section class="space-y-4">
|
||||
<header>
|
||||
<h2 class="h5 fw-bold text-gray-800 mb-1">Excluir Conta</h2>
|
||||
<p class="text-muted small">Depois que sua conta for excluída, todos os seus recursos e dados serão excluídos permanentemente. Antes de excluir sua conta, baixe todos os dados ou informações que deseja reter.</p>
|
||||
</header>
|
||||
|
||||
<button type="button" class="btn btn-danger" data-bs-toggle="modal" data-bs-target="#confirmUserDeletion">
|
||||
Excluir Conta
|
||||
</button>
|
||||
|
||||
<div class="modal fade" id="confirmUserDeletion" tabindex="-1" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<form method="post" action="{{ route('profile.destroy') }}" class="p-4">
|
||||
@csrf
|
||||
@method('delete')
|
||||
|
||||
<h2 class="h5 fw-bold mb-3">Tem certeza que deseja excluir sua conta?</h2>
|
||||
<p class="text-muted small mb-3">Depois que sua conta for excluída, todos os seus recursos e dados serão excluídos permanentemente. Insira sua senha para confirmar.</p>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="password" class="form-label sr-only">Senha</label>
|
||||
<input type="password" class="form-control" id="password" name="password" placeholder="Senha">
|
||||
@error('password') <div class="text-danger small">{{ $message }}</div> @enderror
|
||||
</div>
|
||||
|
||||
<div class="d-flex justify-content-end gap-2">
|
||||
<button type="button" class="btn btn-secondary" data-bs-dismiss="modal">Cancelar</button>
|
||||
<button type="submit" class="btn btn-danger">Excluir Conta</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
@@ -0,0 +1,56 @@
|
||||
<section>
|
||||
<header>
|
||||
<h2 class="h5 fw-bold text-gray-800 mb-1">Atualizar Senha</h2>
|
||||
<p class="text-muted small">Garanta que sua conta esteja usando uma senha longa e aleatória para manter-se segura.</p>
|
||||
</header>
|
||||
|
||||
<form method="post" action="{{ route('password.update') }}" class="mt-4">
|
||||
@csrf
|
||||
@method('put')
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="update_password_password" class="form-label">Nova Senha</label>
|
||||
<div class="input-group">
|
||||
<input type="password" class="form-control" id="update_password_password" name="password" autocomplete="new-password">
|
||||
<button class="btn btn-outline-secondary" type="button" onclick="togglePasswordVisibility('update_password_password', this)">
|
||||
<i class="bi bi-eye"></i>
|
||||
</button>
|
||||
</div>
|
||||
@error('password') <div class="text-danger small">{{ $message }}</div> @enderror
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="update_password_password_confirmation" class="form-label">Confirmar Senha</label>
|
||||
<div class="input-group">
|
||||
<input type="password" class="form-control" id="update_password_password_confirmation" name="password_confirmation" autocomplete="new-password">
|
||||
<button class="btn btn-outline-secondary" type="button" onclick="togglePasswordVisibility('update_password_password_confirmation', this)">
|
||||
<i class="bi bi-eye"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<button type="submit" class="btn btn-primary">Salvar</button>
|
||||
|
||||
@if (session('status') === 'password-updated')
|
||||
<span class="small text-success">Senha atualizada.</span>
|
||||
@endif
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
|
||||
<script>
|
||||
function togglePasswordVisibility(inputId, button) {
|
||||
const input = document.getElementById(inputId);
|
||||
const icon = button.querySelector('i');
|
||||
if (input.type === 'password') {
|
||||
input.type = 'text';
|
||||
icon.classList.remove('bi-eye');
|
||||
icon.classList.add('bi-eye-slash');
|
||||
} else {
|
||||
input.type = 'password';
|
||||
icon.classList.remove('bi-eye-slash');
|
||||
icon.classList.add('bi-eye');
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@@ -0,0 +1,46 @@
|
||||
<section>
|
||||
<header>
|
||||
<h2 class="h5 fw-bold text-gray-800 mb-1">Informações do Perfil</h2>
|
||||
<p class="text-muted small">Atualize as informações da sua conta e endereço de e-mail.</p>
|
||||
</header>
|
||||
|
||||
<form id="send-verification" method="post" action="{{ route('verification.send') }}">
|
||||
@csrf
|
||||
</form>
|
||||
|
||||
<form method="post" action="{{ route('profile.update') }}" class="mt-4">
|
||||
@csrf
|
||||
@method('patch')
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="name" class="form-label">Nome</label>
|
||||
<input type="text" class="form-control" id="name" name="name" value="{{ old('name', $user->name) }}" required autofocus autocomplete="name">
|
||||
@error('name') <div class="text-danger small">{{ $message }}</div> @enderror
|
||||
</div>
|
||||
|
||||
<div class="mb-3">
|
||||
<label for="email" class="form-label">E-mail</label>
|
||||
<input type="email" class="form-control" id="email" name="email" value="{{ old('email', $user->email) }}" required autocomplete="username">
|
||||
@error('email') <div class="text-danger small">{{ $message }}</div> @enderror
|
||||
|
||||
@if ($user instanceof \Illuminate\Contracts\Auth\MustVerifyEmail && ! $user->hasVerifiedEmail())
|
||||
<div class="mt-2">
|
||||
<p class="small text-muted">Seu e-mail não está verificado.</p>
|
||||
<button form="send-verification" class="btn btn-link btn-sm p-0">Reenviar e-mail de verificação</button>
|
||||
|
||||
@if (session('status') === 'verification-link-sent')
|
||||
<p class="mt-2 small text-success">Um novo link de verificação foi enviado.</p>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<button type="submit" class="btn btn-primary">Salvar</button>
|
||||
|
||||
@if (session('status') === 'profile-updated')
|
||||
<span class="small text-success">Salvo com sucesso.</span>
|
||||
@endif
|
||||
</div>
|
||||
</form>
|
||||
</section>
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('header', 'Histórico de Gerações')
|
||||
|
||||
@section('breadcrumbs')
|
||||
@php
|
||||
$breadcrumbs = [
|
||||
'Histórico' => route('prompt-generations.index')
|
||||
];
|
||||
@endphp
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
<div class="card shadow mb-4">
|
||||
<div class="card-header py-3">
|
||||
<h6 class="m-0 fw-bold text-primary">Histórico de Gerações</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
@if(isset($generations) && count($generations) > 0)
|
||||
@foreach($generations as $generation)
|
||||
<div class="card mb-3">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<span>
|
||||
<strong>#{{ $generation->id }}</strong>
|
||||
{{ $generation->project->title ?? 'N/A' }}
|
||||
<span class="text-muted">|</span>
|
||||
{{ $generation->model->name ?? 'N/A' }}
|
||||
<span class="text-muted">|</span>
|
||||
{{ $generation->created_at->format('d/m/Y H:i') }}
|
||||
@if($generation->status === 'failed')
|
||||
<span class="badge bg-danger ms-2">Falha</span>
|
||||
@else
|
||||
<span class="badge bg-success ms-2">Sucesso</span>
|
||||
@endif
|
||||
</span>
|
||||
<div class="d-flex align-items-center gap-3">
|
||||
<small class="text-muted">
|
||||
{{ $generation->tokens_used ?? 0 }} tokens | {{ $generation->generation_time ?? 0 }}s
|
||||
</small>
|
||||
<form action="{{ route('prompt-generations.destroy', $generation) }}" method="POST" onsubmit="return confirm('Tem certeza que deseja excluir esta geração?')">
|
||||
@csrf @method('DELETE')
|
||||
<button type="submit" class="btn btn-danger btn-sm"><i class="bi bi-trash"></i> Excluir</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h6 class="card-title">Prompt Enviado</h6>
|
||||
<pre class="bg-light p-3 rounded small mb-3" style="white-space: pre-wrap;">{{ $generation->generated_prompt }}</pre>
|
||||
|
||||
@if($generation->status === 'completed' && !empty($generation->output_data['content'] ?? ''))
|
||||
<h6 class="card-title">Resposta da IA</h6>
|
||||
<div class="bg-white border p-3 rounded small" style="white-space: pre-wrap;">{{ $generation->output_data['content'] }}</div>
|
||||
@elseif($generation->status === 'failed')
|
||||
<div class="alert alert-danger small">
|
||||
<strong>Erro:</strong> {{ $generation->output_data['error'] ?? 'Erro desconhecido' }}
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
<div class="mt-3">
|
||||
{{ $generations->links() }}
|
||||
</div>
|
||||
@else
|
||||
<p class="text-muted">Nenhuma geração encontrada.</p>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
+380
@@ -0,0 +1,380 @@
|
||||
@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;
|
||||
} 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')
|
||||
+146
@@ -0,0 +1,146 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('header', 'Editar Projeto')
|
||||
|
||||
@section('breadcrumbs')
|
||||
@php
|
||||
$breadcrumbs = [
|
||||
'Projetos' => route('prompt-projects.index'),
|
||||
'Editar' => 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">Editar Projeto</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="POST" action="{{ route('prompt-projects.update', $promptProject) }}">
|
||||
@csrf @method('PUT')
|
||||
<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', $promptProject->title) }}" required>
|
||||
@error('name') <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', $promptProject->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', $promptProject->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', $promptProject->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', $promptProject->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', $promptProject->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', $promptProject->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', $promptProject->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', $promptProject->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', $promptProject->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', $promptProject->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', $promptProject->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', $promptProject->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', $promptProject->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', $promptProject->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">{{ is_array($promptProject->examples) ? implode("\n", $promptProject->examples) : $promptProject->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">{{ is_array($promptProject->variables) ? implode(", ", $promptProject->variables) : $promptProject->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', $promptProject->temperature) }}" 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', $promptProject->max_tokens) }}" 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> Atualizar</button>
|
||||
<a href="{{ route('prompt-projects.index') }}" class="btn btn-secondary">Cancelar</a>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('header', 'Projetos de Prompt')
|
||||
|
||||
@section('breadcrumbs')
|
||||
@php
|
||||
$breadcrumbs = [
|
||||
'Projetos' => route('prompt-projects.index')
|
||||
];
|
||||
@endphp
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
<div class="card shadow mb-4">
|
||||
<div class="card-header py-3 d-flex justify-content-between align-items-center">
|
||||
<h6 class="m-0 fw-bold text-primary">Lista de Projetos</h6>
|
||||
<a href="{{ route('prompt-projects.create') }}" class="btn btn-primary btn-sm">
|
||||
<i class="bi bi-plus-circle"></i> Novo Projeto
|
||||
</a>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
@if(isset($projects) && count($projects) > 0)
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Nome</th>
|
||||
<th>Objetivo</th>
|
||||
<th>Provedor</th>
|
||||
<th>Temperatura</th>
|
||||
<th>Tokens</th>
|
||||
<th>Ações</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($projects as $project)
|
||||
<tr>
|
||||
<td>{{ $project->title }}</td>
|
||||
<td>{{ Str::limit($project->objective, 50) }}</td>
|
||||
<td>{{ $project->provider->name ?? 'N/A' }}</td>
|
||||
<td>{{ $project->temperature }}</td>
|
||||
<td>{{ $project->max_tokens }}</td>
|
||||
<td>
|
||||
<a href="{{ route('prompt-projects.show', $project) }}" class="btn btn-info btn-sm">
|
||||
<i class="bi bi-eye"></i>
|
||||
</a>
|
||||
<a href="{{ route('prompt-projects.edit', $project) }}" class="btn btn-warning btn-sm">
|
||||
<i class="bi bi-pencil"></i>
|
||||
</a>
|
||||
<form action="{{ route('prompt-projects.destroy', $project) }}" method="POST" class="d-inline" onsubmit="return confirm('Tem certeza?')">
|
||||
@csrf @method('DELETE')
|
||||
<button class="btn btn-danger btn-sm"><i class="bi bi-trash"></i></button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@if(is_object($projects) && method_exists($projects, 'links'))
|
||||
<div class="mt-3">
|
||||
{{ $projects->links() }}
|
||||
</div>
|
||||
@endif
|
||||
@else
|
||||
<p class="text-muted">Nenhum projeto encontrado.</p>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
+202
@@ -0,0 +1,202 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('header', $promptProject->title)
|
||||
|
||||
@section('breadcrumbs')
|
||||
@php
|
||||
$breadcrumbs = [
|
||||
'Projetos' => route('prompt-projects.index'),
|
||||
$promptProject->title => null
|
||||
];
|
||||
@endphp
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
<div class="card shadow mb-4">
|
||||
<div class="card-header py-3 d-flex justify-content-between align-items-center">
|
||||
<h6 class="m-0 fw-bold text-primary">Detalhes do Projeto</h6>
|
||||
<div>
|
||||
<a href="{{ route('prompt-projects.edit', $promptProject) }}" class="btn btn-warning btn-sm me-2">
|
||||
<i class="bi bi-pencil"></i> Editar
|
||||
</a>
|
||||
<form action="{{ route('prompt-projects.destroy', $promptProject) }}" method="POST" class="d-inline" onsubmit="return confirm('Tem certeza?')">
|
||||
@csrf @method('DELETE')
|
||||
<button class="btn btn-danger btn-sm"><i class="bi bi-trash"></i> Excluir</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<dl class="row">
|
||||
<dt class="col-sm-3">Nome</dt>
|
||||
<dd class="col-sm-9">{{ $promptProject->title }}</dd>
|
||||
|
||||
<dt class="col-sm-3">Nome da Empresa</dt>
|
||||
<dd class="col-sm-9">{{ $promptProject->company_name ?: '-' }}</dd>
|
||||
|
||||
<dt class="col-sm-3">Endereço da Empresa</dt>
|
||||
<dd class="col-sm-9">{{ $promptProject->company_address ?: '-' }}</dd>
|
||||
|
||||
<dt class="col-sm-3">WhatsApp da Empresa</dt>
|
||||
<dd class="col-sm-9">{{ $promptProject->company_whatsapp ?: '-' }}</dd>
|
||||
|
||||
<dt class="col-sm-3">Website da Empresa</dt>
|
||||
<dd class="col-sm-9">{{ $promptProject->company_website ?: '-' }}</dd>
|
||||
|
||||
<dt class="col-sm-3">Redes Sociais da Empresa</dt>
|
||||
<dd class="col-sm-9">{{ $promptProject->company_social_media ?: '-' }}</dd>
|
||||
|
||||
<dt class="col-sm-3">Atividades de Negócio da Empresa</dt>
|
||||
<dd class="col-sm-9">{{ $promptProject->company_business_activities ?: '-' }}</dd>
|
||||
|
||||
<dt class="col-sm-3">Serviços e Produtos da Empresa</dt>
|
||||
<dd class="col-sm-9">{{ $promptProject->company_services_products ?: '-' }}</dd>
|
||||
|
||||
<dt class="col-sm-3">Objetivo</dt>
|
||||
<dd class="col-sm-9">{{ $promptProject->objective }}</dd>
|
||||
|
||||
<dt class="col-sm-3">Contexto</dt>
|
||||
<dd class="col-sm-9">{{ $promptProject->context ?: '-' }}</dd>
|
||||
|
||||
<dt class="col-sm-3">Persona</dt>
|
||||
<dd class="col-sm-9">{{ $promptProject->persona ?: '-' }}</dd>
|
||||
|
||||
<dt class="col-sm-3">Tom</dt>
|
||||
<dd class="col-sm-9">{{ $promptProject->tone ?: '-' }}</dd>
|
||||
|
||||
<dt class="col-sm-3">Restrições</dt>
|
||||
<dd class="col-sm-9">{{ $promptProject->constraints ?: '-' }}</dd>
|
||||
|
||||
<dt class="col-sm-3">Formato de Saída</dt>
|
||||
<dd class="col-sm-9">{{ $promptProject->output_format ?: '-' }}</dd>
|
||||
|
||||
<dt class="col-sm-3">Exemplos</dt>
|
||||
<dd class="col-sm-9">{{ is_array($promptProject->examples) ? implode(', ', $promptProject->examples) : ($promptProject->examples ?: '-') }}</dd>
|
||||
|
||||
<dt class="col-sm-3">Variáveis</dt>
|
||||
<dd class="col-sm-9">{{ is_array($promptProject->variables) ? implode(', ', $promptProject->variables) : ($promptProject->variables ?: '-') }}</dd>
|
||||
|
||||
<dt class="col-sm-3">Temperatura</dt>
|
||||
<dd class="col-sm-9">{{ $promptProject->temperature }}</dd>
|
||||
|
||||
<dt class="col-sm-3">Max Tokens</dt>
|
||||
<dd class="col-sm-9">{{ $promptProject->max_tokens }}</dd>
|
||||
|
||||
<dt class="col-sm-3">Provedor</dt>
|
||||
<dd class="col-sm-9">{{ $promptProject->provider->name ?? 'N/A' }}</dd>
|
||||
|
||||
<dt class="col-sm-3">Modelo</dt>
|
||||
<dd class="col-sm-9">{{ $promptProject->model->name ?? 'N/A' }}</dd>
|
||||
</dl>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card shadow mb-4">
|
||||
<div class="card-header py-3">
|
||||
<h6 class="m-0 fw-bold text-primary">Gerar Prompt</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="POST" action="{{ route('prompt-projects.generate', $promptProject) }}">
|
||||
@csrf
|
||||
<div class="mb-3">
|
||||
<label for="model" class="form-label">Modelo</label>
|
||||
<select class="form-select" id="model" name="model" required>
|
||||
<option value="">Selecione...</option>
|
||||
@foreach($models ?? [] as $model)
|
||||
<option value="{{ $model->slug }}" {{ old('model', $promptProject->model->slug ?? '') == $model->slug ? 'selected' : '' }}>
|
||||
{{ $model->name }}
|
||||
</option>
|
||||
@endforeach
|
||||
<option value="outros">Outros</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3 d-none" id="custom-model-container">
|
||||
<label for="custom_model" class="form-label">Modelo Personalizado</label>
|
||||
<input type="text" class="form-control" id="custom_model" name="custom_model" placeholder="Ex: gpt-4, claude-3, etc.">
|
||||
</div>
|
||||
<button type="submit" class="btn btn-success"><i class="bi bi-play-circle"></i> Gerar</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if($generations->isNotEmpty())
|
||||
<div class="card shadow mb-4">
|
||||
<div class="card-header py-3">
|
||||
<h6 class="m-0 fw-bold text-primary">Gerações Recentes</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
@foreach($generations as $generation)
|
||||
<div class="card mb-3">
|
||||
<div class="card-header d-flex justify-content-between align-items-center">
|
||||
<span>
|
||||
<i class="bi bi-robot me-1"></i>
|
||||
{{ $generation->model->name ?? 'Modelo desconhecido' }}
|
||||
@if($generation->status === 'failed')
|
||||
<span class="badge bg-danger ms-2">Falha</span>
|
||||
@else
|
||||
<span class="badge bg-success ms-2">Sucesso</span>
|
||||
@endif
|
||||
</span>
|
||||
<small class="text-muted">{{ $generation->created_at->format('d/m/Y H:i') }}</small>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<h6 class="card-title">Prompt Enviado</h6>
|
||||
<pre class="bg-light p-3 rounded small mb-3" style="white-space: pre-wrap;">{{ $generation->generated_prompt }}</pre>
|
||||
|
||||
@if($generation->status === 'completed' && !empty($generation->output_data['content'] ?? ''))
|
||||
<div class="d-flex justify-content-between align-items-center mb-2">
|
||||
<h6 class="card-title mb-0">Resposta da IA</h6>
|
||||
<button class="btn btn-sm btn-outline-secondary copy-btn" type="button" data-target="response-{{ $generation->id }}">
|
||||
<i class="bi bi-clipboard me-1"></i> Copy
|
||||
</button>
|
||||
</div>
|
||||
<textarea class="form-control font-monospace small" id="response-{{ $generation->id }}" rows="12">{{ $generation->output_data['content'] }}</textarea>
|
||||
@elseif($generation->status === 'failed')
|
||||
<div class="alert alert-danger small">
|
||||
<strong>Erro:</strong> {{ $generation->output_data['error'] ?? 'Erro desconhecido' }}
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if($generation->tokens_used)
|
||||
<p class="text-muted small mt-2 mb-0">
|
||||
<i class="bi bi-lightning me-1"></i>Tokens: {{ $generation->tokens_used }} |
|
||||
<i class="bi bi-clock me-1"></i>Tempo: {{ $generation->generation_time }}s
|
||||
</p>
|
||||
@endif
|
||||
<form action="{{ route('prompt-generations.destroy', $generation) }}" method="POST" class="mt-2" onsubmit="return confirm('Tem certeza que deseja excluir esta geração?')">
|
||||
@csrf @method('DELETE')
|
||||
<button type="submit" class="btn btn-danger btn-sm"><i class="bi bi-trash"></i> Excluir</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@section('scripts')
|
||||
<script>
|
||||
document.getElementById('model').addEventListener('change', function() {
|
||||
const customContainer = document.getElementById('custom-model-container');
|
||||
if (this.value === 'outros') {
|
||||
customContainer.classList.remove('d-none');
|
||||
document.getElementById('custom_model').setAttribute('required', 'required');
|
||||
} else {
|
||||
customContainer.classList.add('d-none');
|
||||
document.getElementById('custom_model').removeAttribute('required');
|
||||
}
|
||||
});
|
||||
|
||||
document.querySelectorAll('.copy-btn').forEach(button => {
|
||||
button.addEventListener('click', function() {
|
||||
const targetId = this.getAttribute('data-target');
|
||||
const textarea = document.getElementById(targetId);
|
||||
if (textarea) {
|
||||
textarea.select();
|
||||
document.execCommand('copy');
|
||||
alert('Conteúdo copiado para a área de transferência!');
|
||||
}
|
||||
});
|
||||
});
|
||||
</script>
|
||||
@endsection
|
||||
@endsection
|
||||
@@ -0,0 +1,51 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('header', 'Novo Template')
|
||||
|
||||
@section('breadcrumbs')
|
||||
@php
|
||||
$breadcrumbs = [
|
||||
'Templates' => route('prompt-templates.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">Cadastrar Template</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="POST" action="{{ route('prompt-templates.store') }}">
|
||||
@csrf
|
||||
<div class="mb-3">
|
||||
<label for="name" class="form-label">Nome</label>
|
||||
<input type="text" class="form-control" id="name" name="name" value="{{ old('name') }}" required>
|
||||
@error('name') <div class="text-danger small">{{ $message }}</div> @enderror
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="description" class="form-label">Descrição</label>
|
||||
<textarea class="form-control" id="description" name="description" rows="2">{{ old('description') }}</textarea>
|
||||
@error('description') <div class="text-danger small">{{ $message }}</div> @enderror
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="category" class="form-label">Categoria</label>
|
||||
<input type="text" class="form-control" id="category" name="category" value="{{ old('category') }}">
|
||||
@error('category') <div class="text-danger small">{{ $message }}</div> @enderror
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="template_data" class="form-label">Dados do Template (JSON)</label>
|
||||
<textarea class="form-control font-monospace" id="template_data" name="template_data" rows="20" required>{{ old('template_data') }}</textarea>
|
||||
@error('template_data') <div class="text-danger small">{{ $message }}</div> @enderror
|
||||
</div>
|
||||
<div class="mb-3 form-check">
|
||||
<input type="checkbox" class="form-check-input" id="is_public" name="is_public" value="1" {{ old('is_public', true) ? 'checked' : '' }}>
|
||||
<label class="form-check-label" for="is_public">Público</label>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary"><i class="bi bi-save"></i> Salvar</button>
|
||||
<a href="{{ route('prompt-templates.index') }}" class="btn btn-secondary">Cancelar</a>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
@@ -0,0 +1,52 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('header', 'Editar Template')
|
||||
|
||||
@section('breadcrumbs')
|
||||
@php
|
||||
$breadcrumbs = [
|
||||
'Templates' => route('prompt-templates.index'),
|
||||
'Editar' => 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">Editar Template</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="POST" action="{{ route('prompt-templates.update', $promptTemplate) }}">
|
||||
@csrf
|
||||
@method('PUT')
|
||||
<div class="mb-3">
|
||||
<label for="name" class="form-label">Nome</label>
|
||||
<input type="text" class="form-control" id="name" name="name" value="{{ old('name', $promptTemplate->name) }}" required>
|
||||
@error('name') <div class="text-danger small">{{ $message }}</div> @enderror
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="description" class="form-label">Descrição</label>
|
||||
<textarea class="form-control" id="description" name="description" rows="2">{{ old('description', $promptTemplate->description) }}</textarea>
|
||||
@error('description') <div class="text-danger small">{{ $message }}</div> @enderror
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="category" class="form-label">Categoria</label>
|
||||
<input type="text" class="form-control" id="category" name="category" value="{{ old('category', $promptTemplate->category) }}">
|
||||
@error('category') <div class="text-danger small">{{ $message }}</div> @enderror
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="template_data" class="form-label">Dados do Template (JSON)</label>
|
||||
<textarea class="form-control font-monospace" id="template_data" name="template_data" rows="20" required>{{ old('template_data', json_encode($promptTemplate->template_data, JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)) }}</textarea>
|
||||
@error('template_data') <div class="text-danger small">{{ $message }}</div> @enderror
|
||||
</div>
|
||||
<div class="mb-3 form-check">
|
||||
<input type="checkbox" class="form-check-input" id="is_public" name="is_public" value="1" {{ old('is_public', $promptTemplate->is_public) ? 'checked' : '' }}>
|
||||
<label class="form-check-label" for="is_public">Público</label>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary"><i class="bi bi-save"></i> Salvar</button>
|
||||
<a href="{{ route('prompt-templates.index') }}" class="btn btn-secondary">Cancelar</a>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
@@ -0,0 +1,67 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('header', 'Templates')
|
||||
|
||||
@section('breadcrumbs')
|
||||
@php
|
||||
$breadcrumbs = [
|
||||
'Templates' => null
|
||||
];
|
||||
@endphp
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
<div class="card shadow mb-4">
|
||||
<div class="card-header py-3 d-flex justify-content-between align-items-center">
|
||||
<h6 class="m-0 fw-bold text-primary">Lista de Templates</h6>
|
||||
<a href="{{ route('prompt-templates.create') }}" class="btn btn-primary btn-sm">
|
||||
<i class="bi bi-plus-circle"></i> Novo Template
|
||||
</a>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
@if(isset($templates) && count($templates) > 0)
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Nome</th>
|
||||
<th>Categoria</th>
|
||||
<th>Status</th>
|
||||
<th>Ações</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($templates as $template)
|
||||
<tr>
|
||||
<td>{{ $template->name }}</td>
|
||||
<td>{{ $template->category ?? 'N/A' }}</td>
|
||||
<td>
|
||||
<span class="badge bg-{{ $template->is_public ? 'success' : 'secondary' }}">
|
||||
{{ $template->is_public ? 'Público' : 'Privado' }}
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<a href="{{ route('prompt-templates.edit', $template) }}" class="btn btn-warning btn-sm">
|
||||
<i class="bi bi-pencil"></i>
|
||||
</a>
|
||||
<form action="{{ route('prompt-templates.destroy', $template) }}" method="POST" class="d-inline" onsubmit="return confirm('Tem certeza?')">
|
||||
@csrf @method('DELETE')
|
||||
<button class="btn btn-danger btn-sm"><i class="bi bi-trash"></i></button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
@if(is_object($templates) && method_exists($templates, 'links'))
|
||||
<div class="mt-3">
|
||||
{{ $templates->links() }}
|
||||
</div>
|
||||
@endif
|
||||
@else
|
||||
<p class="text-muted">Nenhum template encontrado.</p>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
+205
@@ -0,0 +1,205 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="pt-BR">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Certificado de Conclusão - Curso de Engenharia de Prompt</title>
|
||||
<style>
|
||||
* {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'Georgia', 'Times New Roman', serif;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
padding: 40px;
|
||||
}
|
||||
|
||||
.certificate {
|
||||
background: white;
|
||||
border: 20px solid #667eea;
|
||||
border-radius: 20px;
|
||||
padding: 60px;
|
||||
text-align: center;
|
||||
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.certificate::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
bottom: 10px;
|
||||
border: 3px solid #ffd700;
|
||||
border-radius: 10px;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.logo {
|
||||
font-size: 24px;
|
||||
color: #667eea;
|
||||
font-weight: bold;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 48px;
|
||||
color: #667eea;
|
||||
font-weight: bold;
|
||||
margin: 30px 0;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 3px;
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
font-size: 24px;
|
||||
color: #666;
|
||||
margin-bottom: 40px;
|
||||
}
|
||||
|
||||
.certificate-text {
|
||||
font-size: 20px;
|
||||
color: #333;
|
||||
line-height: 1.8;
|
||||
margin: 30px 0;
|
||||
}
|
||||
|
||||
.student-name {
|
||||
font-size: 36px;
|
||||
color: #667eea;
|
||||
font-weight: bold;
|
||||
margin: 20px 0;
|
||||
padding: 20px;
|
||||
border-bottom: 3px solid #ffd700;
|
||||
display: inline-block;
|
||||
min-width: 400px;
|
||||
}
|
||||
|
||||
.course-name {
|
||||
font-size: 28px;
|
||||
color: #764ba2;
|
||||
font-weight: bold;
|
||||
margin: 30px 0;
|
||||
}
|
||||
|
||||
.details {
|
||||
font-size: 18px;
|
||||
color: #666;
|
||||
margin: 20px 0;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.score {
|
||||
font-size: 22px;
|
||||
color: #28a745;
|
||||
font-weight: bold;
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.footer {
|
||||
margin-top: 60px;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.signature {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.signature-line {
|
||||
width: 200px;
|
||||
border-bottom: 2px solid #333;
|
||||
margin: 10px auto;
|
||||
}
|
||||
|
||||
.signature-text {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.date {
|
||||
font-size: 16px;
|
||||
color: #666;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.badge {
|
||||
display: inline-block;
|
||||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||||
color: white;
|
||||
padding: 10px 30px;
|
||||
border-radius: 50px;
|
||||
font-size: 18px;
|
||||
margin-top: 30px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.certificate-id {
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
margin-top: 30px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="certificate">
|
||||
<div class="logo">Syspanel - GPrompt</div>
|
||||
|
||||
<div class="title">Certificado</div>
|
||||
<div class="subtitle">de Conclusão</div>
|
||||
|
||||
<div class="certificate-text">
|
||||
Certificamos que
|
||||
</div>
|
||||
|
||||
<div class="student-name">
|
||||
{{ $user->name }}
|
||||
</div>
|
||||
|
||||
<div class="certificate-text">
|
||||
concluiu com sucesso o curso
|
||||
</div>
|
||||
|
||||
<div class="course-name">
|
||||
Engenharia de Prompt
|
||||
</div>
|
||||
|
||||
<div class="details">
|
||||
Curso completo com 20 lições sobre técnicas avançadas de engenharia de prompt<br>
|
||||
para modelos de linguagem artificial
|
||||
</div>
|
||||
|
||||
<div class="score">
|
||||
Pontuação no Quiz: {{ $progress->quiz_score }}/10
|
||||
</div>
|
||||
|
||||
<div class="badge">
|
||||
✓ Aprovado
|
||||
</div>
|
||||
|
||||
<div class="footer">
|
||||
<div class="signature">
|
||||
<div class="signature-line"></div>
|
||||
<div class="signature-text">Syspanel - GPrompt</div>
|
||||
<div class="signature-text">www.syspanel.com.br</div>
|
||||
</div>
|
||||
|
||||
<div class="signature">
|
||||
<div class="signature-line"></div>
|
||||
<div class="signature-text">Data de Conclusão</div>
|
||||
<div class="signature-text">{{ $progress->quiz_completed_at ? $progress->quiz_completed_at->format('d/m/Y') : now()->format('d/m/Y') }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="certificate-id">
|
||||
Certificado ID: {{ strtoupper(Str::random(16)) }}<br>
|
||||
Emitido em: {{ now()->format('d/m/Y H:i:s') }}
|
||||
</div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
Executable
+111
@@ -0,0 +1,111 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('header', 'Curso de Engenharia de Prompt')
|
||||
|
||||
@section('breadcrumbs')
|
||||
@php
|
||||
$breadcrumbs = [
|
||||
'Tutorial' => route('tutorial.index')
|
||||
];
|
||||
@endphp
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
<div class="card shadow mb-4">
|
||||
<div class="card-header py-3">
|
||||
<h6 class="m-0 fw-bold text-primary">Curso Completo de Engenharia de Prompt</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-lg-8">
|
||||
<h2>Bem-vindo ao Curso de Engenharia de Prompt</h2>
|
||||
<p>Este curso completo contém <strong>20 lições</strong> que irão ensinar você a criar prompts eficazes para obter os melhores resultados de modelos de linguagem como GPT, Claude, Gemini e outros.</p>
|
||||
|
||||
<div class="alert alert-info mt-4">
|
||||
<h5><i class="bi bi-info-circle me-2"></i>Como funciona o curso?</h5>
|
||||
<ol class="mb-0">
|
||||
<li>Estude as <strong>20 lições</strong> do curso</li>
|
||||
<li>Cada lição contém exemplos práticos de prompts bons e ruins</li>
|
||||
<li>Ao final, faça o <strong>quiz com 10 perguntas</strong></li>
|
||||
<li>Acertando pelo menos <strong>6 de 10 perguntas</strong>, você receberá um <strong>Certificado de Conclusão em PDF</strong></li>
|
||||
</ol>
|
||||
</div>
|
||||
|
||||
<div class="mt-4">
|
||||
<h4>Conteúdo do Curso:</h4>
|
||||
<div class="list-group mt-3">
|
||||
@foreach($lessons as $lesson)
|
||||
<a href="{{ route('tutorial.lesson', $lesson->id) }}" class="list-group-item list-group-item-action">
|
||||
<div class="d-flex w-100 justify-content-between">
|
||||
<h5 class="mb-1">Lição {{ $lesson->order }}: {{ $lesson->title }}</h5>
|
||||
@if($progress && in_array($lesson->id, $progress->completed_lessons ?? []))
|
||||
<span class="badge bg-success rounded-pill"><i class="bi bi-check-circle me-1"></i>Concluída</span>
|
||||
@endif
|
||||
</div>
|
||||
</a>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-4 text-center">
|
||||
<a href="{{ route('tutorial.quiz') }}" class="btn btn-primary btn-lg">
|
||||
<i class="bi bi-question-circle me-2"></i>Fazer Quiz Final
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-4">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h6 class="m-0 fw-bold">Seu Progresso</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
@if($progress)
|
||||
<div class="mb-3">
|
||||
<strong>Lições Concluídas:</strong>
|
||||
<div class="progress mt-2">
|
||||
<div class="progress-bar" role="progressbar" style="width: {{ ($progress->completed_lessons ? count($progress->completed_lessons) : 0) / count($lessons) * 100 }}%">
|
||||
{{ count($progress->completed_lessons ?? []) }}/{{ count($lessons) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@if($progress->quiz_passed)
|
||||
<div class="alert alert-success">
|
||||
<i class="bi bi-trophy me-2"></i>
|
||||
<strong>Parabéns!</strong><br>
|
||||
Você passou no quiz com {{ $progress->quiz_score }}/10 acertos.
|
||||
@if($progress->certificate_generated_at)
|
||||
<br><a href="{{ route('tutorial.certificate') }}" class="btn btn-success btn-sm mt-2">
|
||||
<i class="bi bi-download me-1"></i>Baixar Certificado
|
||||
</a>
|
||||
@endif
|
||||
</div>
|
||||
@elseif($progress->quiz_completed_at)
|
||||
<div class="alert alert-danger">
|
||||
<i class="bi bi-x-circle me-2"></i>
|
||||
<strong>Quiz não aprovado</strong><br>
|
||||
Você acertou {{ $progress->quiz_score }}/10. É necessário acertar pelo menos 6.
|
||||
<br><a href="{{ route('tutorial.quiz') }}" class="btn btn-primary btn-sm mt-2">Tentar Novamente</a>
|
||||
</div>
|
||||
@else
|
||||
<a href="{{ route('tutorial.quiz') }}" class="btn btn-primary w-100">
|
||||
<i class="bi bi-question-circle me-2"></i>Fazer Quiz
|
||||
</a>
|
||||
@endif
|
||||
@else
|
||||
<p class="text-muted">Comece a estudar as lições para acompanhar seu progresso.</p>
|
||||
@if($lessons->count() > 0)
|
||||
<a href="{{ route('tutorial.lesson', $lessons->first()->id) }}" class="btn btn-primary w-100">
|
||||
@else
|
||||
<a href="#" class="btn btn-secondary w-100" disabled>
|
||||
@endif
|
||||
<i class="bi bi-play-circle me-2"></i>Começar Curso
|
||||
</a>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
Executable
+102
@@ -0,0 +1,102 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('header', 'Lição do Curso')
|
||||
|
||||
@section('breadcrumbs')
|
||||
@php
|
||||
$breadcrumbs = [
|
||||
'Tutorial' => route('tutorial.index'),
|
||||
'Lição ' . $lesson->order => route('tutorial.lesson', $lesson->id)
|
||||
];
|
||||
@endphp
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
<div class="card shadow mb-4">
|
||||
<div class="card-header py-3 d-flex justify-content-between align-items-center">
|
||||
<h6 class="m-0 fw-bold text-primary">Lição {{ $lesson->order }}: {{ $lesson->title }}</h6>
|
||||
<span class="badge bg-primary">{{ $lesson->order }}/{{ count($lessons) }}</span>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-lg-8">
|
||||
<div class="lesson-content">
|
||||
{!! nl2br(e($lesson->content)) !!}
|
||||
</div>
|
||||
|
||||
@if($lesson->example_bad || $lesson->example_good)
|
||||
<div class="mt-4">
|
||||
<h4>Exemplos Práticos</h4>
|
||||
|
||||
@if($lesson->example_bad)
|
||||
<div class="alert alert-warning mt-3">
|
||||
<strong><i class="bi bi-exclamation-triangle me-2"></i>Exemplo Ruim:</strong>
|
||||
<pre class="mb-0 mt-2"><code>{{ $lesson->example_bad }}</code></pre>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
@if($lesson->example_good)
|
||||
<div class="alert alert-success mt-3">
|
||||
<strong><i class="bi bi-check-circle me-2"></i>Exemplo Bom:</strong>
|
||||
<pre class="mb-0 mt-2"><code>{{ $lesson->example_good }}</code></pre>
|
||||
</div>
|
||||
@endif
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="mt-4 d-flex justify-content-between">
|
||||
@if($lesson->order > 1)
|
||||
<?php
|
||||
$previousLesson = $lessons->where('order', $lesson->order - 1)->first();
|
||||
?>
|
||||
@if($previousLesson)
|
||||
<a href="{{ route('tutorial.lesson', $previousLesson->id) }}" class="btn btn-secondary">
|
||||
<i class="bi bi-arrow-left me-2"></i>Líção Anterior
|
||||
</a>
|
||||
@endif
|
||||
@else
|
||||
<div></div>
|
||||
@endif
|
||||
|
||||
@if($lesson->order < count($lessons))
|
||||
<?php
|
||||
$nextLesson = $lessons->where('order', $lesson->order + 1)->first();
|
||||
?>
|
||||
@if($nextLesson)
|
||||
<a href="{{ route('tutorial.lesson', $nextLesson->id) }}" class="btn btn-primary">
|
||||
Próxima Lição<i class="bi bi-arrow-right ms-2"></i>
|
||||
</a>
|
||||
@endif
|
||||
@else
|
||||
<a href="{{ route('tutorial.quiz') }}" class="btn btn-success">
|
||||
Fazer Quiz Final<i class="bi bi-arrow-right ms-2"></i>
|
||||
</a>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-lg-4">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h6 class="m-0 fw-bold">Navegação</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="list-group">
|
||||
@foreach($lessons as $l)
|
||||
<a href="{{ route('tutorial.lesson', $l->id) }}" class="list-group-item list-group-item-action {{ $l->id === $lesson->id ? 'active' : '' }}">
|
||||
<div class="d-flex w-100 justify-content-between">
|
||||
<small>Líção {{ $l->order }}</small>
|
||||
@if($progress && in_array($l->id, $progress->completed_lessons ?? []))
|
||||
<i class="bi bi-check-circle text-success"></i>
|
||||
@endif
|
||||
</div>
|
||||
<small>{{ $l->title }}</small>
|
||||
</a>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
Executable
+66
@@ -0,0 +1,66 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('header', 'Quiz do Curso')
|
||||
|
||||
@section('breadcrumbs')
|
||||
@php
|
||||
$breadcrumbs = [
|
||||
'Tutorial' => route('tutorial.index'),
|
||||
'Quiz' => route('tutorial.quiz')
|
||||
];
|
||||
@endphp
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
<div class="card shadow mb-4">
|
||||
<div class="card-header py-3">
|
||||
<h6 class="m-0 fw-bold text-primary">Quiz Final - Engenharia de Prompt</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="alert alert-info">
|
||||
<h5><i class="bi bi-info-circle me-2"></i>Instruções</h5>
|
||||
<p class="mb-0">Este quiz contém <strong>10 perguntas</strong> sobre o conteúdo do curso. Você precisa acertar pelo menos <strong>6 de 10</strong> para ser aprovado e receber o certificado de conclusão.</p>
|
||||
</div>
|
||||
|
||||
@if($progress && $progress->quiz_completed_at)
|
||||
<div class="alert alert-warning">
|
||||
<i class="bi bi-exclamation-triangle me-2"></i>
|
||||
<strong>Atenção!</strong> Você já realizou este quiz. Sua última tentativa: <strong>{{ $progress->quiz_score }}/10</strong>.
|
||||
@if($progress->quiz_passed)
|
||||
<span class="text-success">(Aprovado)</span>
|
||||
@else
|
||||
<span class="text-danger">(Reprovado)</span>
|
||||
@endif
|
||||
<br>Tente novamente para melhorar sua pontuação.
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<form method="POST" action="{{ route('tutorial.quiz.submit') }}" class="mt-4">
|
||||
@csrf
|
||||
@foreach($questions as $index => $question)
|
||||
<div class="card mb-3">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Pergunta {{ $index + 1 }} de {{ count($questions) }}</h5>
|
||||
<p class="card-text">{{ $question->question }}</p>
|
||||
|
||||
@foreach($question->options as $option)
|
||||
<div class="form-check">
|
||||
<input class="form-check-input" type="radio" name="answers[{{ $question->id }}]" value="{{ $option }}" id="q{{ $question->id }}_{{ $loop->index }}" required>
|
||||
<label class="form-check-label" for="q{{ $question->id }}_{{ $loop->index }}">
|
||||
{{ $option }}
|
||||
</label>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
|
||||
<div class="text-center mt-4">
|
||||
<button type="submit" class="btn btn-primary btn-lg">
|
||||
<i class="bi bi-check-circle me-2"></i>Enviar Respostas
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
Executable
+79
@@ -0,0 +1,79 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('header', 'Resultado do Quiz')
|
||||
|
||||
@section('breadcrumbs')
|
||||
@php
|
||||
$breadcrumbs = [
|
||||
'Tutorial' => route('tutorial.index'),
|
||||
'Resultado' => route('tutorial.quiz')
|
||||
];
|
||||
@endphp
|
||||
@endsection
|
||||
|
||||
@section('content')
|
||||
<div class="card shadow mb-4">
|
||||
<div class="card-header py-3">
|
||||
<h6 class="m-0 fw-bold text-primary">Resultado do Quiz</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
@if($passed)
|
||||
<div class="alert alert-success">
|
||||
<h4 class="alert-heading"><i class="bi bi-trophy me-2"></i>Parabéns!</h4>
|
||||
<p>Você acertou <strong>{{ $score }}</strong> de {{ count($results) }} perguntas e foi aprovado no quiz!</p>
|
||||
<hr>
|
||||
<p class="mb-0">Agora você pode baixar seu certificado de conclusão do curso.</p>
|
||||
</div>
|
||||
|
||||
<div class="text-center mt-4">
|
||||
<a href="{{ route('tutorial.certificate') }}" class="btn btn-success btn-lg">
|
||||
<i class="bi bi-download me-2"></i>Baixar Certificado PDF
|
||||
</a>
|
||||
</div>
|
||||
@else
|
||||
<div class="alert alert-danger">
|
||||
<h4 class="alert-heading"><i class="bi bi-x-circle me-2"></i>Não foi desta vez</h4>
|
||||
<p>Você acertou <strong>{{ $score }}</strong> de {{ count($results) }} perguntas. É necessário acertar pelo menos <strong>6 de 10</strong> para ser aprovado.</p>
|
||||
<hr>
|
||||
<p class="mb-0">Revise as lições e tente novamente!</p>
|
||||
</div>
|
||||
|
||||
<div class="text-center mt-4">
|
||||
<a href="{{ route('tutorial.index') }}" class="btn btn-primary btn-lg">
|
||||
<i class="bi bi-book me-2"></i>Revisar Lições
|
||||
</a>
|
||||
<a href="{{ route('tutorial.quiz') }}" class="btn btn-warning btn-lg ms-2">
|
||||
<i class="bi bi-arrow-repeat me-2"></i>Tentar Novamente
|
||||
</a>
|
||||
</div>
|
||||
@endif
|
||||
|
||||
<div class="mt-5">
|
||||
<h4>Detalhes das Respostas</h4>
|
||||
@foreach($results as $index => $result)
|
||||
<div class="card mb-3 {{ $result['is_correct'] ? 'border-success' : 'border-danger' }}">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">
|
||||
Pergunta {{ $index + 1 }}
|
||||
@if($result['is_correct'])
|
||||
<i class="bi bi-check-circle text-success ms-2"></i>
|
||||
@else
|
||||
<i class="bi bi-x-circle text-danger ms-2"></i>
|
||||
@endif
|
||||
</h5>
|
||||
<p class="card-text">{{ $result['question'] }}</p>
|
||||
|
||||
<div class="mt-3">
|
||||
<p><strong>Sua resposta:</strong> {{ $result['your_answer'] ?? 'Não respondida' }}</p>
|
||||
@if(!$result['is_correct'])
|
||||
<p class="text-success"><strong>Resposta correta:</strong> {{ $result['correct_answer'] }}</p>
|
||||
@endif
|
||||
<p class="text-muted"><strong>Explicação:</strong> {{ $result['explanation'] }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
Executable
+78
@@ -0,0 +1,78 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('header', 'Editar Usuário')
|
||||
|
||||
@section('breadcrumbs')
|
||||
@php
|
||||
$breadcrumbs = [
|
||||
'Usuários' => route('users.index'),
|
||||
$user->name => 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">Editar Usuário</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form method="POST" action="{{ route('users.update', $user) }}">
|
||||
@csrf
|
||||
@method('PUT')
|
||||
<div class="mb-3">
|
||||
<label for="name" class="form-label">Nome</label>
|
||||
<input type="text" class="form-control" id="name" name="name" value="{{ old('name', $user->name) }}" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="email" class="form-label">E-mail</label>
|
||||
<input type="email" class="form-control" id="email" name="email" value="{{ old('email', $user->email) }}" required>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="role" class="form-label">Papel</label>
|
||||
<select class="form-select" id="role" name="role" required>
|
||||
<option value="user" {{ old('role', $user->role) === 'user' ? 'selected' : '' }}>Usuário</option>
|
||||
<option value="superadmin" {{ old('role', $user->role) === 'superadmin' ? 'selected' : '' }}>SuperAdmin</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="is_approved" class="form-label">Status</label>
|
||||
<select class="form-select" id="is_approved" name="is_approved" required>
|
||||
<option value="1" {{ old('is_approved', $user->is_approved) ? 'selected' : '' }}>Aprovado</option>
|
||||
<option value="0" {{ !old('is_approved', $user->is_approved) ? 'selected' : '' }}>Pendente</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="mb-3">
|
||||
<label for="password" class="form-label">Nova Senha</label>
|
||||
<div class="input-group">
|
||||
<input type="password" class="form-control" id="password" name="password" placeholder="Deixe em branco para manter a senha atual">
|
||||
<button class="btn btn-outline-secondary" type="button" id="togglePassword">
|
||||
<i class="bi bi-eye"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
<button type="submit" class="btn btn-primary"><i class="bi bi-check-circle"></i> Salvar</button>
|
||||
<a href="{{ route('users.index') }}" class="btn btn-secondary">Cancelar</a>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@section('scripts')
|
||||
<script>
|
||||
document.getElementById('togglePassword').addEventListener('click', function() {
|
||||
const passwordInput = document.getElementById('password');
|
||||
const icon = this.querySelector('i');
|
||||
|
||||
if (passwordInput.type === 'password') {
|
||||
passwordInput.type = 'text';
|
||||
icon.classList.remove('bi-eye');
|
||||
icon.classList.add('bi-eye-slash');
|
||||
} else {
|
||||
passwordInput.type = 'password';
|
||||
icon.classList.remove('bi-eye-slash');
|
||||
icon.classList.add('bi-eye');
|
||||
}
|
||||
});
|
||||
</script>
|
||||
@endsection
|
||||
Executable
+84
@@ -0,0 +1,84 @@
|
||||
@extends('layouts.app')
|
||||
|
||||
@section('header', 'Usuários do Sistema')
|
||||
|
||||
@section('breadcrumbs')
|
||||
@php
|
||||
$breadcrumbs = [
|
||||
'Usuários' => 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">Lista de Usuários</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
@if($users->count() > 0)
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Nome</th>
|
||||
<th>E-mail</th>
|
||||
<th>Papel</th>
|
||||
<th>Status</th>
|
||||
<th>Gerações</th>
|
||||
<th>Tokens</th>
|
||||
<th>Cadastro</th>
|
||||
<th>Ações</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
@foreach($users as $user)
|
||||
<tr>
|
||||
<td>{{ $user->name }}</td>
|
||||
<td>{{ $user->email }}</td>
|
||||
<td>
|
||||
@if($user->role === 'superadmin')
|
||||
<span class="badge bg-danger">SuperAdmin</span>
|
||||
@elseif($user->role === 'user')
|
||||
<span class="badge bg-primary">Usuário</span>
|
||||
@else
|
||||
<span class="badge bg-secondary">{{ $user->role }}</span>
|
||||
@endif
|
||||
</td>
|
||||
<td>
|
||||
@if($user->is_approved)
|
||||
<span class="badge bg-success">Aprovado</span>
|
||||
@else
|
||||
<span class="badge bg-warning text-dark">Pendente</span>
|
||||
@endif
|
||||
</td>
|
||||
<td>{{ $user->generations_count }}</td>
|
||||
<td>{{ number_format($user->tokens_used, 0, ',', '.') }}</td>
|
||||
<td>{{ $user->created_at->format('d/m/Y H:i') }}</td>
|
||||
<td>
|
||||
<a href="{{ route('users.edit', $user) }}" class="btn btn-warning btn-sm me-1">
|
||||
<i class="bi bi-pencil"></i> Editar
|
||||
</a>
|
||||
@if($user->id !== auth()->id())
|
||||
<form action="{{ route('users.destroy', $user) }}" method="POST" class="d-inline" onsubmit="return confirm('Tem certeza que deseja excluir este usuário?')">
|
||||
@csrf @method('DELETE')
|
||||
<button class="btn btn-danger btn-sm">
|
||||
<i class="bi bi-trash"></i> Excluir
|
||||
</button>
|
||||
</form>
|
||||
@endif
|
||||
</td>
|
||||
</tr>
|
||||
@endforeach
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="mt-3">
|
||||
{{ $users->links() }}
|
||||
</div>
|
||||
@else
|
||||
<p class="text-muted">Nenhum usuário encontrado.</p>
|
||||
@endif
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
Executable
+112
@@ -0,0 +1,112 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<meta name="csrf-token" content="{{ csrf_token() }}">
|
||||
<title>GPrompt - Bem-vindo</title>
|
||||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
|
||||
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500;700&display=swap" rel="stylesheet">
|
||||
<style>
|
||||
body { font-family: 'Roboto', sans-serif; }
|
||||
.hero-section { background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); color: white; padding: 120px 0; }
|
||||
.feature-card { transition: transform 0.3s; border: none; box-shadow: 0 2px 15px rgba(0,0,0,0.1); }
|
||||
.feature-card:hover { transform: translateY(-5px); }
|
||||
.feature-icon { font-size: 3rem; color: #4e73df; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="hero-section text-center">
|
||||
<div class="container">
|
||||
<h1 class="display-4 fw-bold mb-4">GPrompt</h1>
|
||||
<p class="lead mb-5">Sistema Inteligente de Geração de Prompts com IA</p>
|
||||
@auth
|
||||
<a href="{{ route('dashboard') }}" class="btn btn-light btn-lg me-2"><i class="bi bi-speedometer2"></i> Acessar Dashboard</a>
|
||||
@else
|
||||
<a href="{{ route('login') }}" class="btn btn-light btn-lg me-2"><i class="bi bi-box-arrow-in-right"></i> Entrar</a>
|
||||
@if (Route::has('register'))
|
||||
<a href="{{ route('register') }}" class="btn btn-outline-light btn-lg"><i class="bi bi-person-plus"></i> Cadastrar</a>
|
||||
@endif
|
||||
@endauth
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="py-5 bg-light">
|
||||
<div class="container">
|
||||
<div class="text-center mb-5">
|
||||
<h2 class="fw-bold">Recursos do Sistema</h2>
|
||||
<p class="text-muted">Tudo que você precisa para criar prompts profissionais</p>
|
||||
</div>
|
||||
<div class="row g-4">
|
||||
<div class="col-md-4">
|
||||
<div class="card feature-card h-100 p-4 text-center">
|
||||
<div class="card-body">
|
||||
<i class="bi bi-folder2-open feature-icon mb-3"></i>
|
||||
<h5 class="card-title">Projetos de Prompt</h5>
|
||||
<p class="card-text text-muted">Organize seus prompts em projetos com variáveis e exemplos.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="card feature-card h-100 p-4 text-center">
|
||||
<div class="card-body">
|
||||
<i class="bi bi-cloud feature-icon mb-3"></i>
|
||||
<h5 class="card-title">Multi-Provedores</h5>
|
||||
<p class="card-text text-muted">Conecte-se a OpenRouter, StepFun e modelos locais via Llama.cpp.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-md-4">
|
||||
<div class="card feature-card h-100 p-4 text-center">
|
||||
<div class="card-body">
|
||||
<i class="bi bi-clock-history feature-icon mb-3"></i>
|
||||
<h5 class="card-title">Histórico Completo</h5>
|
||||
<p class="card-text text-muted">Acompanhe todas as gerações com métricas de tokens e temperatura.</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="py-5">
|
||||
<div class="container">
|
||||
<div class="row align-items-center">
|
||||
<div class="col-lg-6">
|
||||
<h2 class="fw-bold mb-3">Engenharia de Prompt Simplificada</h2>
|
||||
<p class="lead text-muted">Crie prompts profissionais com estrutura completa:</p>
|
||||
<ul class="list-unstyled">
|
||||
<li class="mb-2"><i class="bi bi-check-circle-fill text-success me-2"></i>Objetivo e Contexto</li>
|
||||
<li class="mb-2"><i class="bi bi-check-circle-fill text-success me-2"></i>Persona e Tom</li>
|
||||
<li class="mb-2"><i class="bi bi-check-circle-fill text-success me-2"></i>Restrições e Formato</li>
|
||||
<li class="mb-2"><i class="bi bi-check-circle-fill text-success me-2"></i>Exemplos e Variáveis</li>
|
||||
<li class="mb-2"><i class="bi bi-check-circle-fill text-success me-2"></i>Temperatura e Max Tokens</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="col-lg-6 mt-4 mt-lg-0">
|
||||
<div class="card shadow">
|
||||
<div class="card-body">
|
||||
<h5 class="card-title"><i class="bi bi-lightbulb"></i> Por que GPrompt?</h5>
|
||||
<p class="card-text">Sistema completo para gerenciar provedores de IA, criar projetos de prompt estruturados e gerar respostas de alta qualidade. Tudo em um único lugar, com interface moderna e responsiva.</p>
|
||||
@auth
|
||||
<a href="{{ route('prompt-projects.create') }}" class="btn btn-primary"><i class="bi bi-plus-circle"></i> Criar Primeiro Projeto</a>
|
||||
@else
|
||||
<a href="{{ route('register') }}" class="btn btn-primary"><i class="bi bi-person-plus"></i> Começar Agora</a>
|
||||
@endauth
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
@include('components.app-footer')
|
||||
|
||||
@include('components.cookie-consent-banner')
|
||||
|
||||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.2/dist/js/bootstrap.bundle.min.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user