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:
Executable
+10
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Banner extends Model
|
||||
{
|
||||
protected $fillable = ['title', 'image', 'link', 'button_text', 'order', 'active'];
|
||||
}
|
||||
Executable
+29
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class ContactMessage extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'name',
|
||||
'email',
|
||||
'company',
|
||||
'website',
|
||||
'whatsapp',
|
||||
'subject_id',
|
||||
'message',
|
||||
'read'
|
||||
];
|
||||
|
||||
public function subject()
|
||||
{
|
||||
return $this->belongsTo(ContactSubject::class, 'subject_id');
|
||||
}
|
||||
|
||||
public function responses()
|
||||
{
|
||||
return $this->hasMany(ContactResponse::class, 'contact_message_id');
|
||||
}
|
||||
}
|
||||
Executable
+20
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class ContactResponse extends Model
|
||||
{
|
||||
protected $fillable = ['contact_message_id', 'user_id', 'message'];
|
||||
|
||||
public function message()
|
||||
{
|
||||
return $this->belongsTo(ContactMessage::class, 'contact_message_id');
|
||||
}
|
||||
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
}
|
||||
Executable
+15
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class ContactSubject extends Model
|
||||
{
|
||||
protected $fillable = ['name', 'active'];
|
||||
|
||||
public function messages()
|
||||
{
|
||||
return $this->hasMany(ContactMessage::class, 'subject_id');
|
||||
}
|
||||
}
|
||||
Executable
+10
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Download extends Model
|
||||
{
|
||||
protected $fillable = ['title', 'description', 'file_path', 'file_type', 'download_count', 'active'];
|
||||
}
|
||||
Executable
+10
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Faq extends Model
|
||||
{
|
||||
protected $fillable = ['question', 'answer', 'order', 'active'];
|
||||
}
|
||||
Executable
+20
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Gallery extends Model
|
||||
{
|
||||
protected $fillable = ['title', 'slug', 'description', 'active', 'category_id'];
|
||||
|
||||
public function images()
|
||||
{
|
||||
return $this->hasMany(GalleryImage::class)->orderBy('order');
|
||||
}
|
||||
|
||||
public function category()
|
||||
{
|
||||
return $this->belongsTo(GalleryCategory::class);
|
||||
}
|
||||
}
|
||||
Executable
+15
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class GalleryCategory extends Model
|
||||
{
|
||||
protected $fillable = ['name', 'slug', 'description', 'active'];
|
||||
|
||||
public function galleries()
|
||||
{
|
||||
return $this->hasMany(Gallery::class, 'category_id')->orderBy('created_at', 'desc');
|
||||
}
|
||||
}
|
||||
Executable
+15
@@ -0,0 +1,15 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class GalleryImage extends Model
|
||||
{
|
||||
protected $fillable = ['gallery_id', 'file_path', 'description', 'order'];
|
||||
|
||||
public function gallery()
|
||||
{
|
||||
return $this->belongsTo(Gallery::class);
|
||||
}
|
||||
}
|
||||
Executable
+66
@@ -0,0 +1,66 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
||||
class News extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'title',
|
||||
'slug',
|
||||
'summary',
|
||||
'content',
|
||||
'featured_image',
|
||||
'author',
|
||||
'active',
|
||||
'published_at',
|
||||
'user_id',
|
||||
'seo_title',
|
||||
'seo_description',
|
||||
'seo_keywords',
|
||||
];
|
||||
|
||||
protected $casts = [
|
||||
'active' => 'boolean',
|
||||
'published_at' => 'datetime',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the images for the news article.
|
||||
*/
|
||||
public function images(): HasMany
|
||||
{
|
||||
return $this->hasMany(NewsImage::class)->orderBy('order');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user who created the news article.
|
||||
*/
|
||||
public function user(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the news article is published.
|
||||
*/
|
||||
public function isPublished(): bool
|
||||
{
|
||||
return $this->active && ($this->published_at === null || $this->published_at->isPast());
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope to get only published news.
|
||||
*/
|
||||
public function scopePublished($query)
|
||||
{
|
||||
return $query->where('active', true)
|
||||
->where(function ($q) {
|
||||
$q->whereNull('published_at')
|
||||
->orWhere('published_at', '<=', now());
|
||||
});
|
||||
}
|
||||
}
|
||||
Executable
+24
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
|
||||
class NewsImage extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'news_id',
|
||||
'file_path',
|
||||
'description',
|
||||
'order',
|
||||
];
|
||||
|
||||
/**
|
||||
* Get the news article this image belongs to.
|
||||
*/
|
||||
public function news(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(News::class);
|
||||
}
|
||||
}
|
||||
Executable
+21
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Page extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'slug',
|
||||
'title',
|
||||
'content',
|
||||
'meta_description',
|
||||
'meta_keywords',
|
||||
'og_title',
|
||||
'og_description',
|
||||
'og_image',
|
||||
'canonical_url',
|
||||
'active'
|
||||
];
|
||||
}
|
||||
Executable
+21
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class Setting extends Model
|
||||
{
|
||||
protected $fillable = ['key', 'value', 'group'];
|
||||
|
||||
public static function get($key, $default = null)
|
||||
{
|
||||
$setting = self::where('key', $key)->first();
|
||||
return ($setting && $setting->value !== '' && $setting->value !== null) ? $setting->value : $default;
|
||||
}
|
||||
|
||||
public static function set($key, $value, $group = 'general')
|
||||
{
|
||||
return self::updateOrCreate(['key' => $key], ['value' => $value, 'group' => $group]);
|
||||
}
|
||||
}
|
||||
Executable
+87
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Contracts\Auth\MustVerifyEmail;
|
||||
use Database\Factories\UserFactory;
|
||||
use Illuminate\Database\Eloquent\Attributes\Fillable;
|
||||
use Illuminate\Database\Eloquent\Attributes\Hidden;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
|
||||
#[Fillable(['name', 'email', 'avatar', 'password', 'user_type', 'company', 'website', 'whatsapp', 'preferences', 'permissions'])]
|
||||
#[Hidden(['password', 'remember_token'])]
|
||||
class User extends Authenticatable implements MustVerifyEmail
|
||||
{
|
||||
/** @use HasFactory<UserFactory> */
|
||||
use HasFactory, Notifiable;
|
||||
|
||||
/**
|
||||
* Get the attributes that should be cast.
|
||||
*
|
||||
* @return array<string, string>
|
||||
*/
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'email_verified_at' => 'datetime',
|
||||
'password' => 'hashed',
|
||||
'preferences' => 'array',
|
||||
'permissions' => 'array',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the user is an Admin.
|
||||
*/
|
||||
public function isAdmin(): bool
|
||||
{
|
||||
$adminType = \App\Models\UserType::where('is_admin', true)->pluck('name')->toArray();
|
||||
return in_array($this->user_type, $adminType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the user is a regular User.
|
||||
*/
|
||||
public function isUser(): bool
|
||||
{
|
||||
return !$this->isAdmin();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the user has a specific permission.
|
||||
* Admin types always have all permissions.
|
||||
*/
|
||||
public function hasPermission(string $permission): bool
|
||||
{
|
||||
if ($this->isAdmin()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return in_array($permission, $this->permissions ?? []);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all available permissions in the system.
|
||||
*
|
||||
* @return array<string, string>
|
||||
*/
|
||||
public static function getAvailablePermissions(): array
|
||||
{
|
||||
return [
|
||||
'contacts' => 'Gerenciar Contatos',
|
||||
'subjects' => 'Gerenciar Assuntos',
|
||||
'services' => 'Gerenciar Serviços',
|
||||
'faqs' => 'Gerenciar FAQ',
|
||||
'downloads' => 'Gerenciar Downloads',
|
||||
'products' => 'Gerenciar Produtos',
|
||||
'galleries' => 'Gerenciar Galerias',
|
||||
'news' => 'Gerenciar Notícias',
|
||||
'pages' => 'Gerenciar Páginas',
|
||||
'marketing' => 'Marketing',
|
||||
'chatbot' => 'Gerenciar Chatbot',
|
||||
'settings' => 'Dados da Empresa',
|
||||
];
|
||||
}
|
||||
}
|
||||
Executable
+36
@@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class UserType extends Model
|
||||
{
|
||||
protected $fillable = ['name', 'description', 'is_admin', 'is_protected'];
|
||||
|
||||
protected function casts(): array
|
||||
{
|
||||
return [
|
||||
'is_admin' => 'boolean',
|
||||
'is_protected' => 'boolean',
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the users with this type.
|
||||
*/
|
||||
public function users()
|
||||
{
|
||||
return User::where('user_type', $this->name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all user types as a key-value array for select fields.
|
||||
*
|
||||
* @return \Illuminate\Database\Eloquent\Collection
|
||||
*/
|
||||
public static function getAllForSelect()
|
||||
{
|
||||
return self::orderBy('name')->get();
|
||||
}
|
||||
}
|
||||
Executable
+18
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
|
||||
class VisitorLog extends Model
|
||||
{
|
||||
protected $fillable = [
|
||||
'ip_address',
|
||||
'country',
|
||||
'city',
|
||||
'region',
|
||||
'session_id',
|
||||
'user_agent',
|
||||
'page_url'
|
||||
];
|
||||
}
|
||||
Reference in New Issue
Block a user