First Commit

This commit is contained in:
marco
2026-09-01 07:49:48 -03:00
commit 28b1044038
399 changed files with 41659 additions and 0 deletions
+80
View File
@@ -0,0 +1,80 @@
@extends("layouts.app")
@section("title", __("Edit Custom Subject"))
@section("header")
<div class="flex justify-between items-center">
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
{{ __("Edit Custom Subject") }}
</h2>
</div>
@endsection
@section("content")
<div class="py-6">
<div class="max-w-3xl mx-auto sm:px-6 lg:px-8">
@if(session("success"))
<div class="bg-green-100 border border-green-400 text-green-700 px-4 py-3 rounded-lg mb-6">
{{ session("success") }}
</div>
@endif
@if(session("error"))
<div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded-lg mb-6">
{{ session("error") }}
</div>
@endif
<div class="bg-white rounded-xl shadow-sm border border-gray-100 p-6">
<div class="mb-6">
<h3 class="text-lg font-semibold text-gray-900">{{ $customSubject->name }}</h3>
<p class="text-sm text-gray-500">{{ __("Created") }}: {{ $customSubject->created_at->format("d/m/Y H:i") }}</p>
</div>
<form action="{{ route("admin.custom-subjects.update", $customSubject) }}" method="POST">
@csrf
@method("PUT")
<div class="mb-4">
<label for="name" class="block text-sm font-medium text-gray-700 mb-2">{{ __("Subject Name") }} *</label>
<input type="text"
name="name"
id="name"
value="{{ old("name", $customSubject->name) }}"
required
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500 @error("name") border-red-500 @enderror">
@error("name")
<p class="text-red-500 text-xs mt-1">{{ $message }}</p>
@enderror
</div>
<div class="mb-6">
<div class="flex items-center">
<input type="checkbox"
name="is_active"
id="is_active"
value="1"
{{ old("is_active", $customSubject->is_active) ? "checked" : "" }}
class="h-4 w-4 text-indigo-600 focus:ring-indigo-500 border-gray-300 rounded">
<label for="is_active" class="ml-2 block text-sm text-gray-900">
{{ __("Active") }}
</label>
</div>
<p class="text-xs text-gray-500 mt-1">{{ __("Inactive subjects will not receive scheduled insights.") }}</p>
</div>
<div class="flex items-center gap-3">
<button type="submit"
class="px-4 py-2 bg-indigo-600 text-white text-sm font-medium rounded-lg hover:bg-indigo-700 transition">
{{ __("Save Changes") }}
</button>
<a href="{{ route("admin.custom-subjects.index") }}"
class="px-4 py-2 bg-gray-200 text-gray-700 text-sm font-medium rounded-lg hover:bg-gray-300 transition">
{{ __("Cancel") }}
</a>
</div>
</form>
</div>
</div>
</div>
@endsection
+137
View File
@@ -0,0 +1,137 @@
@extends("layouts.app")
@section("title", __("My Custom Subjects"))
@section("header")
<div class="flex justify-between items-center">
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
{{ __("My Custom Subjects") }}
</h2>
</div>
@endsection
@section("content")
<div class="py-6">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
@if(session("success"))
<div class="bg-green-100 border border-green-400 text-green-700 px-4 py-3 rounded-lg mb-6">
{{ session("success") }}
</div>
@endif
@if(session("error"))
<div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded-lg mb-6">
{{ session("error") }}
</div>
@endif
<!-- Upload Form -->
<div class="bg-white rounded-xl shadow-sm border border-gray-100 p-6 mb-6">
<h3 class="text-lg font-semibold text-gray-900 mb-4">{{ __("Upload PDF") }}</h3>
<form action="{{ route("admin.custom-subjects.store") }}" method="POST" enctype="multipart/form-data">
@csrf
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
<div class="md:col-span-1">
<label for="name" class="block text-sm font-medium text-gray-700 mb-2">{{ __("Subject Name") }} *</label>
<input type="text" name="name" id="name" value="{{ old("name") }}" required
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500 @error("name") border-red-500 @enderror">
@error("name")
<p class="text-red-500 text-xs mt-1">{{ $message }}</p>
@enderror
</div>
<div class="md:col-span-1">
<label for="pdf" class="block text-sm font-medium text-gray-700 mb-2">{{ __("PDF File") }} *</label>
<input type="file" name="pdf" id="pdf" accept=".pdf,application/pdf" required
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500 @error("pdf") border-red-500 @enderror">
@error("pdf")
<p class="text-red-500 text-xs mt-1">{{ $message }}</p>
@enderror
<p class="text-xs text-gray-500 mt-1">{{ __("Maximum file size: 10MB") }}</p>
</div>
<div class="md:col-span-1 flex items-end">
<button type="submit"
class="w-full px-4 py-2 bg-indigo-600 text-white text-sm font-medium rounded-lg hover:bg-indigo-700 transition">
<svg class="w-4 h-4 inline mr-1.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12"/>
</svg>
{{ __("Upload and Process") }}
</button>
</div>
</div>
</form>
</div>
<!-- Custom Subjects List -->
<div class="bg-white rounded-xl shadow-sm border border-gray-100 p-6">
<h3 class="text-lg font-semibold text-gray-900 mb-4">{{ __("My Custom Subjects") }}</h3>
@if($customSubjects->count() === 0)
<p class="text-gray-500 text-center py-8">{{ __("No custom subjects yet. Upload a PDF to get started.") }}</p>
@else
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">{{ __("Name") }}</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">{{ __("Status") }}</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">{{ __("Created") }}</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">{{ __("Actions") }}</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200">
@foreach($customSubjects as $subject)
<tr class="hover:bg-gray-50">
<td class="px-6 py-4 whitespace-nowrap font-medium text-gray-900">{{ $subject->name }}</td>
<td class="px-6 py-4 whitespace-nowrap">
@if($subject->processing_status === "completed")
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-green-100 text-green-800">
{{ __("Ready") }}
</span>
@elseif($subject->processing_status === "processing")
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-yellow-100 text-yellow-800">
{{ __("Processing") }}
</span>
@elseif($subject->processing_status === "failed")
<span class="px-2 inline-flex text-xs leading-5 font-semibold rounded-full bg-red-100 text-red-800">
{{ __("Failed") }}
</span>
@endif
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
{{ $subject->created_at->format("d/m/Y H:i") }}
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium">
@if($subject->processing_status === "completed")
<a href="{{ route("admin.custom-subjects.edit", $subject) }}"
class="text-indigo-600 hover:text-indigo-900 mr-3">
{{ __("Edit") }}
</a>
<a href="{{ route("admin.custom-subjects.show", $subject) }}"
class="text-indigo-600 hover:text-indigo-900 mr-3">
{{ __("Ask Questions") }}
</a>
@else
<a href="{{ route("admin.custom-subjects.edit", $subject) }}"
class="text-indigo-600 hover:text-indigo-900 mr-3">
{{ __("Edit") }}
</a>
@endif
<form action="{{ route("admin.custom-subjects.destroy", $subject) }}" method="POST" class="inline"
onsubmit="return confirm('{{ __("Delete this custom subject? This action cannot be undone.") }}')">
@csrf
@method("DELETE")
<button type="submit" class="text-red-600 hover:text-red-900">
{{ __("Delete") }}
</button>
</form>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
@endif
</div>
</div>
</div>
@endsection
+126
View File
@@ -0,0 +1,126 @@
@extends("layouts.app")
@section("title", $customSubject->name)
@section("header")
<div class="flex justify-between items-center">
<div>
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
{{ $customSubject->name }}
</h2>
<p class="text-sm text-gray-500 mt-1">{{ __("Ask questions about the PDF content") }}</p>
</div>
<div class="flex gap-2">
<a href="{{ route("admin.custom-subjects.index") }}"
class="inline-flex items-center px-4 py-2 bg-gray-100 text-gray-700 text-sm font-medium rounded-lg hover:bg-gray-200 transition">
<svg class="w-4 h-4 mr-1.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18"/>
</svg>
{{ __("Back") }}
</a>
</div>
</div>
@endsection
@section("content")
<div class="py-6">
<div class="max-w-4xl mx-auto sm:px-6 lg:px-8">
@if(session("success"))
<div class="bg-green-100 border border-green-400 text-green-700 px-4 py-3 rounded-lg mb-6">
{{ session("success") }}
</div>
@endif
@if(session("error"))
<div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded-lg mb-6">
{{ session("error") }}
</div>
@endif
<!-- Q&A Form -->
<div class="bg-white rounded-xl shadow-sm border border-gray-100 p-6 mb-6">
<form action="{{ route("admin.custom-subjects.ask", $customSubject) }}" method="POST">
@csrf
<div class="mb-4">
<label for="question" class="block text-sm font-medium text-gray-700 mb-2">{{ __("Your Question") }}</label>
<textarea name="question" id="question" rows="3" required
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500 @error("question") border-red-500 @enderror"
placeholder="{{ __("Ask something about the PDF content...") }}">{{ old("question") }}</textarea>
@error("question")
<p class="text-red-500 text-xs mt-1">{{ $message }}</p>
@enderror
</div>
<div class="bg-blue-50 border-l-4 border-blue-400 p-3 mb-4">
<p class="text-sm text-blue-700">
{{ __("Tip: Always specify the desired language in your question, e.g. \"Answer in English\" or \"Responda em português\".") }}
</p>
</div>
<div class="flex justify-end">
<button type="submit"
class="inline-flex items-center px-4 py-2 bg-indigo-600 text-white text-sm font-medium rounded-lg hover:bg-indigo-700 transition">
<svg class="w-4 h-4 mr-1.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"/>
</svg>
{{ __("Ask") }}
</button>
</div>
</form>
</div>
<!-- Questions History -->
<div class="bg-white rounded-xl shadow-sm border border-gray-100 p-6">
<h3 class="text-lg font-semibold text-gray-900 mb-4">{{ __("Questions History") }}</h3>
@if($questions->count() === 0)
<p class="text-gray-500 text-center py-8">{{ __("No questions asked yet.") }}</p>
@else
<div class="space-y-4">
@foreach($questions as $q)
<div class="border border-gray-200 rounded-lg p-4">
<div class="flex items-start gap-3">
<div class="flex-shrink-0 w-8 h-8 bg-indigo-100 rounded-full flex items-center justify-center">
<svg class="w-4 h-4 text-indigo-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8.228 9c.549-1.165 2.03-2 3.772-2 2.21 0 4 1.343 4 3 0 1.4-1.278 2.575-3.006 2.907-.542.104-.994.54-.994 1.093m0 3h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
</svg>
</div>
<div class="flex-1 min-w-0">
<p class="text-sm font-medium text-gray-900">{{ $q->question }}</p>
<p class="text-xs text-gray-500 mt-1">{{ $q->asked_at->format("d/m/Y H:i") }}</p>
</div>
</div>
@if($q->answer)
<div class="mt-3 ml-11 p-3 bg-gray-50 rounded-lg">
<div class="prose prose-sm max-w-none text-gray-700">
{!! nl2br(e($q->answer)) !!}
</div>
</div>
@endif
<div class="flex items-center gap-2 ml-11 mt-2">
<form action="{{ route('admin.custom-subjects.questions.translate', [$customSubject, $q]) }}" method="POST" class="flex items-center gap-1">
@csrf
<select name="target_language" class="text-xs border border-gray-300 rounded-lg px-2 py-1 focus:outline-none focus:ring-2 focus:ring-indigo-500">
<option value="pt_BR" {{ app()->getLocale() === 'pt_BR' ? 'selected' : '' }}>PT</option>
<option value="en" {{ app()->getLocale() === 'en' ? 'selected' : '' }}>EN</option>
<option value="es" {{ app()->getLocale() === 'es' ? 'selected' : '' }}>ES</option>
</select>
<button type="submit" class="text-xs text-indigo-500 hover:text-indigo-700 font-medium" title="{{ __('Translate') }}">
{{ __('Translate') }}
</button>
</form>
<form action="{{ route('admin.custom-subjects.questions.destroy', [$customSubject, $q]) }}" method="POST"
onsubmit="return confirm('{{ __("Delete this question?") }}')">
@csrf
@method('DELETE')
<button type="submit" class="text-xs text-red-500 hover:text-red-700">
{{ __("Delete") }}
</button>
</form>
</div>
</div>
@endforeach
</div>
@endif
</div>
</div>
</div>
@endsection
+311
View File
@@ -0,0 +1,311 @@
@extends("layouts.app")
@section("title", __("Insights"))
@section("header")
<div class="flex justify-between items-center">
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
{{ __("My Insights") }}
</h2>
</div>
@endsection
@section("content")
<div class="py-6">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
@if(session("success"))
<div class="bg-green-100 border border-green-400 text-green-700 px-4 py-3 rounded-lg mb-6">
{{ session("success") }}
</div>
@endif
@if(session("error"))
<div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded-lg mb-6">
{{ session("error") }}
</div>
@endif
<!-- Token Usage Summary -->
<div class="bg-gradient-to-r from-indigo-500 to-purple-600 rounded-xl shadow-sm p-6 mb-6 text-white">
<div class="grid grid-cols-1 md:grid-cols-3 gap-6">
<div>
<p class="text-indigo-100 text-sm">{{ __("Total Input Tokens") }}</p>
<p class="text-2xl font-bold">{{ number_format($totalInputTokens) }}</p>
</div>
<div>
<p class="text-indigo-100 text-sm">{{ __("Total Output Tokens") }}</p>
<p class="text-2xl font-bold">{{ number_format($totalOutputTokens) }}</p>
</div>
<div>
<p class="text-indigo-100 text-sm">{{ __("Total Tokens Used") }}</p>
<p class="text-2xl font-bold">{{ number_format($totalTokens) }}</p>
</div>
</div>
</div>
<!-- Generate Message -->
<div class="bg-white rounded-xl shadow-sm border border-gray-100 p-6 mb-6">
<h3 class="text-lg font-semibold text-gray-900 mb-4">{{ __("Generate AI Message") }}</h3>
<form action="{{ route("admin.insights.generate") }}" method="POST" class="space-y-4">
@csrf
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
<div>
<label for="subject_id" class="block text-sm font-medium text-gray-700 mb-2">{{ __("Subject") }}</label>
<select name="subject_id" id="subject_id" required
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500">
<option value="">{{ __("Select a subject...") }}</option>
@foreach($subjects as $subject)
<option value="{{ $subject->id }}" {{ $subjectId == $subject->id ? 'selected' : '' }}>
{{ $subject->name }}
</option>
@endforeach
</select>
</div>
<div>
<label for="message_type" class="block text-sm font-medium text-gray-700 mb-2">{{ __("Message Type") }}</label>
<select name="message_type" id="message_type"
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500">
@foreach(\App\Enums\MessageType::cases() as $case)
@if(in_array($case->value, ['insight', 'language_lesson', 'game_simulator', 'technical_teaching']))
<option value="{{ $case->value }}">{{ $case->label() }}</option>
@endif
@endforeach
</select>
</div>
<div>
<label for="custom_prompt" class="block text-sm font-medium text-gray-700 mb-2">{{ __("Custom Prompt (optional)") }}</label>
<input type="text" name="custom_prompt" id="custom_prompt"
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500"
placeholder="{{ __("e.g., Give me a practical tip about...") }}">
</div>
<div class="md:col-span-3">
<label class="block text-sm font-medium text-gray-700 mb-2">{{ __("Response Language") }}</label>
<div class="flex flex-wrap items-center gap-4">
<label class="inline-flex items-center gap-2 cursor-pointer">
<input type="radio" name="language" value="pt_BR" {{ app()->getLocale() === 'pt_BR' ? 'checked' : '' }}
class="h-4 w-4 text-indigo-600 border-gray-300 focus:ring-indigo-500">
<span class="text-sm text-gray-700">Português (Brasil)</span>
</label>
<label class="inline-flex items-center gap-2 cursor-pointer">
<input type="radio" name="language" value="en" {{ app()->getLocale() === 'en' ? 'checked' : '' }}
class="h-4 w-4 text-indigo-600 border-gray-300 focus:ring-indigo-500">
<span class="text-sm text-gray-700">English</span>
</label>
<label class="inline-flex items-center gap-2 cursor-pointer">
<input type="radio" name="language" value="es" {{ app()->getLocale() === 'es' ? 'checked' : '' }}
class="h-4 w-4 text-indigo-600 border-gray-300 focus:ring-indigo-500">
<span class="text-sm text-gray-700">Español</span>
</label>
</div>
</div>
</div>
<div class="flex justify-end">
<button type="submit" class="inline-flex items-center px-4 py-2 bg-indigo-600 text-white text-sm font-medium rounded-lg hover:bg-indigo-700 transition">
<svg class="w-4 h-4 mr-1.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"/>
</svg>
{{ __("Generate Message") }}
</button>
</div>
</form>
</div>
<!-- Filter -->
<div class="bg-white rounded-xl shadow-sm border border-gray-100 p-6 mb-6">
<div class="flex items-center gap-4">
<label class="text-sm font-medium text-gray-700">{{ __("Filter by Subject") }}</label>
<select onchange="window.location.href = '{{ route("admin.insights.index") }}?subject_id=' + this.value"
class="px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500 text-sm">
<option value="">{{ __("All Subjects") }}</option>
@foreach($subjects as $subject)
<option value="{{ $subject->id }}" {{ $subjectId == $subject->id ? 'selected' : '' }}>
{{ $subject->name }}
</option>
@endforeach
</select>
</div>
</div>
<!-- Insights List -->
<div class="bg-white rounded-xl shadow-sm border border-gray-100 p-6">
<h3 class="text-lg font-semibold text-gray-900 mb-4">{{ __("Messages History") }}</h3>
@forelse($insights as $insight)
<div class="border-b border-gray-100 pb-4 mb-4 last:border-0 last:mb-0 last:pb-0">
<div class="flex justify-between items-start">
<div class="flex-1 min-w-0">
<div class="flex items-center gap-2 mb-2">
<span class="text-xs font-medium text-indigo-600 bg-indigo-50 px-2 py-0.5 rounded-full">
{{ $insight->subject->name }}
</span>
<span class="text-xs bg-gray-100 text-gray-600 px-2 py-0.5 rounded-full">
{{ $insight->type->label() }}
</span>
@if($insight->input_tokens > 0 || $insight->output_tokens > 0)
<span class="text-xs text-gray-400">
🔤 {{ number_format($insight->input_tokens + $insight->output_tokens) }} tokens
</span>
@endif
</div>
<div class="text-sm text-gray-900 prose prose-sm max-w-none">
{!! \App\Services\MarkdownService::parse($insight->content) !!}
</div>
<p class="text-xs text-gray-400 mt-1">{{ $insight->created_at->format('d/m/Y H:i') }}</p>
</div>
<div class="flex items-center gap-2 ml-4">
<form action="{{ route("admin.insights.translate", $insight) }}" method="POST" class="flex items-center gap-1">
@csrf
<select name="target_language" class="text-xs border border-gray-300 rounded-lg px-2 py-1 focus:outline-none focus:ring-2 focus:ring-indigo-500">
<option value="pt_BR" {{ app()->getLocale() === 'pt_BR' ? 'selected' : '' }}>PT</option>
<option value="en" {{ app()->getLocale() === 'en' ? 'selected' : '' }}>EN</option>
<option value="es" {{ app()->getLocale() === 'es' ? 'selected' : '' }}>ES</option>
</select>
<button type="submit" class="text-xs text-indigo-500 hover:text-indigo-700 font-medium" title="{{ __('Translate') }}">
{{ __('Translate') }}
</button>
</form>
<form action="{{ route("admin.insights.destroy", $insight) }}" method="POST"
onsubmit="return confirm('{{ __("Delete this insight?") }}')">
@csrf
@method("DELETE")
<button type="submit" class="text-xs text-red-500 hover:text-red-700">
{{ __("Delete") }}
</button>
</form>
</div>
</div>
</div>
@empty
<div class="text-center py-8">
<svg class="w-12 h-12 text-gray-300 mx-auto mb-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.548.547A3.374 3.374 0 0014 18.469V19a2 2 0 11-4 0v-.531c0-.895-.356-1.754-.988-2.386l-.548-.547z"/>
</svg>
<p class="text-gray-500">{{ __("No messages yet. Generate your first message above!") }}</p>
</div>
@endforelse
@if(method_exists($insights, 'links'))
<div class="mt-6">
{{ $insights->links() }}
</div>
@endif
</div>
</div>
</div>
@endsection
@push('scripts')
<script>
(function() {
if (!('speechSynthesis' in window)) {
return;
}
var englishVoice = null;
function loadVoices() {
var voices = speechSynthesis.getVoices();
englishVoice = voices.find(function(v) {
return v.lang === 'en-US' || v.lang === 'en_GB' || v.lang.startsWith('en-');
}) || null;
}
loadVoices();
if (speechSynthesis.onvoiceschanged !== undefined) {
speechSynthesis.onvoiceschanged = loadVoices;
}
function attachAudioButtons() {
document.querySelectorAll('.tts[data-text]:not([data-tts-attached="1"])').forEach(function(el) {
el.dataset.ttsAttached = '1';
var btn = document.createElement('button');
btn.type = 'button';
btn.innerHTML = '&#127908;';
btn.title = @json(__('Listen'));
btn.className = 'ml-1 inline-flex items-center justify-center w-6 h-6 rounded-full bg-indigo-50 text-indigo-600 hover:bg-indigo-100 text-xs';
var isSpeaking = false;
btn.addEventListener('click', function() {
var text = el.getAttribute('data-text') || el.textContent || '';
if (!text.trim()) {
return;
}
try {
if (!('speechSynthesis' in window)) {
copyFallback(text);
return;
}
var voices = speechSynthesis.getVoices();
if (!voices || !voices.length) {
copyFallback(text);
return;
}
speechSynthesis.cancel();
var utter = new SpeechSynthesisUtterance(text);
var locale = @json(app()->getLocale());
var langMap = {
'pt_BR': 'pt-BR',
'en': 'en-US',
'es': 'es-ES'
};
utter.lang = langMap[locale] || 'en-US';
utter.rate = 0.9;
if (englishVoice) {
utter.voice = englishVoice;
}
utter.onerror = function(e) {
console.error('Speech error:', e);
copyFallback(text);
isSpeaking = false;
btn.classList.remove('bg-green-50', 'text-green-600');
btn.classList.add('bg-indigo-50', 'text-indigo-600');
};
utter.onend = function() {
isSpeaking = false;
btn.classList.remove('bg-green-50', 'text-green-600');
btn.classList.add('bg-indigo-50', 'text-indigo-600');
};
speechSynthesis.speak(utter);
isSpeaking = true;
btn.classList.remove('bg-indigo-50', 'text-indigo-600');
btn.classList.add('bg-green-50', 'text-green-600');
} catch (e) {
console.error('TTS error:', e);
copyFallback(text);
}
});
function copyFallback(text) {
if (!text.trim()) {
return;
}
if (navigator && navigator.clipboard && navigator.clipboard.writeText) {
navigator.clipboard.writeText(text).then(function() {
alert(@json(__('No voices available. Text copied to listen elsewhere: ')) + text);
}).catch(function() {
prompt(@json(__('No voices available. Copy the text to listen:')), text);
});
} else {
prompt(@json(__('No voices available. Copy the text to listen:')), text);
}
}
el.insertAdjacentElement('afterend', btn);
});
}
attachAudioButtons();
if (window.MutationObserver) {
var observer = new MutationObserver(function() {
attachAudioButtons();
});
observer.observe(document.body, { childList: true, subtree: true });
}
})();
</script>
@endpush
+128
View File
@@ -0,0 +1,128 @@
@extends("layouts.app")
@section("title", __("My Interests"))
@section("header")
<div class="flex justify-between items-center">
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
{{ __("My Interests") }}
</h2>
</div>
@endsection
@section("content")
<div class="py-6">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
@if(session("success"))
<div class="bg-green-100 border border-green-400 text-green-700 px-4 py-3 rounded-lg mb-6">
{{ session("success") }}
</div>
@endif
@if(session("error"))
<div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded-lg mb-6">
{{ session("error") }}
</div>
@endif
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
<!-- Left column: My Interests -->
<div class="lg:col-span-1">
<div class="bg-white rounded-xl shadow-sm border border-gray-100 p-6 mb-6">
<h3 class="text-lg font-semibold text-gray-900 mb-4">{{ __("Add Interest") }}</h3>
<form method="POST" action="{{ route("admin.interests.store") }}">
@csrf
<div class="mb-4">
<label for="interest" class="block text-sm font-medium text-gray-700 mb-2">{{ __("What topic are you interested in?") }}</label>
<input type="text" name="interest" id="interest" required
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500"
placeholder="{{ __("e.g., Business, Technology, Marketing") }}">
</div>
<button type="submit" class="w-full bg-indigo-600 text-white px-4 py-2 rounded-lg hover:bg-indigo-700 transition font-medium">
{{ __("Add Interest") }}
</button>
</form>
</div>
<div class="bg-white rounded-xl shadow-sm border border-gray-100 p-6">
<h3 class="text-lg font-semibold text-gray-900 mb-4">{{ __("My Interests") }}</h3>
@if($interests->count() > 0)
<div class="space-y-2">
@foreach($interests as $interest)
<div class="flex items-center justify-between p-3 bg-gray-50 rounded-lg">
<span class="text-sm text-gray-700">{{ $interest->interest }}</span>
<form action="{{ route("admin.interests.destroy", $interest) }}" method="POST" onsubmit="return confirm('{{ __("Are you sure?") }}')">
@csrf
@method("DELETE")
<button type="submit" class="text-xs text-red-600 hover:text-red-800 font-medium">
{{ __("Remove") }}
</button>
</form>
</div>
@endforeach
</div>
@else
<p class="text-sm text-gray-500">{{ __("You have not added any interests yet.") }}</p>
@endif
</div>
</div>
<!-- Right column: AI Suggestions -->
<div class="lg:col-span-2">
<div class="bg-white rounded-xl shadow-sm border border-gray-100 p-6">
<div class="flex justify-between items-center mb-6">
<div>
<h3 class="text-lg font-semibold text-gray-900">{{ __("AI Suggestions") }}</h3>
<p class="text-sm text-gray-500 mt-1">{{ __("Based on your interests, the AI suggests the following subjects") }}</p>
</div>
<form action="{{ route("admin.interests.generateSuggestions") }}" method="POST">
@csrf
<button type="submit" class="inline-flex items-center px-4 py-2 bg-purple-600 text-white text-sm font-medium rounded-lg hover:bg-purple-700 transition">
<svg class="w-4 h-4 mr-1.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"/>
</svg>
{{ __("Generate Suggestions") }}
</button>
</form>
</div>
@if(session("suggestions"))
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
@foreach(session("suggestions") as $suggestion)
<div class="border border-gray-200 rounded-lg p-5 hover:shadow-md transition">
<div class="flex items-start justify-between mb-3">
<h4 class="font-semibold text-gray-900">{{ $suggestion["name"] ?? "" }}</h4>
<span class="text-xs bg-purple-100 text-purple-800 px-2 py-1 rounded-full font-medium">{{ __("Suggestion") }}</span>
</div>
@if(!empty($suggestion["description"]))
<p class="text-sm text-gray-600 mb-3">{{ $suggestion["description"] }}</p>
@endif
@if(!empty($suggestion["introduction"]))
<p class="text-xs text-gray-500 italic mb-3">{{ \Illuminate\Support\Str::limit($suggestion["introduction"], 100) }}</p>
@endif
<form action="{{ route("admin.interests.createSubject") }}" method="POST" class="mt-3">
@csrf
<input type="hidden" name="name" value="{{ $suggestion["name"] ?? "" }}">
<input type="hidden" name="description" value="{{ $suggestion["description"] ?? "" }}">
<input type="hidden" name="introduction" value="{{ $suggestion["introduction"] ?? "" }}">
<button type="submit" class="w-full text-center text-sm bg-purple-600 text-white px-3 py-2 rounded-lg hover:bg-purple-700 transition font-medium">
{{ __("Create Automatically with AI") }}
</button>
</form>
</div>
@endforeach
</div>
@else
<div class="text-center py-12">
<svg class="w-12 h-12 text-gray-300 mx-auto mb-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9.663 17h4.673M12 3v1m6.364 1.636l-.707.707M21 12h-1M4 12H3m3.343-5.657l-.707-.707m2.828 9.9a5 5 0 117.072 0l-.548.547A3.374 3.374 0 0014 18.469V19a2 2 0 11-4 0v-.531c0-.895-.356-1.754-.988-2.386l-.548-.547z"/>
</svg>
<p class="text-gray-500">{{ __("Click 'Generate Suggestions' to receive AI-powered subject ideas based on your interests.") }}</p>
</div>
@endif
</div>
</div>
</div>
</div>
</div>
@endsection
+825
View File
@@ -0,0 +1,825 @@
@extends("layouts.app")
@section("title", __("Subjects"))
@section("header")
<div class="flex justify-between items-center">
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
{{ __("My Subjects") }}
</h2>
</div>
@endsection
@section("content")
<div class="py-6">
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
@if(session("success"))
<div class="bg-green-100 border border-green-400 text-green-700 px-4 py-3 rounded-lg mb-6">
{{ session("success") }}
</div>
@endif
@if(session("error"))
<div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded-lg mb-6">
{{ session("error") }}
</div>
@endif
<!-- System Subjects -->
<div class="bg-white rounded-xl shadow-sm border border-gray-100 p-6 mb-6">
<div class="flex justify-between items-center mb-6">
<div>
<h3 class="text-xl font-semibold text-gray-900">{{ __("System Subjects") }}</h3>
<p class="text-sm text-gray-500 mt-1">{{ __("Pre-registered subjects available to all users") }}</p>
</div>
@role('superadmin')
<button onclick="document.getElementById('generateAiSubjectModal').classList.remove('hidden')"
class="inline-flex items-center px-4 py-2 bg-purple-600 text-white text-sm font-medium rounded-lg hover:bg-purple-700 transition">
<svg class="w-4 h-4 mr-1.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"/>
</svg>
{{ __("Generate with AI") }}
</button>
@endrole
</div>
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
@forelse($systemSubjects as $subject)
@php
$userSubject = $subject->userSubjects->first();
@endphp
<div class="border border-gray-200 rounded-lg p-5 hover:shadow-md transition">
<div class="flex items-start justify-between mb-3">
<h4 class="font-semibold text-gray-900">{{ $subject->name }}</h4>
<div class="flex items-center gap-2">
<button onclick="openEditSystemSubjectModal({{ $subject->id }}, '{{ addslashes($subject->name) }}', '{{ addslashes($subject->description ?? '') }}', '{{ $subject->message_type ?? '' }}', '{{ addslashes($subject->custom_instructions ?? '') }}', {{ $subject->is_active ? 'true' : 'false' }})"
class="text-xs text-indigo-600 hover:text-indigo-800 font-medium">
{{ __("Edit") }}
</button>
<form action="{{ route("admin.subjects.destroy", $subject) }}" method="POST" onsubmit="return confirm('{{ __("Are you sure?") }}')" class="inline">
@csrf
@method("DELETE")
<button type="submit" class="text-xs text-red-600 hover:text-red-800 font-medium">
{{ __("Delete") }}
</button>
</form>
<span class="text-xs bg-blue-100 text-blue-800 px-2 py-1 rounded-full font-medium">{{ __("System") }}</span>
</div>
</div>
@if($subject->description)
<p class="text-sm text-gray-600 mb-3">{{ $subject->description }}</p>
@endif
@if($subject->introduction)
<p class="text-xs text-gray-500 italic mb-3">{{ Str::limit($subject->introduction, 100) }}</p>
@endif
@if($userSubject)
<div class="mt-4 pt-3 border-t border-gray-100">
<div class="flex items-center justify-between">
<div class="flex items-center gap-2">
<span class="w-2 h-2 rounded-full {{ $userSubject->is_active ? 'bg-green-500' : 'bg-gray-300' }}"></span>
<span class="text-sm {{ $userSubject->is_active ? 'text-green-700' : 'text-gray-500' }}">
{{ $userSubject->is_active ? __("Active") : __("Inactive") }}
</span>
</div>
<div class="flex gap-2">
<button onclick="openEditScheduleModal({{ $userSubject->id }}, '{{ addslashes($userSubject->frequency) }}', {{ json_encode($userSubject->preferred_times ?? ['09:00']) }}, {{ json_encode($userSubject->weekdays ?? []) }}, '{{ $userSubject->insight_language ?? 'en' }}', {{ json_encode($userSubject->delivery_channels ?? ['dashboard']) }})"
class="text-xs text-indigo-600 hover:text-indigo-800 font-medium">
{{ __("Edit Schedule") }}
</button>
<form action="{{ route("admin.subjects.toggle", $userSubject) }}" method="POST">
@csrf
<input type="hidden" name="is_active" value="{{ $userSubject->is_active ? '0' : '1' }}">
<button type="submit" class="text-xs {{ $userSubject->is_active ? 'text-red-600 hover:text-red-800' : 'text-green-600 hover:text-green-800' }} font-medium">
{{ $userSubject->is_active ? __("Deactivate") : __("Activate") }}
</button>
</form>
<form action="{{ route("admin.subjects.unsubscribe", $userSubject) }}" method="POST" onsubmit="return confirm('{{ __("Are you sure?") }}')">
@csrf
@method("DELETE")
<button type="submit" class="text-xs text-red-600 hover:text-red-800 font-medium">
{{ __("Unsubscribe") }}
</button>
</form>
</div>
</div>
@if($userSubject->frequency)
<div class="mt-2 text-xs text-gray-500">
{{ ucfirst($userSubject->frequency) }}
@if($userSubject->preferred_times)
| {{ $userSubject->formatted_times }}
@endif
@if($userSubject->weekdays)
| {{ implode(', ', array_map(function($day) {
return ['Dom', 'Seg', 'Ter', 'Qua', 'Qui', 'Sex', 'Sáb'][$day] ?? $day;
}, $userSubject->weekdays)) }}
@endif
</div>
@endif
<div class="mt-3 flex gap-2">
<form action="{{ route("admin.subjects.send-insight", $subject) }}" method="POST">
@csrf
<input type="hidden" name="message_type" value="{{ $subject->message_type ?? 'insight' }}">
<button type="submit" class="flex-1 text-xs bg-indigo-600 text-white px-3 py-1.5 rounded-lg hover:bg-indigo-700 transition font-medium">
<svg class="w-3 h-3 inline mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"/>
</svg>
@php
$label = match($subject->message_type) {
'language_lesson' => __('Send Lesson'),
'game_simulator' => __('Send Game'),
'technical_teaching' => __('Send Teaching'),
default => __('Send Insight'),
};
@endphp
{{ $label }}
</button>
</form>
<form action="{{ route("admin.subjects.send-introduction", $subject) }}" method="POST">
@csrf
<button type="submit" class="flex-1 text-xs bg-green-600 text-white px-3 py-1.5 rounded-lg hover:bg-green-700 transition font-medium">
<svg class="w-3 h-3 inline mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 16.5 18c-1.746 0-3.332.477-4.5 1.253"/>
</svg>
{{ __("Introduction") }}
</button>
</form>
</div>
</div>
@else
<div class="mt-4 pt-3 border-t border-gray-100">
<button onclick="openSubscribeModal({{ $subject->id }}, '{{ addslashes($subject->name) }}')"
class="w-full text-center text-sm bg-indigo-600 text-white px-3 py-2 rounded-lg hover:bg-indigo-700 transition font-medium">
{{ __("Subscribe") }}
</button>
</div>
@endif
</div>
@empty
<div class="col-span-full text-center py-8 text-gray-500">
{{ __("No system subjects available.") }}
</div>
@endforelse
</div>
</div>
<!-- My Custom Subjects -->
<div class="bg-white rounded-xl shadow-sm border border-gray-100 p-6">
<div class="flex justify-between items-center mb-6">
<div>
<h3 class="text-xl font-semibold text-gray-900">{{ __("My Custom Subjects") }}</h3>
<p class="text-sm text-gray-500 mt-1">{{ __("Subjects you created for your own learning") }}</p>
</div>
<div class="flex gap-2">
<button onclick="document.getElementById('generateAdminAiSubjectModal').classList.remove('hidden')"
class="inline-flex items-center px-4 py-2 bg-purple-600 text-white text-sm font-medium rounded-lg hover:bg-purple-700 transition">
<svg class="w-4 h-4 mr-1.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"/>
</svg>
{{ __("Generate with AI") }}
</button>
<button onclick="document.getElementById('createSubjectModal').classList.remove('hidden')"
class="inline-flex items-center px-4 py-2 bg-indigo-600 text-white text-sm font-medium rounded-lg hover:bg-indigo-700 transition">
<svg class="w-4 h-4 mr-1.5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"/>
</svg>
{{ __("Create Subject") }}
</button>
</div>
</div>
@if($mySubjects->count() > 0)
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
@foreach($mySubjects as $userSubject)
@php $subject = $userSubject->subject; @endphp
<div class="border border-gray-200 rounded-lg p-5 hover:shadow-md transition">
<div class="flex items-start justify-between mb-3">
<h4 class="font-semibold text-gray-900">{{ $subject->name }}</h4>
<div class="flex items-center gap-2">
<button onclick="openEditSystemSubjectModal({{ $subject->id }}, '{{ addslashes($subject->name) }}', '{{ addslashes($subject->description ?? '') }}', '{{ $subject->message_type ?? '' }}', '{{ addslashes($subject->custom_instructions ?? '') }}', {{ $subject->is_active ? 'true' : 'false' }})"
class="text-xs text-indigo-600 hover:text-indigo-800 font-medium">
{{ __("Edit") }}
</button>
<span class="text-xs bg-purple-100 text-purple-800 px-2 py-1 rounded-full font-medium">{{ __("Custom") }}</span>
</div>
</div>
@if($subject->description)
<p class="text-sm text-gray-600 mb-3">{{ $subject->description }}</p>
@endif
@if($subject->introduction)
<p class="text-xs text-gray-500 italic mb-3">{{ Str::limit($subject->introduction, 100) }}</p>
@endif
<div class="mt-4 pt-3 border-t border-gray-100">
<div class="flex items-center justify-between">
<div class="flex items-center gap-2">
<span class="w-2 h-2 rounded-full {{ $userSubject->is_active ? 'bg-green-500' : 'bg-gray-300' }}"></span>
<span class="text-sm {{ $userSubject->is_active ? 'text-green-700' : 'text-gray-500' }}">
{{ $userSubject->is_active ? __("Active") : __("Inactive") }}
</span>
</div>
<div class="flex gap-2">
<button onclick="openEditScheduleModal({{ $userSubject->id }}, '{{ addslashes($userSubject->frequency) }}', {{ json_encode($userSubject->preferred_times ?? ['09:00']) }}, {{ json_encode($userSubject->weekdays ?? []) }}, '{{ $userSubject->insight_language ?? 'en' }}')"
class="text-xs text-indigo-600 hover:text-indigo-800 font-medium">
{{ __("Edit Schedule") }}
</button>
<form action="{{ route("admin.subjects.toggle", $userSubject) }}" method="POST">
@csrf
<input type="hidden" name="is_active" value="{{ $userSubject->is_active ? '0' : '1' }}">
<button type="submit" class="text-xs {{ $userSubject->is_active ? 'text-red-600 hover:text-red-800' : 'text-green-600 hover:text-green-800' }} font-medium">
{{ $userSubject->is_active ? __("Deactivate") : __("Activate") }}
</button>
</form>
<form action="{{ route("admin.subjects.destroy", $subject) }}" method="POST" onsubmit="return confirm('{{ __("Are you sure?") }}')">
@csrf
@method("DELETE")
<button type="submit" class="text-xs text-red-600 hover:text-red-800 font-medium">
{{ __("Delete") }}
</button>
</form>
</div>
</div>
@if($userSubject->frequency)
<div class="mt-2 text-xs text-gray-500">
{{ ucfirst($userSubject->frequency) }}
@if($userSubject->preferred_times)
| {{ $userSubject->formatted_times }}
@endif
@if($userSubject->weekdays)
| {{ implode(', ', array_map(function($day) {
return ['Dom', 'Seg', 'Ter', 'Qua', 'Qui', 'Sex', 'Sáb'][$day] ?? $day;
}, $userSubject->weekdays)) }}
@endif
</div>
@endif
<div class="mt-3 flex gap-2">
<form action="{{ route("admin.subjects.send-insight", $subject) }}" method="POST">
@csrf
<input type="hidden" name="message_type" value="{{ $subject->message_type ?? 'insight' }}">
<button type="submit" class="flex-1 text-xs bg-indigo-600 text-white px-3 py-1.5 rounded-lg hover:bg-indigo-700 transition font-medium">
<svg class="w-3 h-3 inline mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"/>
</svg>
@php
$label = match($subject->message_type) {
'language_lesson' => __('Send Lesson'),
'game_simulator' => __('Send Game'),
'technical_teaching' => __('Send Teaching'),
default => __('Send Insight'),
};
@endphp
{{ $label }}
</button>
</form>
<form action="{{ route("admin.subjects.send-introduction", $subject) }}" method="POST">
@csrf
<button type="submit" class="flex-1 text-xs bg-green-600 text-white px-3 py-1.5 rounded-lg hover:bg-green-700 transition font-medium">
<svg class="w-3 h-3 inline mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 18 16.5 18c-1.746 0-3.332.477-4.5 1.253"/>
</svg>
{{ __("Introduction") }}
</button>
</form>
</div>
</div>
</div>
@endforeach
</div>
@else
<div class="text-center py-8">
<svg class="w-12 h-12 text-gray-300 mx-auto mb-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253"/>
</svg>
<p class="text-gray-500">{{ __("You have not created any custom subjects yet.") }}</p>
<button onclick="document.getElementById('createSubjectModal').classList.remove('hidden')"
class="mt-3 text-indigo-600 hover:text-indigo-800 text-sm font-medium">
{{ __("Create your first subject") }} →
</button>
</div>
@endif
</div>
</div>
</div>
<!-- Subscribe Modal -->
<div id="subscribeModal" class="hidden fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-50">
<div class="relative top-20 mx-auto p-5 border w-full max-w-md shadow-lg rounded-xl bg-white">
<div class="mt-3">
<div class="flex justify-between items-center mb-4">
<h3 class="text-lg font-medium text-gray-900">{{ __("Subscribe to Subject") }}</h3>
<button onclick="closeSubscribeModal()" class="text-gray-400 hover:text-gray-600">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
</svg>
</button>
</div>
<form id="subscribeForm" method="POST">
@csrf
<div class="mb-4">
<label class="block text-sm font-medium text-gray-700 mb-2">{{ __("Subject") }}</label>
<p id="subscribeSubjectName" class="text-gray-900 font-medium"></p>
</div>
<div class="mb-4">
<label for="frequency" class="block text-sm font-medium text-gray-700 mb-2">{{ __("Frequency") }}</label>
<select name="frequency" id="frequency" required
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500">
<option value="daily">{{ __("Daily") }}</option>
<option value="weekly">{{ __("Weekly") }}</option>
<option value="biweekly">{{ __("Biweekly") }}</option>
<option value="monthly">{{ __("Monthly") }}</option>
</select>
</div>
<div class="mb-4">
<label class="block text-sm font-medium text-gray-700 mb-2">{{ __("Preferred Times") }}</label>
<p class="text-xs text-gray-500 mb-2">{{ __("Add one or more times for insight delivery") }}</p>
<div id="timesContainer">
<div class="flex items-center gap-2 mb-2">
<input type="time" name="preferred_times[]" value="09:00" required
class="flex-1 px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500">
<button type="button" onclick="addTimeField()"
class="px-3 py-2 bg-green-100 text-green-700 rounded-lg hover:bg-green-200 transition text-sm font-medium">
+
</button>
</div>
</div>
</div>
<div class="mb-4">
<label class="block text-sm font-medium text-gray-700 mb-2">{{ __("Days of Week") }}</label>
<div class="grid grid-cols-7 gap-2">
@foreach([['value' => 1, 'label' => 'Seg'], ['value' => 2, 'label' => 'Ter'], ['value' => 3, 'label' => 'Qua'], ['value' => 4, 'label' => 'Qui'], ['value' => 5, 'label' => 'Sex'], ['value' => 6, 'label' => 'Sáb'], ['value' => 0, 'label' => 'Dom']] as $day)
<label class="flex flex-col items-center">
<input type="checkbox" name="weekdays[]" value="{{ $day['value'] }}"
class="rounded border-gray-300 text-indigo-600 focus:ring-indigo-500">
<span class="text-xs text-gray-500 mt-1">{{ $day['label'] }}</span>
</label>
@endforeach
</div>
</div>
<div class="mb-4">
<label for="insight_language" class="block text-sm font-medium text-gray-700 mb-2">{{ __("Insight Language") }}</label>
<select name="insight_language" id="insight_language" required
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500">
<option value="pt_BR">🇧🇷 {{ __("Português (Brasil)") }}</option>
<option value="en" selected>🇺🇸 {{ __("English") }}</option>
<option value="es">🇪🇸 {{ __("Español") }}</option>
</select>
</div>
<div class="mb-4">
<label class="block text-sm font-medium text-gray-700 mb-2">{{ __("Delivery Channels") }}</label>
<p class="text-xs text-gray-500 mb-2">{{ __("Choose where to receive insights") }}</p>
<div class="space-y-2">
<label class="flex items-center">
<input type="checkbox" name="delivery_channels[]" value="dashboard" checked disabled
class="rounded border-gray-300 text-indigo-600 focus:ring-indigo-500">
<span class="ml-2 text-sm text-gray-700">{{ __("Dashboard") }} <span class="text-xs text-gray-400">({{ __("always enabled") }})</span></span>
</label>
<label class="flex items-center">
<input type="checkbox" name="delivery_channels[]" value="telegram"
class="rounded border-gray-300 text-blue-600 focus:ring-blue-500">
<span class="ml-2 text-sm text-gray-700">{{ __("Telegram Bot") }}</span>
</label>
</div>
</div>
<div class="flex justify-end gap-2 mt-6">
<button type="button" onclick="closeSubscribeModal()"
class="px-4 py-2 bg-gray-100 text-gray-700 rounded-lg hover:bg-gray-200 transition font-medium">
{{ __("Cancel") }}
</button>
<button type="submit" class="px-4 py-2 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700 transition font-medium">
{{ __("Subscribe") }}
</button>
</div>
</form>
</div>
</div>
</div>
<!-- Edit Schedule Modal -->
<div id="editScheduleModal" class="hidden fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-50">
<div class="relative top-20 mx-auto p-5 border w-full max-w-md shadow-lg rounded-xl bg-white">
<div class="mt-3">
<div class="flex justify-between items-center mb-4">
<h3 class="text-lg font-medium text-gray-900">{{ __("Edit Schedule") }}</h3>
<button onclick="closeEditScheduleModal()" class="text-gray-400 hover:text-gray-600">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
</svg>
</button>
</div>
<form id="editScheduleForm" method="POST">
@csrf
@method("PUT")
<div class="mb-4">
<label for="edit_frequency" class="block text-sm font-medium text-gray-700 mb-2">{{ __("Frequency") }}</label>
<select name="frequency" id="edit_frequency" required
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500">
<option value="daily">{{ __("Daily") }}</option>
<option value="weekly">{{ __("Weekly") }}</option>
<option value="biweekly">{{ __("Biweekly") }}</option>
<option value="monthly">{{ __("Monthly") }}</option>
</select>
</div>
<div class="mb-4">
<label class="block text-sm font-medium text-gray-700 mb-2">{{ __("Preferred Times") }}</label>
<p class="text-xs text-gray-500 mb-2">{{ __("Add one or more times for insight delivery") }}</p>
<div id="editTimesContainer">
<div class="flex items-center gap-2 mb-2">
<input type="time" name="preferred_times[]" value="09:00" required
class="flex-1 px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500">
<button type="button" onclick="addEditTimeField()"
class="px-3 py-2 bg-green-100 text-green-700 rounded-lg hover:bg-green-200 transition text-sm font-medium">
+
</button>
</div>
</div>
</div>
<div class="mb-4">
<label class="block text-sm font-medium text-gray-700 mb-2">{{ __("Days of Week") }}</label>
<div class="grid grid-cols-7 gap-2">
@foreach([['value' => 1, 'label' => 'Seg'], ['value' => 2, 'label' => 'Ter'], ['value' => 3, 'label' => 'Qua'], ['value' => 4, 'label' => 'Qui'], ['value' => 5, 'label' => 'Sex'], ['value' => 6, 'label' => 'Sáb'], ['value' => 0, 'label' => 'Dom']] as $day)
<label class="flex flex-col items-center">
<input type="checkbox" name="weekdays[]" value="{{ $day['value'] }}"
class="rounded border-gray-300 text-indigo-600 focus:ring-indigo-500 edit-weekday">
<span class="text-xs text-gray-500 mt-1">{{ $day['label'] }}</span>
</label>
@endforeach
</div>
</div>
<div class="mb-4">
<label for="edit_insight_language" class="block text-sm font-medium text-gray-700 mb-2">{{ __("Insight Language") }}</label>
<select name="insight_language" id="edit_insight_language" required
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500">
<option value="pt_BR">🇧🇷 {{ __("Português (Brasil)") }}</option>
<option value="en">🇺🇸 {{ __("English") }}</option>
<option value="es">🇪🇸 {{ __("Español") }}</option>
</select>
</div>
<div class="mb-4">
<label class="block text-sm font-medium text-gray-700 mb-2">{{ __("Delivery Channels") }}</label>
<p class="text-xs text-gray-500 mb-2">{{ __("Choose where to receive insights") }}</p>
<div class="space-y-2">
<label class="flex items-center">
<input type="checkbox" name="delivery_channels[]" value="dashboard" checked disabled
class="rounded border-gray-300 text-indigo-600 focus:ring-indigo-500 edit-channel-dashboard">
<span class="ml-2 text-sm text-gray-700">{{ __("Dashboard") }} <span class="text-xs text-gray-400">({{ __("always enabled") }})</span></span>
</label>
<label class="flex items-center">
<input type="checkbox" name="delivery_channels[]" value="telegram"
class="rounded border-gray-300 text-blue-600 focus:ring-blue-500 edit-channel-telegram">
<span class="ml-2 text-sm text-gray-700">{{ __("Telegram Bot") }}</span>
</label>
</div>
</div>
<div class="flex justify-end gap-2 mt-6">
<button type="button" onclick="closeEditScheduleModal()"
class="px-4 py-2 bg-gray-100 text-gray-700 rounded-lg hover:bg-gray-200 transition font-medium">
{{ __("Cancel") }}
</button>
<button type="submit" class="px-4 py-2 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700 transition font-medium">
{{ __("Update Schedule") }}
</button>
</div>
</form>
</div>
</div>
</div>
<!-- Create Subject Modal -->
<div id="createSubjectModal" class="hidden fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-50">
<div class="relative top-20 mx-auto p-5 border w-full max-w-md shadow-lg rounded-xl bg-white">
<div class="mt-3">
<div class="flex justify-between items-center mb-4">
<h3 class="text-lg font-medium text-gray-900">{{ __("Create New Subject") }}</h3>
<button onclick="document.getElementById('createSubjectModal').classList.add('hidden')" class="text-gray-400 hover:text-gray-600">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
</svg>
</button>
</div>
<form action="{{ route("admin.subjects.store") }}" method="POST">
@csrf
<div class="mb-4">
<label for="name" class="block text-sm font-medium text-gray-700 mb-2">{{ __("Name") }} *</label>
<input type="text" name="name" id="name" required
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500"
placeholder="{{ __("e.g., Aprendizado de Python") }}">
</div>
<div class="mb-4">
<label for="description" class="block text-sm font-medium text-gray-700 mb-2">{{ __("Description") }}</label>
<textarea name="description" id="description" rows="3"
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500"
placeholder="{{ __("Brief description of the subject") }}"></textarea>
</div>
<div class="mb-4">
<label for="introduction" class="block text-sm font-medium text-gray-700 mb-2">{{ __("Introduction") }}</label>
<textarea name="introduction" id="introduction" rows="3"
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500"
placeholder="{{ __("Detailed introduction to the subject for AI context") }}"></textarea>
</div>
<div class="mb-4">
<label for="message_type" class="block text-sm font-medium text-gray-700 mb-2">{{ __("Message Type") }}</label>
<select name="message_type" id="message_type"
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500">
<option value="">{{ __("Default") }}</option>
<option value="insight">Insight</option>
<option value="language_lesson">Language Lesson</option>
<option value="game_simulator">Game / Simulator</option>
<option value="technical_teaching">Technical Teaching</option>
</select>
</div>
<div class="mb-4">
<label for="custom_instructions" class="block text-sm font-medium text-gray-700 mb-2">{{ __("Custom Instructions (optional)") }}</label>
<textarea name="custom_instructions" id="custom_instructions" rows="3"
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500"
placeholder="{{ __("Additional instructions for the AI...") }}"></textarea>
</div>
<div class="flex justify-end gap-2 mt-6">
<button type="button" onclick="document.getElementById('createSubjectModal').classList.add('hidden')"
class="px-4 py-2 bg-gray-100 text-gray-700 rounded-lg hover:bg-gray-200 transition font-medium">
{{ __("Cancel") }}
</button>
<button type="submit" class="px-4 py-2 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700 transition font-medium">
{{ __("Create") }}
</button>
</div>
</form>
</div>
</div>
</div>
<!-- Edit System Subject Modal -->
<div id="editSystemSubjectModal" class="hidden fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-50">
<div class="relative top-20 mx-auto p-5 border w-full max-w-md shadow-lg rounded-xl bg-white">
<div class="mt-3">
<div class="flex justify-between items-center mb-4">
<h3 class="text-lg font-medium text-gray-900">{{ __("Edit System Subject") }}</h3>
<button onclick="document.getElementById('editSystemSubjectModal').classList.add('hidden')" class="text-gray-400 hover:text-gray-600">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
</svg>
</button>
</div>
<form id="editSystemSubjectForm" method="POST">
@csrf
@method("PUT")
<div class="mb-4">
<label for="edit_system_name" class="block text-sm font-medium text-gray-700 mb-2">{{ __("Name") }} *</label>
<input type="text" name="name" id="edit_system_name" required
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500">
</div>
<div class="mb-4">
<label for="edit_system_description" class="block text-sm font-medium text-gray-700 mb-2">{{ __("Description") }}</label>
<textarea name="description" id="edit_system_description" rows="3"
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500"></textarea>
</div>
<div class="mb-4">
<label for="edit_system_message_type" class="block text-sm font-medium text-gray-700 mb-2">{{ __("Message Type") }}</label>
<select name="message_type" id="edit_system_message_type"
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500">
<option value="">{{ __("Default") }}</option>
<option value="insight">Insight</option>
<option value="language_lesson">Language Lesson</option>
<option value="game_simulator">Game / Simulator</option>
<option value="technical_teaching">Technical Teaching</option>
</select>
</div>
<div class="mb-4">
<label for="edit_system_custom_instructions" class="block text-sm font-medium text-gray-700 mb-2">{{ __("Custom Instructions (optional)") }}</label>
<textarea name="custom_instructions" id="edit_system_custom_instructions" rows="3"
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500"></textarea>
</div>
<div class="mb-4">
<label class="flex items-center">
<input type="checkbox" name="is_active" id="edit_system_is_active" value="1"
class="rounded border-gray-300 text-indigo-600 shadow-sm focus:ring-indigo-500">
<span class="ml-2 text-sm text-gray-700">{{ __("Active") }}</span>
</label>
</div>
<div class="flex justify-end gap-2 mt-6">
<button type="button" onclick="document.getElementById('editSystemSubjectModal').classList.add('hidden')"
class="px-4 py-2 bg-gray-100 text-gray-700 rounded-lg hover:bg-gray-200 transition font-medium">
{{ __("Cancel") }}
</button>
<button type="submit" class="px-4 py-2 bg-indigo-600 text-white rounded-lg hover:bg-indigo-700 transition font-medium">
{{ __("Update") }}
</button>
</div>
</form>
</div>
</div>
</div>
<!-- Generate AI System Subject Modal -->
<div id="generateAiSubjectModal" class="hidden fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-50">
<div class="relative top-20 mx-auto p-5 border w-full max-w-md shadow-lg rounded-xl bg-white">
<div class="mt-3">
<div class="flex justify-between items-center mb-4">
<h3 class="text-lg font-medium text-gray-900">{{ __("Create System Subject with AI") }}</h3>
<button onclick="document.getElementById('generateAiSubjectModal').classList.add('hidden')" class="text-gray-400 hover:text-gray-600">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
</svg>
</button>
</div>
<form id="generateAiSubjectForm" method="POST" action="{{ route('superadmin.system.subjects.generateAi') }}">
@csrf
<div class="mb-4">
<label for="ai_theme" class="block text-sm font-medium text-gray-700 mb-2">{{ __("Theme / Topic") }}</label>
<input type="text" name="theme" id="ai_theme" required
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500"
placeholder="{{ __("e.g., Gurus dos Negócios e Riqueza") }}">
</div>
<div class="flex justify-end gap-2 mt-6">
<button type="button" onclick="document.getElementById('generateAiSubjectModal').classList.add('hidden')"
class="px-4 py-2 bg-gray-100 text-gray-700 rounded-lg hover:bg-gray-200 transition font-medium">
{{ __("Cancel") }}
</button>
<button type="submit" class="px-4 py-2 bg-purple-600 text-white rounded-lg hover:bg-purple-700 transition font-medium">
{{ __("Generate with AI") }}
</button>
</div>
</form>
</div>
</div>
</div>
@endsection
@push("scripts")
<script>
function openSubscribeModal(subjectId, subjectName) {
document.getElementById('subscribeSubjectName').textContent = subjectName;
document.getElementById('subscribeForm').action = '{{ url("admin/subjects") }}/' + subjectId + '/subscribe';
// Reset times container
document.getElementById('timesContainer').innerHTML = `
<div class="flex items-center gap-2 mb-2">
<input type="time" name="preferred_times[]" value="09:00" required
class="flex-1 px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500">
<button type="button" onclick="addTimeField()"
class="px-3 py-2 bg-green-100 text-green-700 rounded-lg hover:bg-green-200 transition text-sm font-medium">
+
</button>
</div>
`;
document.getElementById('subscribeModal').classList.remove('hidden');
}
function addTimeField() {
const container = document.getElementById('timesContainer');
const div = document.createElement('div');
div.className = 'flex items-center gap-2 mb-2';
div.innerHTML = `
<input type="time" name="preferred_times[]" value="09:00" required
class="flex-1 px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500">
<button type="button" onclick="this.parentElement.remove()"
class="px-3 py-2 bg-red-100 text-red-700 rounded-lg hover:bg-red-200 transition text-sm font-medium">
×
</button>
`;
container.appendChild(div);
}
function openEditScheduleModal(id, frequency, times, weekdays, language, channels) {
// Set form action
document.getElementById('editScheduleForm').action = '{{ url("admin/subjects") }}/' + id + '/schedule';
// Set frequency
document.getElementById('edit_frequency').value = frequency;
// Set language
if (document.getElementById('edit_insight_language')) {
document.getElementById('edit_insight_language').value = language || 'en';
}
// Build times container
const container = document.getElementById('editTimesContainer');
container.innerHTML = '';
if (times && times.length > 0) {
times.forEach(function(time, index) {
const div = document.createElement('div');
div.className = 'flex items-center gap-2 mb-2';
const timeVal = typeof time === 'string' ? time.substring(0, 5) : '09:00';
div.innerHTML = `
<input type="time" name="preferred_times[]" value="${timeVal}" required
class="flex-1 px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500">
${index === 0
? `<button type="button" onclick="addEditTimeField()"
class="px-3 py-2 bg-green-100 text-green-700 rounded-lg hover:bg-green-200 transition text-sm font-medium">+</button>`
: `<button type="button" onclick="this.parentElement.remove()"
class="px-3 py-2 bg-red-100 text-red-700 rounded-lg hover:bg-red-200 transition text-sm font-medium">×</button>`
}
`;
container.appendChild(div);
});
} else {
const div = document.createElement('div');
div.className = 'flex items-center gap-2 mb-2';
div.innerHTML = `
<input type="time" name="preferred_times[]" value="09:00" required
class="flex-1 px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500">
<button type="button" onclick="addEditTimeField()"
class="px-3 py-2 bg-green-100 text-green-700 rounded-lg hover:bg-green-200 transition text-sm font-medium">+</button>
`;
container.appendChild(div);
}
// Set weekdays (compare as strings to match DB storage format)
document.querySelectorAll('.edit-weekday').forEach(function(cb) {
cb.checked = weekdays && weekdays.some(function(d) {
return String(d) === cb.value;
});
});
// Set delivery channels (default to ['dashboard'] if not set)
var channelsArr = channels;
if (!channelsArr || !Array.isArray(channelsArr) || channelsArr.length === 0) {
channelsArr = ['dashboard'];
}
document.querySelectorAll('.edit-channel-telegram').forEach(function(cb) {
cb.checked = channelsArr.includes('telegram');
});
document.getElementById('editScheduleModal').classList.remove('hidden');
}
function addEditTimeField() {
const container = document.getElementById('editTimesContainer');
const div = document.createElement('div');
div.className = 'flex items-center gap-2 mb-2';
div.innerHTML = `
<input type="time" name="preferred_times[]" value="09:00" required
class="flex-1 px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500">
<button type="button" onclick="this.parentElement.remove()"
class="px-3 py-2 bg-red-100 text-red-700 rounded-lg hover:bg-red-200 transition text-sm font-medium">
×
</button>
`;
container.appendChild(div);
}
function closeEditScheduleModal() {
document.getElementById('editScheduleModal').classList.add('hidden');
}
function closeSubscribeModal() {
document.getElementById('subscribeModal').classList.add('hidden');
}
// Close modals on Escape key
document.addEventListener('keydown', function(event) {
if (event.key === 'Escape') {
document.getElementById('subscribeModal').classList.add('hidden');
document.getElementById('createSubjectModal').classList.add('hidden');
document.getElementById('editScheduleModal').classList.add('hidden');
document.getElementById('editSystemSubjectModal').classList.add('hidden');
}
});
function openEditSystemSubjectModal(id, name, description, messageType, customInstructions, isActive) {
document.getElementById('edit_system_name').value = name;
document.getElementById('edit_system_description').value = description;
document.getElementById('edit_system_message_type').value = messageType;
document.getElementById('edit_system_custom_instructions').value = customInstructions;
document.getElementById('edit_system_is_active').checked = isActive;
document.getElementById('editSystemSubjectForm').action = '{{ url("admin/subjects") }}/' + id;
document.getElementById('editSystemSubjectModal').classList.remove('hidden');
}
function closeGenerateAdminAiSubjectModal() {
document.getElementById('generateAdminAiSubjectModal').classList.add('hidden');
}
</script>
@endpush
<!-- Generate AI Subject Modal -->
<div id="generateAdminAiSubjectModal" class="hidden fixed inset-0 bg-gray-600 bg-opacity-50 overflow-y-auto h-full w-full z-50">
<div class="relative top-20 mx-auto p-5 border w-full max-w-md shadow-lg rounded-xl bg-white">
<div class="mt-3">
<div class="flex justify-between items-center mb-4">
<h3 class="text-lg font-medium text-gray-900">{{ __("Generate Subject with AI") }}</h3>
<button onclick="closeGenerateAdminAiSubjectModal()" class="text-gray-400 hover:text-gray-600">
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12"/>
</svg>
</button>
</div>
<form method="POST" action="{{ route('admin.subjects.generateAi') }}">
@csrf
<div class="mb-4">
<label for="admin_ai_theme" class="block text-sm font-medium text-gray-700 mb-2">{{ __("Theme / Topic") }}</label>
<input type="text" name="theme" id="admin_ai_theme" required
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500"
placeholder="{{ __("e.g., Marketing Digital e Vendas") }}">
</div>
<div class="flex justify-end gap-2 mt-6">
<button type="button" onclick="closeGenerateAdminAiSubjectModal()"
class="px-4 py-2 bg-gray-100 text-gray-700 rounded-lg hover:bg-gray-200 transition font-medium">
{{ __("Cancel") }}
</button>
<button type="submit" class="px-4 py-2 bg-purple-600 text-white rounded-lg hover:bg-purple-700 transition font-medium">
{{ __("Generate with AI") }}
</button>
</div>
</form>
</div>
</div>
</div>
+171
View File
@@ -0,0 +1,171 @@
@extends('layouts.app')
@section('title', __('Telegram Integration'))
@section('header')
<div class="flex justify-between items-center">
<h2 class="font-semibold text-xl text-gray-800 leading-tight">
{{ __('Telegram Integration') }}
</h2>
</div>
@endsection
@section('content')
<div class="py-6">
<div class="max-w-4xl mx-auto sm:px-6 lg:px-8">
@if(session('success'))
<div class="bg-green-100 border border-green-400 text-green-700 px-4 py-3 rounded-lg mb-6">
{{ session('success') }}
</div>
@endif
@if(session('error'))
<div class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded-lg mb-6">
{{ session('error') }}
</div>
@endif
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<!-- Configure Bot -->
<div class="bg-white rounded-xl shadow-sm border border-gray-100 p-6">
<div class="flex items-center gap-3 mb-4">
<div class="w-12 h-12 bg-blue-100 rounded-lg flex items-center justify-center">
<svg class="w-6 h-6 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 12h.01M12 12h.01M16 12h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"/>
</svg>
</div>
<div>
<h3 class="text-lg font-semibold text-gray-900">{{ __('Configure Your Bot') }}</h3>
<p class="text-sm text-gray-500">{{ __('Set up your own Telegram bot to receive insights.') }}</p>
</div>
</div>
<div class="bg-gray-50 rounded-lg p-4 mb-4">
<h4 class="text-sm font-medium text-gray-700 mb-2">{{ __('How to create your bot:') }}</h4>
<ol class="text-sm text-gray-600 space-y-2 list-decimal list-inside">
<li>{{ __('Open Telegram and search for') }} <strong>@BotFather</strong></li>
<li>{{ __('Send') }} <code>/newbot</code> {{ __('and follow the instructions') }}</li>
<li>{{ __('Copy the HTTP API token provided by BotFather') }}</li>
<li>{{ __('Start a chat with your new bot and send') }} <code>/start</code></li>
<li>{{ __('Get your Chat ID (use @userinfobot or similar)') }}</li>
<li>{{ __('Fill in the fields below and click Connect') }}</li>
</ol>
</div>
<form method="POST" action="{{ route('admin.telegram.connect') }}" class="space-y-4">
@csrf
<div>
<label for="bot_token" class="block text-sm font-medium text-gray-700 mb-2">
{{ __('Bot Token') }} *
</label>
<input type="text" name="bot_token" id="bot_token" required
value="{{ old('bot_token', $telegramUser->bot_token ?? '') }}"
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500 font-mono text-sm"
placeholder="1234567890:ABCdefGHIjklMNOpqrsTUVwxyz">
</div>
<div>
<label for="bot_username" class="block text-sm font-medium text-gray-700 mb-2">
{{ __('Bot Username (optional)') }}
</label>
<input type="text" name="bot_username" id="bot_username"
value="{{ old('bot_username', $telegramUser->bot_username ?? '') }}"
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500"
placeholder="@your_bot">
</div>
<div>
<label for="chat_id" class="block text-sm font-medium text-gray-700 mb-2">
{{ __('Chat ID') }} *
</label>
<input type="text" name="chat_id" id="chat_id" required
value="{{ old('chat_id', $telegramUser->chat_id ?? '') }}"
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500 font-mono text-sm"
placeholder="123456789">
</div>
<div>
<label for="telegram_username" class="block text-sm font-medium text-gray-700 mb-2">
{{ __('Your Telegram Username (optional)') }}
</label>
<input type="text" name="telegram_username" id="telegram_username"
value="{{ old('telegram_username', $telegramUser->telegram_username ?? '') }}"
class="w-full px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500"
placeholder="@your_username">
</div>
<button type="submit" class="w-full px-4 py-2 bg-blue-600 text-white rounded-lg hover:bg-blue-700 transition font-medium">
{{ $telegramUser && $telegramUser->isFullyConfigured() ? __('Update Bot Configuration') : __('Connect Bot') }}
</button>
</form>
</div>
<!-- Connection Status & Test -->
<div class="bg-white rounded-xl shadow-sm border border-gray-100 p-6">
<div class="flex items-center gap-3 mb-4">
<div class="w-12 h-12 {{ $telegramUser && $telegramUser->isFullyConfigured() ? 'bg-green-100' : 'bg-gray-100' }} rounded-lg flex items-center justify-center">
<svg class="w-6 h-6 {{ $telegramUser && $telegramUser->isFullyConfigured() ? 'text-green-600' : 'text-gray-400' }}" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/>
</svg>
</div>
<div>
<h3 class="text-lg font-semibold text-gray-900">{{ __('Connection Status') }}</h3>
<p class="text-sm text-gray-500">{{ __('Your bot configuration status.') }}</p>
</div>
</div>
@if($telegramUser && $telegramUser->isFullyConfigured())
<div class="bg-green-50 border border-green-200 rounded-lg p-4 mb-4">
<div class="flex items-center gap-2 mb-2">
<span class="w-3 h-3 rounded-full bg-green-500"></span>
<span class="text-sm font-medium text-green-800">{{ __('Bot Configured') }}</span>
</div>
@if($telegramUser->bot_username)
<p class="text-sm text-green-700">{{ __('Bot: :username', ['username' => '@' . ltrim($telegramUser->bot_username, '@')]) }}</p>
@endif
<p class="text-sm text-green-700">{{ __('Chat ID: :id', ['id' => $telegramUser->chat_id]) }}</p>
<p class="text-sm text-green-700">{{ __('Connected since: :date', ['date' => $telegramUser->created_at->format('d/m/Y H:i')]) }}</p>
</div>
<div class="flex gap-2 mb-4">
<!-- Test Button -->
<form method="POST" action="{{ route('admin.telegram.test') }}" class="flex-1">
@csrf
<button type="submit" class="w-full px-4 py-2 bg-green-600 text-white rounded-lg hover:bg-green-700 transition font-medium">
<svg class="w-4 h-4 inline mr-1" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M14.752 11.168l-3.197-2.132A1 1 0 0010 9.87v4.263a1 1 0 001.555.832l3.197-2.132a1 1 0 000-1.664z"/>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
</svg>
{{ __('Send Test Message') }}
</button>
</form>
<!-- Disconnect Button -->
<form method="POST" action="{{ route('admin.telegram.disconnect') }}">
@csrf
<button type="submit" class="px-4 py-2 bg-red-600 text-white rounded-lg hover:bg-red-700 transition font-medium"
onclick="return confirm('{{ __('Disconnect Telegram bot?') }}')">
{{ __('Disconnect') }}
</button>
</form>
</div>
@else
<div class="bg-gray-50 border border-gray-200 rounded-lg p-4 mb-4">
<div class="flex items-center gap-2 mb-2">
<span class="w-3 h-3 rounded-full bg-gray-400"></span>
<span class="text-sm font-medium text-gray-600">{{ __('Not Configured') }}</span>
</div>
<p class="text-sm text-gray-500">{{ __('Create your own Telegram bot and configure it here to receive insights.') }}</p>
</div>
@endif
<div class="bg-indigo-50 border border-indigo-100 rounded-lg p-4">
<h4 class="text-sm font-medium text-indigo-800 mb-2">{{ __('Why use your own bot?') }}</h4>
<ul class="text-sm text-indigo-700 space-y-1">
<li>✓ {{ __('Full control over your notifications') }}</li>
<li>✓ {{ __('No shared bots - completely private') }}</li>
<li>✓ {{ __('Receive insights in real-time') }}</li>
<li>✓ {{ __('Works with your existing bot configuration') }}</li>
</ul>
</div>
</div>
</div>
</div>
</div>
@endsection