feat(cms): implement complete SysSiteCMS content management system

Initial implementation of the HomeSysPanel CMS built with Laravel 13.0.
Includes complete admin panel, content management, user authentication,
modular architecture for products/services/chatbot, and marketing tools.

Key features:
- User authentication with email verification and password reset
- Role-based access control (Admin/User with granular permissions)
- Content management (pages, news, FAQ, downloads, galleries, banners)
- Contact management system with subjects and responses
- Marketing tools (email campaigns, visitor analytics, social sharing)
- Modular architecture (Products, Services, Chatbot modules)
- AI Chatbot integration with OpenRouter.ai and LlamaCpp support
- Visitor tracking with geolocation
- SEO optimization with meta tags and Open Graph
- Responsive design with Bootstrap 5
- Dark mode support
- Maintenance mode
- Feature request system
This commit is contained in:
marco
2026-09-07 19:06:53 -03:00
commit 38295e7e88
279 changed files with 33691 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
*.sqlite*
+45
View File
@@ -0,0 +1,45 @@
<?php
namespace Database\Factories;
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Facades\Hash;
use Illuminate\Support\Str;
/**
* @extends Factory<User>
*/
class UserFactory extends Factory
{
/**
* The current password being used by the factory.
*/
protected static ?string $password;
/**
* Define the model's default state.
*
* @return array<string, mixed>
*/
public function definition(): array
{
return [
'name' => fake()->name(),
'email' => fake()->unique()->safeEmail(),
'email_verified_at' => now(),
'password' => static::$password ??= Hash::make('password'),
'remember_token' => Str::random(10),
];
}
/**
* Indicate that the model's email address should be unverified.
*/
public function unverified(): static
{
return $this->state(fn (array $attributes) => [
'email_verified_at' => null,
]);
}
}
@@ -0,0 +1,49 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email')->unique();
$table->timestamp('email_verified_at')->nullable();
$table->string('password');
$table->rememberToken();
$table->timestamps();
});
Schema::create('password_reset_tokens', function (Blueprint $table) {
$table->string('email')->primary();
$table->string('token');
$table->timestamp('created_at')->nullable();
});
Schema::create('sessions', function (Blueprint $table) {
$table->string('id')->primary();
$table->foreignId('user_id')->nullable()->index();
$table->string('ip_address', 45)->nullable();
$table->text('user_agent')->nullable();
$table->longText('payload');
$table->integer('last_activity')->index();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('users');
Schema::dropIfExists('password_reset_tokens');
Schema::dropIfExists('sessions');
}
};
@@ -0,0 +1,35 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('cache', function (Blueprint $table) {
$table->string('key')->primary();
$table->mediumText('value');
$table->bigInteger('expiration')->index();
});
Schema::create('cache_locks', function (Blueprint $table) {
$table->string('key')->primary();
$table->string('owner');
$table->bigInteger('expiration')->index();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('cache');
Schema::dropIfExists('cache_locks');
}
};
@@ -0,0 +1,57 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('jobs', function (Blueprint $table) {
$table->id();
$table->string('queue')->index();
$table->longText('payload');
$table->unsignedTinyInteger('attempts');
$table->unsignedInteger('reserved_at')->nullable();
$table->unsignedInteger('available_at');
$table->unsignedInteger('created_at');
});
Schema::create('job_batches', function (Blueprint $table) {
$table->string('id')->primary();
$table->string('name');
$table->integer('total_jobs');
$table->integer('pending_jobs');
$table->integer('failed_jobs');
$table->longText('failed_job_ids');
$table->mediumText('options')->nullable();
$table->integer('cancelled_at')->nullable();
$table->integer('created_at');
$table->integer('finished_at')->nullable();
});
Schema::create('failed_jobs', function (Blueprint $table) {
$table->id();
$table->string('uuid')->unique();
$table->text('connection');
$table->text('queue');
$table->longText('payload');
$table->longText('exception');
$table->timestamp('failed_at')->useCurrent();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('jobs');
Schema::dropIfExists('job_batches');
Schema::dropIfExists('failed_jobs');
}
};
@@ -0,0 +1,28 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('users', function (Blueprint $table) {
$table->enum('user_type', ['Cliente', 'Fornecedor', 'Colaborador', 'Outro'])->after('email')->default('Cliente');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn('user_type');
});
}
};
@@ -0,0 +1,30 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('users', function (Blueprint $table) {
$table->string('company')->nullable()->after('user_type');
$table->string('website')->nullable()->after('company');
$table->string('whatsapp')->nullable()->after('website');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn(['company', 'website', 'whatsapp']);
});
}
};
@@ -0,0 +1,29 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('contact_subjects', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->boolean('active')->default(true);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('contact_subjects');
}
};
@@ -0,0 +1,35 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('contact_messages', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('email');
$table->string('company')->nullable();
$table->string('website')->nullable();
$table->string('whatsapp')->nullable();
$table->foreignId('subject_id')->constrained('contact_subjects')->onDelete('cascade');
$table->text('message');
$table->boolean('read')->default(false);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('contact_messages');
}
};
@@ -0,0 +1,39 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\DB;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
// 1. Muda temporariamente para string para permitir qualquer valor
Schema::table('users', function (Blueprint $table) {
$table->string('user_type')->change();
});
// 2. Atualiza os valores antigos para o novo padrão
DB::table('users')->whereIn('user_type', ['Cliente', 'Fornecedor', 'Outro'])->update(['user_type' => 'User']);
// Se houver algum 'Colaborador', ele permanece como está.
// 3. Define o novo enum
Schema::table('users', function (Blueprint $table) {
$table->enum('user_type', ['Admin', 'Colaborador', 'User'])->default('User')->change();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('users', function (Blueprint $table) {
$table->enum('user_type', ['Cliente', 'Fornecedor', 'Colaborador', 'Outro'])->default('Cliente')->change();
});
}
};
@@ -0,0 +1,38 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\DB;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
// 1. Muda temporariamente para string para permitir qualquer valor
Schema::table('users', function (Blueprint $table) {
$table->string('user_type')->change();
});
// 2. Atualiza valores antigos se necessário (Colaborador -> Member)
DB::table('users')->where('user_type', 'Colaborador')->update(['user_type' => 'Member']);
// 3. Define o novo enum com os 5 tipos solicitados
Schema::table('users', function (Blueprint $table) {
$table->enum('user_type', ['Admin', 'Member', 'Cliente', 'Fornecedor', 'User'])->default('User')->change();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('users', function (Blueprint $table) {
$table->enum('user_type', ['Admin', 'Colaborador', 'User'])->default('User')->change();
});
}
};
@@ -0,0 +1,30 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('contact_responses', function (Blueprint $table) {
$table->id();
$table->foreignId('contact_message_id')->constrained()->onDelete('cascade');
$table->foreignId('user_id')->constrained(); // Quem respondeu
$table->text('message');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('contact_responses');
}
};
@@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('pages', function (Blueprint $table) {
$table->id();
$table->string('slug')->unique();
$table->string('title');
$table->longText('content')->nullable();
$table->string('meta_description')->nullable();
$table->boolean('active')->default(true);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('pages');
}
};
@@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('pages', function (Blueprint $table) {
$table->string('meta_keywords')->nullable()->after('meta_description');
$table->string('og_title')->nullable()->after('meta_keywords');
$table->string('og_description')->nullable()->after('og_title');
$table->string('og_image')->nullable()->after('og_description');
$table->string('canonical_url')->nullable()->after('og_image');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('pages', function (Blueprint $table) {
$table->dropColumn(['meta_keywords', 'og_title', 'og_description', 'og_image', 'canonical_url']);
});
}
};
@@ -0,0 +1,30 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('settings', function (Blueprint $table) {
$table->id();
$table->string('key')->unique();
$table->text('value')->nullable();
$table->string('group')->default('general'); // general, seo, scripts
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('settings');
}
};
@@ -0,0 +1,31 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('faqs', function (Blueprint $table) {
$table->id();
$table->string('question');
$table->text('answer');
$table->integer('order')->default(0);
$table->boolean('active')->default(true);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('faqs');
}
};
@@ -0,0 +1,33 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('downloads', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->text('description')->nullable();
$table->string('file_path');
$table->string('file_type')->nullable(); // Ex: Driver, Manual, Software
$table->bigInteger('download_count')->default(0);
$table->boolean('active')->default(true);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('downloads');
}
};
@@ -0,0 +1,31 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('galleries', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->string('slug')->unique();
$table->text('description')->nullable();
$table->boolean('active')->default(true);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('galleries');
}
};
@@ -0,0 +1,31 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('gallery_images', function (Blueprint $table) {
$table->id();
$table->foreignId('gallery_id')->constrained()->onDelete('cascade');
$table->string('file_path');
$table->string('description')->nullable();
$table->integer('order')->default(0);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('gallery_images');
}
};
@@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('users', function (Blueprint $table) {
if (!Schema::hasColumn('users', 'avatar')) {
$table->string('avatar')->nullable()->after('email');
}
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('users', function (Blueprint $table) {
if (Schema::hasColumn('users', 'avatar')) {
$table->dropColumn('avatar');
}
});
}
};
@@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('users', function (Blueprint $table) {
if (!Schema::hasColumn('users', 'preferences')) {
$table->json('preferences')->nullable()->after('whatsapp');
}
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('users', function (Blueprint $table) {
if (Schema::hasColumn('users', 'preferences')) {
$table->dropColumn('preferences');
}
});
}
};
@@ -0,0 +1,31 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('notifications', function (Blueprint $table) {
$table->uuid('id')->primary();
$table->string('type');
$table->morphs('notifiable');
$table->text('data');
$table->timestamp('read_at')->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('notifications');
}
};
@@ -0,0 +1,33 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('products', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('slug')->unique();
$table->text('description')->nullable();
$table->decimal('price', 10, 2)->nullable();
$table->string('image')->nullable();
$table->boolean('active')->default(true);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('products');
}
};
@@ -0,0 +1,33 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('product_categories', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('slug')->unique();
$table->boolean('active')->default(true);
$table->integer('order')->default(0);
$table->timestamps();
});
Schema::table('products', function (Blueprint $table) {
$table->foreignId('category_id')->nullable()->after('id')->constrained('product_categories')->onDelete('set null');
});
}
public function down(): void
{
Schema::table('products', function (Blueprint $table) {
$table->dropForeign(['category_id']);
$table->dropColumn('category_id');
});
Schema::dropIfExists('product_categories');
}
};
@@ -0,0 +1,23 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::table('product_categories', function (Blueprint $table) {
$table->text('description')->nullable()->after('slug');
$table->string('image')->nullable()->after('description');
});
}
public function down(): void
{
Schema::table('product_categories', function (Blueprint $table) {
$table->dropColumn(['description', 'image']);
});
}
};
@@ -0,0 +1,27 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('banners', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->string('image');
$table->string('link')->nullable();
$table->string('button_text')->nullable();
$table->integer('order')->default(0);
$table->boolean('active')->default(true);
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('banners');
}
};
@@ -0,0 +1,28 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('services', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('slug')->unique();
$table->text('description')->nullable();
$table->string('icon')->nullable(); // Ex: bi-tools, fas fa-laptop
$table->string('image')->nullable();
$table->integer('order')->default(0);
$table->boolean('active')->default(true);
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('services');
}
};
@@ -0,0 +1,28 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('visitor_logs', function (Blueprint $table) {
$table->id();
$table->string('ip_address')->nullable();
$table->string('country')->nullable();
$table->string('city')->nullable();
$table->string('region')->nullable();
$table->string('session_id')->nullable();
$table->string('user_agent')->nullable();
$table->string('page_url')->nullable();
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('visitor_logs');
}
};
@@ -0,0 +1,57 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Facades\DB;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
// 1. Change to string temporarily to allow any value
Schema::table('users', function (Blueprint $table) {
$table->string('user_type')->change();
});
// 2. Update all non-Admin users to 'User'
DB::table('users')->where('user_type', '!=', 'Admin')->update(['user_type' => 'User']);
// 3. Define the new enum with only Admin and User
Schema::table('users', function (Blueprint $table) {
$table->enum('user_type', ['Admin', 'User'])->default('User')->change();
});
// 4. Add permissions JSON column
Schema::table('users', function (Blueprint $table) {
if (!Schema::hasColumn('users', 'permissions')) {
$table->json('permissions')->nullable()->after('preferences');
}
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
// Remove permissions column
Schema::table('users', function (Blueprint $table) {
if (Schema::hasColumn('users', 'permissions')) {
$table->dropColumn('permissions');
}
});
// Revert to previous enum
Schema::table('users', function (Blueprint $table) {
$table->string('user_type')->change();
});
Schema::table('users', function (Blueprint $table) {
$table->enum('user_type', ['Admin', 'Member', 'Cliente', 'Fornecedor', 'User'])->default('User')->change();
});
}
};
@@ -0,0 +1,23 @@
<?php
use Illuminate\Database\Migrations\Migration;
use App\Models\Setting;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Setting::set('public_registration', 'off', 'general');
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Setting::where('key', 'public_registration')->delete();
}
};
@@ -0,0 +1,51 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('user_types', function (Blueprint $table) {
$table->id();
$table->string('name')->unique();
$table->string('description')->nullable();
$table->boolean('is_admin')->default(false);
$table->boolean('is_protected')->default(false);
$table->timestamps();
});
// Insert default user types
\DB::table('user_types')->insert([
[
'name' => 'Admin',
'description' => 'Administrador com acesso total ao sistema',
'is_admin' => true,
'is_protected' => true,
'created_at' => now(),
'updated_at' => now(),
],
[
'name' => 'User',
'description' => 'Usuário com acesso restrito por permissões',
'is_admin' => false,
'is_protected' => true,
'created_at' => now(),
'updated_at' => now(),
],
]);
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('user_types');
}
};
@@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('contact_responses', function (Blueprint $table) {
$table->dropForeign(['user_id']);
$table->unsignedBigInteger('user_id')->nullable()->change();
$table->foreign('user_id')->references('id')->on('users')->onDelete('set null');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('contact_responses', function (Blueprint $table) {
$table->dropForeign(['user_id']);
$table->unsignedBigInteger('user_id')->nullable(false)->change();
$table->foreign('user_id')->references('id')->on('users');
});
}
};
@@ -0,0 +1,31 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('gallery_categories', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('slug')->unique();
$table->text('description')->nullable();
$table->boolean('active')->default(true);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('gallery_categories');
}
};
@@ -0,0 +1,29 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('galleries', function (Blueprint $table) {
$table->foreignId('category_id')->nullable()->after('id')->constrained('gallery_categories')->nullOnDelete();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('galleries', function (Blueprint $table) {
$table->dropForeign(['category_id']);
$table->dropColumn('category_id');
});
}
};
@@ -0,0 +1,39 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('news', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->string('slug')->unique();
$table->text('summary')->nullable();
$table->longText('content')->nullable();
$table->string('featured_image')->nullable();
$table->string('author')->nullable();
$table->boolean('active')->default(true);
$table->timestamp('published_at')->nullable();
$table->foreignId('user_id')->nullable()->constrained()->nullOnDelete();
$table->string('seo_title')->nullable();
$table->string('seo_description')->nullable();
$table->string('seo_keywords')->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('news');
}
};
@@ -0,0 +1,31 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('news_images', function (Blueprint $table) {
$table->id();
$table->foreignId('news_id')->constrained()->cascadeOnDelete();
$table->string('file_path');
$table->string('description')->nullable();
$table->integer('order')->default(0);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('news_images');
}
};
@@ -0,0 +1,31 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('chatbot_knowledge', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->text('content');
$table->string('category')->default('general'); // company_info, products, services, faq, general
$table->boolean('active')->default(true);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('chatbot_knowledge');
}
};
@@ -0,0 +1,30 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('chatbot_conversations', function (Blueprint $table) {
$table->id();
$table->string('session_id')->unique();
$table->string('visitor_name')->nullable();
$table->string('visitor_email')->nullable();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('chatbot_conversations');
}
};
@@ -0,0 +1,30 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('chatbot_messages', function (Blueprint $table) {
$table->id();
$table->foreignId('conversation_id')->constrained('chatbot_conversations')->cascadeOnDelete();
$table->enum('role', ['user', 'assistant', 'system']);
$table->text('content');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('chatbot_messages');
}
};
@@ -0,0 +1,28 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('chatbot_knowledge', function (Blueprint $table) {
$table->text('restrictions')->nullable()->after('content');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('chatbot_knowledge', function (Blueprint $table) {
$table->dropColumn('restrictions');
});
}
};
@@ -0,0 +1,29 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('chatbot_conversations', function (Blueprint $table) {
$table->string('visitor_phone')->nullable()->after('visitor_email');
$table->string('visitor_city')->nullable()->after('visitor_phone');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('chatbot_conversations', function (Blueprint $table) {
$table->dropColumn(['visitor_phone', 'visitor_city']);
});
}
};
@@ -0,0 +1,28 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('chatbot_messages', function (Blueprint $table) {
$table->integer('tokens_used')->nullable()->after('content');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('chatbot_messages', function (Blueprint $table) {
$table->dropColumn('tokens_used');
});
}
};
@@ -0,0 +1,31 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::table('chatbot_conversations', function (Blueprint $table) {
$table->string('status', 20)->default('active')->after('visitor_city');
$table->tinyInteger('rating')->nullable()->after('status');
$table->text('rating_comment')->nullable()->after('rating');
$table->timestamp('ended_at')->nullable()->after('rating_comment');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('chatbot_conversations', function (Blueprint $table) {
$table->dropColumn(['status', 'rating', 'rating_comment', 'ended_at']);
});
}
};
@@ -0,0 +1,31 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
/**
* Run the migrations.
*/
public function up(): void
{
Schema::create('chatbot_rules', function (Blueprint $table) {
$table->id();
$table->string('title', 255);
$table->text('content');
$table->boolean('active')->default(true);
$table->unsignedSmallInteger('order')->default(0)->index();
$table->timestamps();
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::dropIfExists('chatbot_rules');
}
};
+26
View File
@@ -0,0 +1,26 @@
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
use App\Models\User;
use Illuminate\Support\Facades\Hash;
class AdminUserSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
User::updateOrCreate(
['email' => 'admin@admin.com'],
[
'name' => 'Administrador',
'password' => Hash::make('admin123'),
'user_type' => 'Admin',
'email_verified_at' => now(),
]
);
}
}
+57
View File
@@ -0,0 +1,57 @@
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
use App\Models\ContactMessage;
use App\Models\ContactSubject;
use Illuminate\Support\Str;
class ContactMessageSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
$subjects = ContactSubject::all();
if ($subjects->isEmpty()) {
$this->call(ContactSubjectSeeder::class);
$subjects = ContactSubject::all();
}
$names = [
'Ricardo Oliveira', 'Mariana Santos', 'Carlos Eduardo', 'Ana Beatriz',
'Fernando Souza', 'Juliana Lima', 'Marcos Rocha', 'Patrícia Gomes',
'Luiz Henrique', 'Sofia Almeida'
];
$messages = [
'Gostaria de um orçamento para manutenção de 5 computadores na minha empresa.',
'Vocês têm notebooks da marca Dell em estoque? Qual o valor médio?',
'Estou precisando de uma placa de vídeo específica para jogos, vocês trabalham com encomendas?',
'Meu computador não liga mais após uma queda de energia. Vocês fazem busca em domicílio?',
'Quais os horários disponíveis para uma visita técnica nesta semana?',
'Gostaria de saber se vocês fazem instalação de redes cabeadas para escritórios.',
'Preciso formatar meu notebook e fazer backup de arquivos importantes.',
'Vocês vendem licenças originais do Windows e Office?',
'Meu teclado parou de funcionar algumas teclas, vocês fazem a troca?',
'Gostaria de informações sobre o plano de manutenção mensal para empresas.'
];
foreach ($names as $index => $name) {
ContactMessage::create([
'name' => $name,
'email' => Str::lower(Str::slug($name)) . '@exemplo.com',
'company' => $index % 2 == 0 ? 'Empresa ' . ($index + 1) : null,
'website' => $index % 3 == 0 ? 'https://empresa' . ($index + 1) . '.com.br' : null,
'whatsapp' => '(11) 9' . rand(7000, 9999) . '-' . rand(1000, 9999),
'subject_id' => $subjects->random()->id,
'message' => $messages[$index],
'read' => rand(0, 1),
'created_at' => now()->subDays(rand(0, 30))->subHours(rand(0, 23)),
]);
}
}
}
+27
View File
@@ -0,0 +1,27 @@
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
use App\Models\ContactSubject;
class ContactSubjectSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
$subjects = [
'Orçamento de Manutenção',
'Compra de Notebook',
'Peças e Acessórios',
'Suporte Técnico',
'Outros'
];
foreach ($subjects as $name) {
ContactSubject::updateOrCreate(['name' => $name]);
}
}
}
+23
View File
@@ -0,0 +1,23 @@
<?php
namespace Database\Seeders;
use App\Models\User;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
use WithoutModelEvents;
/**
* Seed the application's database.
*/
public function run(): void
{
$this->call([
AdminUserSeeder::class,
ContactSubjectSeeder::class,
]);
}
}
+42
View File
@@ -0,0 +1,42 @@
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
use App\Models\Faq;
class FaqSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
$faqs = [
[
'question' => 'Qual o prazo médio para manutenção de notebooks?',
'answer' => 'O prazo médio é de 2 a 5 dias úteis, dependendo da complexidade do reparo e da disponibilidade de peças.',
'order' => 1,
],
[
'question' => 'Vocês oferecem garantia nos serviços?',
'answer' => 'Sim, todos os nossos serviços possuem garantia de 90 dias, conforme o Código de Defesa do Consumidor.',
'order' => 2,
],
[
'question' => 'Fazem orçamento sem compromisso?',
'answer' => 'Sim! Realizamos a avaliação técnica inicial sem custo para o cliente.',
'order' => 3,
],
[
'question' => 'Trabalham com retirada e entrega?',
'answer' => 'Sim, possuímos serviço de leva e traz para sua maior comodidade em toda a região central.',
'order' => 4,
],
];
foreach ($faqs as $faq) {
Faq::updateOrCreate(['question' => $faq['question']], $faq);
}
}
}
+187
View File
@@ -0,0 +1,187 @@
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
use App\Models\Page;
class PageSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
$pages = [
[
'slug' => 'index',
'title' => 'Início',
'meta_description' => 'Bem-vindo à Syspanel - Tecnologia com Confiança.',
'content' => '
<section class="text-center mb-5">
<h1 class="display-3 fw-bold mb-4 text-primary">Tecnologia com Confiança</h1>
<p class="lead mb-5 fs-4 text-muted">Manutenção especializada de computadores, notebooks e venda de acessórios com as melhores marcas.</p>
<div class="d-grid gap-2 d-md-flex justify-content-md-center">
<a href="/servicos" class="btn btn-primary btn-lg px-5 py-3 me-md-2 fw-bold rounded-pill">Nossos Serviços</a>
<a href="/produtos" class="btn btn-outline-primary btn-lg px-5 py-3 rounded-pill">Ver Produtos</a>
</div>
</section>
<div class="row g-4 text-center mt-5">
<div class="col-md-4">
<div class="p-4 border rounded shadow-sm h-100 bg-white">
<i class="bi bi-tools fs-1 text-primary mb-3"></i>
<h3 class="h4 mb-3">Manutenção</h3>
<p class="text-muted">Especialistas em notebooks e desktops de todas as marcas.</p>
</div>
</div>
<div class="col-md-4">
<div class="p-4 border rounded shadow-sm h-100 bg-white">
<i class="bi bi-cart-check fs-1 text-primary mb-3"></i>
<h3 class="h4 mb-3">Vendas</h3>
<p class="text-muted">Notebooks novos e seminovos revisados com garantia.</p>
</div>
</div>
<div class="col-md-4">
<div class="p-4 border rounded shadow-sm h-100 bg-white">
<i class="bi bi-shield-check fs-1 text-primary mb-3"></i>
<h3 class="h4 mb-3">Confiança</h3>
<p class="text-muted">Transparência total em orçamentos e peças originais.</p>
</div>
</div>
</div>
',
],
[
'slug' => 'about',
'title' => 'Sobre Nós',
'meta_description' => 'Conheça a história e os valores da Syspanel.',
'content' => '
<div class="row align-items-center g-5">
<div class="col-lg-6">
<img src="https://images.unsplash.com/photo-1597733336794-12d05021d510?ixlib=rb-1.2.1&auto=format&fit=crop&w=1000&q=80" alt="Equipe" class="img-fluid rounded shadow-lg">
</div>
<div class="col-lg-6">
<h2 class="display-5 fw-bold mb-4">Syspanel: Sua parceira em tecnologia</h2>
<p class="lead mb-4 text-muted">Há mais de 10 anos no mercado, somos referência em soluções de informática.</p>
<p>Nascemos da paixão pela tecnologia e da necessidade de oferecer um serviço de manutenção transparente e qualificado.</p>
</div>
</div>
',
],
[
'slug' => 'produtos',
'title' => 'Produtos',
'meta_description' => 'Confira nossa linha de produtos e acessórios.',
'content' => '
<h2 class="display-4 fw-bold text-center mb-5 text-primary">Produtos em Destaque</h2>
<div class="row g-4">
<div class="col-md-4">
<div class="card h-100 shadow-sm border-0 bg-white text-center p-3">
<h5 class="fw-bold">Notebooks Novos</h5>
<p class="text-muted">As melhores marcas do mercado.</p>
</div>
</div>
<div class="col-md-4">
<div class="card h-100 shadow-sm border-0 bg-white text-center p-3">
<h5 class="fw-bold">Acessórios</h5>
<p class="text-muted">Periféricos de alta qualidade.</p>
</div>
</div>
<div class="col-md-4">
<div class="card h-100 shadow-sm border-0 bg-white text-center p-3">
<h5 class="fw-bold">Peças</h5>
<p class="text-muted">Upgrades e reposição.</p>
</div>
</div>
</div>
',
],
[
'slug' => 'servicos',
'title' => 'Serviços',
'meta_description' => 'Conheça nossos serviços de manutenção e consultoria.',
'content' => '
<h2 class="display-4 fw-bold text-center mb-5">O que podemos fazer por você?</h2>
<div class="row g-4">
<div class="col-md-6">
<div class="p-4 border rounded bg-white">
<h4 class="text-primary fw-bold">Manutenção de Notebooks</h4>
<p>Troca de telas, baterias e reparos em placa-mãe.</p>
</div>
</div>
<div class="col-md-6">
<div class="p-4 border rounded bg-white">
<h4 class="text-primary fw-bold">Computadores e Redes</h4>
<p>Upgrade de hardware e configuração de redes Wi-Fi.</p>
</div>
</div>
</div>
',
],
[
'slug' => 'assistentes-digitais-com-ia',
'title' => 'Assistentes Digitais com IA',
'meta_description' => 'Descubra como os assistentes digitais com IA podem transformar sua empresa.',
'content' => 'Conteúdo dinâmico em assistentes-digitais-ia.blade.php',
],
[
'slug' => 'linux-servers',
'title' => 'Servidores Linux',
'meta_description' => 'Implantação de servidores Linux robustos, seguros e de alta performance para sua empresa.',
'content' => 'Conteúdo dinâmico em servidores-linux.blade.php',
],
[
'slug' => 'erp',
'title' => 'ERP Syspanel',
'meta_description' => 'Descubra como o ERP Syspanel pode transformar a gestão da sua empresa com módulos personalizados e inteligência artificial.',
'content' => 'Conteúdo dinâmico em erp.blade.php',
],
[
'slug' => 'fluxo-de-caixa-pro',
'title' => 'Fluxo de Caixa Pro',
'meta_description' => 'Domine as finanças da sua empresa com o Fluxo de Caixa Pro. Controle total, DRE automático, IA financeira e acesso de qualquer lugar.',
'content' => 'Conteúdo dinâmico em fluxo-de-caixa-pro.blade.php',
],
[
'slug' => 'downloads',
'title' => 'Downloads',
'meta_description' => 'Baixe drivers e utilitários úteis.',
'content' => '
<h2 class="display-4 fw-bold text-center mb-5 text-primary">Downloads Recomendados</h2>
<div class="list-group">
<a href="https://anydesk.com" target="_blank" class="list-group-item list-group-item-action d-flex justify-content-between align-items-center">
AnyDesk Support
<span class="badge bg-primary rounded-pill">Baixar</span>
</a>
<a href="https://cpuid.com" target="_blank" class="list-group-item list-group-item-action d-flex justify-content-between align-items-center">
CPU-Z (Hardware)
<span class="badge bg-success rounded-pill">Acessar</span>
</a>
</div>
',
],
[
'slug' => 'contato',
'title' => 'Contato',
'meta_description' => 'Fale conosco para orçamentos e suporte técnico.',
'content' => null,
],
[
'slug' => 'faq',
'title' => 'FAQ',
'meta_description' => 'Perguntas frequentes sobre nossos serviços.',
'content' => null,
],
[
'slug' => 'politica-privacidade',
'title' => 'Política de Privacidade',
'meta_description' => 'Conheça nossos termos de uso e política de privacidade em conformidade com a LGPD.',
'content' => 'Conteúdo dinâmico em politica-privacidade.blade.php',
],
];
foreach ($pages as $page) {
Page::updateOrCreate(['slug' => $page['slug']], $page);
}
}
}
+25
View File
@@ -0,0 +1,25 @@
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
use App\Modules\Products\Models\ProductCategory;
class ProductCategorySeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
$categories = [
['name' => 'Notebooks', 'slug' => 'notebooks', 'active' => true, 'order' => 1],
['name' => 'Acessórios', 'slug' => 'acessorios', 'active' => true, 'order' => 2],
['name' => 'Peças', 'slug' => 'pecas', 'active' => true, 'order' => 3],
];
foreach ($categories as $category) {
ProductCategory::updateOrCreate(['slug' => $category['slug']], $category);
}
}
}
+70
View File
@@ -0,0 +1,70 @@
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
use App\Modules\Services\Models\Service;
class ServiceSeeder extends Seeder
{
/**
* Run the database seeds.
*/
public function run(): void
{
$services = [
[
'name' => 'Manutenção de Notebooks',
'slug' => 'manutencao-notebooks',
'description' => 'Reparo especializado em hardware, troca de telas, teclados e baterias.',
'icon' => 'bi bi-laptop',
'active' => true,
'order' => 1
],
[
'name' => 'Redes e Wi-Fi',
'slug' => 'redes-wifi',
'description' => 'Configuração de roteadores, repetidores e infraestrutura de rede cabeada.',
'icon' => 'bi bi-wifi',
'active' => true,
'order' => 2
],
[
'name' => 'Limpeza Preventiva',
'slug' => 'limpeza-preventiva',
'description' => 'Limpeza interna completa e troca de pasta térmica para evitar superaquecimento.',
'icon' => 'bi bi-wind',
'active' => true,
'order' => 3
],
[
'name' => 'Servidores Linux',
'slug' => 'servidores-linux',
'description' => 'Implantação e configuração de servidores Linux em infraestrutura local ou nuvem com alta disponibilidade.',
'icon' => 'bi bi-hdd-network',
'active' => true,
'order' => 4
],
[
'name' => 'Assistentes Digitais com IA',
'slug' => 'assistentes-digitais-ia',
'description' => 'Implantação de assistentes digitais inteligentes com IA para automatizar atendimento e processos internos.',
'icon' => 'bi bi-robot',
'active' => true,
'order' => 5
],
[
'name' => 'Registro de Marcas no INPI',
'slug' => 'registro-marcas-inpi',
'description' => 'Proteção legal da sua marca junto ao INPI, garantindo exclusividade de uso em todo território nacional.',
'icon' => 'bi bi-shield-check',
'active' => true,
'order' => 6
],
];
foreach ($services as $service) {
Service::updateOrCreate(['slug' => $service['slug']], $service);
}
}
}