First Commit

This commit is contained in:
marco
2026-09-01 10:46:29 -03:00
commit bf96b77e9c
10000 changed files with 3508600 additions and 0 deletions
@@ -0,0 +1,419 @@
<?php
namespace Modules\AffiliatesAdmin\Controllers;
use App\Controllers\BaseAdminController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Modules\AffiliatesAdmin\Models\Affiliate;
use Modules\AffiliatesAdmin\Models\AffiliateCommission;
use Modules\AffiliatesAdmin\Models\AffiliateBanner;
use Modules\AffiliatesAdmin\Models\AffiliateClick;
use Modules\Auth\Models\User;
class AffiliatesAdminController extends BaseAdminController
{
/**
* Check if user has permission.
*
* @param string $permission
* @return void
*/
protected function checkPermission(string $permission)
{
$user = $this->getCurrentUser();
if (!$user || !$user->hasPermission($permission)) {
$this->addFlash('error', 'Você não tem permissão para acessar esta área.');
$this->redirect('admin/dashboard')->send();
exit;
}
}
/**
* Display a listing of the affiliates.
*
* @param Request $request
* @return Response
*/
public function index(Request $request): Response
{
$this->checkPermission('affiliatesadmin.view');
$affiliates = Affiliate::all();
return $this->renderAdminView('@affiliatesadmin/index.twig', ['affiliates' => $affiliates]);
}
/**
* Show the form for creating a new affiliate.
*
* @param Request $request
* @return Response
*/
public function create(Request $request): Response
{
$this->checkPermission('affiliatesadmin.manage');
return $this->renderAdminView('@affiliatesadmin/form.twig');
}
/**
* Store a newly created affiliate in storage.
*
* @param Request $request
* @return Response
*/
public function store(Request $request): Response
{
$this->checkPermission('affiliatesadmin.manage');
$data = $request->request->all();
if (empty($data['affiliates_name']) || empty($data['affiliates_email'])) {
$this->addFlash('error', __('affiliatesadmin.validation_name_email_required'));
return $this->redirect('/admin/affiliatesadmin/create');
}
if (!filter_var($data['affiliates_email'], FILTER_VALIDATE_EMAIL)) {
$this->addFlash('error', __('affiliatesadmin.validation_invalid_email'));
return $this->redirect('/admin/affiliatesadmin/create');
}
if (Affiliate::where('affiliates_email', $data['affiliates_email'])->exists()) {
$this->addFlash('error', __('affiliatesadmin.validation_email_exists'));
return $this->redirect('/admin/affiliatesadmin/create');
}
// Check if user already exists in the system
$existingUser = User::where('users_email', $data['affiliates_email'])->first();
if (!$existingUser && empty($data['password'])) {
$this->addFlash('error', 'A senha é obrigatória para criar um novo usuário no sistema.');
return $this->redirect('/admin/affiliatesadmin/create');
}
// Create the Affiliate record
Affiliate::create($data);
if ($existingUser) {
// Only change role if user is a standard 'user'
// Prevent downgrading admins or super_admins
if (in_array($existingUser->users_role, ['user', ''])) {
$existingUser->users_role = 'member';
$existingUser->save();
$this->addFlash('success', __('affiliatesadmin.affiliate_created_successfully') . ' O usuário existente foi promovido para "member".');
} else {
$this->addFlash('success', __('affiliatesadmin.affiliate_created_successfully') . ' O usuário existente manteve seu nível de acesso privilegiado: ' . $existingUser->users_role);
}
} else {
// Create a new System User
$user = new User();
$user->users_name = $data['affiliates_name'];
$user->users_email = $data['affiliates_email'];
$user->users_password = password_hash($data['password'], PASSWORD_DEFAULT);
$user->users_role = 'member';
$user->users_status = 'active';
$user->save();
$this->addFlash('success', __('affiliatesadmin.affiliate_created_successfully') . ' Um novo usuário com nível "member" foi criado.');
}
return $this->redirect('/admin/affiliatesadmin');
}
/**
* Display the specified affiliate.
*
* @param Request $request
* @param int $id
* @return Response
*/
public function show(Request $request, int $id): Response
{
$this->checkPermission('affiliatesadmin.view');
$affiliate = Affiliate::findOrFail($id);
return $this->renderAdminView('@affiliatesadmin/show.twig', ['affiliate' => $affiliate]);
}
/**
* Show the form for editing the specified affiliate.
*
* @param Request $request
* @param int $id
* @return Response
*/
public function edit(Request $request, int $id): Response
{
$this->checkPermission('affiliatesadmin.manage');
$affiliate = Affiliate::findOrFail($id);
return $this->renderAdminView('@affiliatesadmin/form.twig', ['affiliate' => $affiliate]);
}
/**
* Update the specified affiliate in storage.
*
* @param Request $request
* @param int $id
* @return Response
*/
public function update(Request $request, int $id): Response
{
$this->checkPermission('affiliatesadmin.manage');
$affiliate = Affiliate::findOrFail($id);
$data = $request->request->all();
if (empty($data['affiliates_name']) || empty($data['affiliates_email'])) {
$this->addFlash('error', __('affiliatesadmin.validation_name_email_required'));
return $this->redirect('/admin/affiliatesadmin/' . $id . '/edit');
}
if (!filter_var($data['affiliates_email'], FILTER_VALIDATE_EMAIL)) {
$this->addFlash('error', __('affiliatesadmin.validation_invalid_email'));
return $this->redirect('/admin/affiliatesadmin/' . $id . '/edit');
}
if (Affiliate::where('affiliates_email', $data['affiliates_email'])->where('affiliates_id', '!=', $id)->exists()) {
$this->addFlash('error', __('affiliatesadmin.validation_email_exists'));
return $this->redirect('/admin/affiliatesadmin/' . $id . '/edit');
}
$affiliate->update($data);
$this->addFlash('success', __('affiliatesadmin.affiliate_updated_successfully'));
return $this->redirect('/admin/affiliatesadmin');
}
/**
* Remove the specified affiliate from storage.
*
* @param Request $request
* @param int $id
* @return Response
*/
public function destroy(Request $request, int $id): Response
{
$this->checkPermission('affiliatesadmin.manage');
Affiliate::destroy($id);
$this->addFlash('success', __('affiliatesadmin.affiliate_deleted_successfully'));
return $this->redirect('/admin/affiliatesadmin');
}
/**
* Display a listing of commissions.
*
* @param Request $request
* @return Response
*/
public function commissions(Request $request): Response
{
$this->checkPermission('affiliatesadmin.view_commissions');
$commissions = AffiliateCommission::all();
return $this->renderAdminView('@affiliatesadmin/commissions.twig', ['commissions' => $commissions]);
}
/**
* Approve a commission.
*
* @param Request $request
* @param int $id
* @return Response
*/
public function approveCommission(Request $request, int $id): Response
{
$this->checkPermission('affiliatesadmin.manage_commissions');
$commission = AffiliateCommission::findOrFail($id);
$commission->affiliate_commissions_status = 'approved';
$commission->save();
$this->addFlash('success', __('affiliatesadmin.commission_approved_successfully'));
return $this->redirect('/admin/affiliatesadmin/commissions');
}
/**
* Reject a commission.
*
* @param Request $request
* @param int $id
* @return Response
*/
public function rejectCommission(Request $request, int $id): Response
{
$this->checkPermission('affiliatesadmin.manage_commissions');
$commission = AffiliateCommission::findOrFail($id);
$commission->affiliate_commissions_status = 'rejected';
$commission->save();
$this->addFlash('success', __('affiliatesadmin.commission_rejected_successfully'));
return $this->redirect('/admin/affiliatesadmin/commissions');
}
/**
* Display a listing of banners.
*
* @param Request $request
* @return Response
*/
public function banners(Request $request): Response
{
$this->checkPermission('affiliatesadmin.view_banners');
$banners = AffiliateBanner::all();
return $this->renderAdminView('@affiliatesadmin/banners.twig', ['banners' => $banners]);
}
/**
* Show the form for creating a new banner.
*
* @param Request $request
* @return Response
*/
public function createBanner(Request $request): Response
{
$this->checkPermission('affiliatesadmin.manage_banners');
return $this->renderAdminView('@affiliatesadmin/banner-form.twig');
}
/**
* Store a newly created banner in storage.
*
* @param Request $request
* @return Response
*/
public function storeBanner(Request $request): Response
{
$this->checkPermission('affiliatesadmin.manage_banners');
$data = $request->request->all();
if (empty($data['affiliate_banners_name']) || empty($data['affiliate_banners_target_url'])) {
$this->addFlash('error', __('affiliatesadmin.validation_banner_fields_required'));
return $this->redirect('/admin/affiliatesadmin/banners/create');
}
if ($request->files->has('banner_file')) {
$file = $request->files->get('banner_file');
if ($file && $file->isValid()) {
$data['affiliate_banners_image_url'] = $this->handleBannerUpload($file);
}
}
if (empty($data['affiliate_banners_image_url'])) {
$this->addFlash('error', 'O arquivo de imagem do banner é obrigatório.');
return $this->redirect('/admin/affiliatesadmin/banners/create');
}
AffiliateBanner::create($data);
$this->addFlash('success', __('affiliatesadmin.banner_created_successfully'));
return $this->redirect('/admin/affiliatesadmin/banners');
}
/**
* Show the form for editing the specified banner.
*
* @param Request $request
* @param int $id
* @return Response
*/
public function editBanner(Request $request, int $id): Response
{
$banner = AffiliateBanner::findOrFail($id);
return $this->renderAdminView('@affiliatesadmin/banner-form.twig', ['banner' => $banner]);
}
/**
* Update the specified banner in storage.
*
* @param Request $request
* @param int $id
* @return Response
*/
public function updateBanner(Request $request, int $id): Response
{
$banner = AffiliateBanner::findOrFail($id);
$data = $request->request->all();
if (empty($data['affiliate_banners_name']) || empty($data['affiliate_banners_target_url'])) {
$this->addFlash('error', __('affiliatesadmin.validation_banner_fields_required'));
return $this->redirect('/admin/affiliatesadmin/banners/' . $id . '/edit');
}
if ($request->files->has('banner_file')) {
$file = $request->files->get('banner_file');
if ($file && $file->isValid()) {
// Delete old file if exists
if ($banner->affiliate_banners_image_url) {
$oldPath = dirname(__DIR__, 4) . '/storage/uploads/affiliatesadmin/' . $banner->affiliate_banners_image_url;
if (file_exists($oldPath) && is_file($oldPath)) {
unlink($oldPath);
}
}
$data['affiliate_banners_image_url'] = $this->handleBannerUpload($file);
}
}
$banner->update($data);
$this->addFlash('success', __('affiliatesadmin.banner_updated_successfully'));
return $this->redirect('/admin/affiliatesadmin/banners');
}
/**
* Handle banner image upload.
*
* @param \Symfony\Component\HttpFoundation\File\UploadedFile $file
* @return string|null
*/
private function handleBannerUpload($file): ?string
{
$uploadDir = dirname(__DIR__, 4) . '/storage/uploads/affiliatesadmin';
if (!is_dir($uploadDir)) {
if (!mkdir($uploadDir, 0777, true)) {
return null;
}
}
$filename = uniqid('banner_') . '.' . $file->guessExtension();
$file->move($uploadDir, $filename);
// Store ONLY the filename in the database
// The view will prepend /storage/uploads/affiliatesadmin/
return $filename;
}
/**
* Remove the specified banner from storage.
*
* @param Request $request
* @param int $id
* @return Response
*/
public function destroyBanner(Request $request, int $id): Response
{
$banner = AffiliateBanner::findOrFail($id);
if ($banner->affiliate_banners_image_url) {
$path = dirname(__DIR__, 4) . '/storage/uploads/affiliatesadmin/' . $banner->affiliate_banners_image_url;
if (file_exists($path) && is_file($path)) {
unlink($path);
}
}
AffiliateBanner::destroy($id);
$this->addFlash('success', __('affiliatesadmin.banner_deleted_successfully'));
return $this->redirect('/admin/affiliatesadmin/banners');
}
/**
* Display affiliate statistics.
*
* @param Request $request
* @return Response
*/
public function statistics(Request $request): Response
{
$this->checkPermission('affiliatesadmin.view_statistics');
$stats = [
'total_affiliates' => Affiliate::count(),
'total_commissions_approved' => AffiliateCommission::where('affiliate_commissions_status', 'approved')->sum('affiliate_commissions_amount'),
'total_clicks' => AffiliateClick::count(),
'conversion_rate' => 0,
];
if ($stats['total_clicks'] > 0) {
$approvedCount = AffiliateCommission::where('affiliate_commissions_status', 'approved')->count();
$stats['conversion_rate'] = round(($approvedCount / $stats['total_clicks']) * 100, 2);
}
return $this->renderAdminView('@affiliatesadmin/statistics.twig', [
'stats' => $stats
]);
}
}
+80
View File
@@ -0,0 +1,80 @@
<?php
namespace Modules\AffiliatesAdmin\Models;
use Illuminate\Database\Eloquent\Model;
class Affiliate extends Model
{
protected $table = 'affiliates';
protected $primaryKey = 'affiliates_id';
protected $fillable = [
'affiliates_code',
'affiliates_name',
'affiliates_email',
'affiliates_phone',
'affiliates_address',
'affiliates_website',
'affiliates_commission_rate',
'affiliates_total_earned',
'affiliates_total_paid',
'affiliates_status',
];
/**
* The "booting" method of the model.
*
* @return void
*/
protected static function boot()
{
parent::boot();
// Ensure affiliates_code is generated before creating
static::creating(function ($affiliate) {
if (empty($affiliate->affiliates_code)) {
$affiliate->affiliates_code = static::generateUniqueAffiliateCode();
}
});
}
/**
* Generate a unique affiliate code.
*
* @return string
*/
protected static function generateUniqueAffiliateCode(): string
{
$code = \Illuminate\Support\Str::random(10); // Using Laravel's Str helper
while (static::where('affiliates_code', $code)->exists()) {
$code = \Illuminate\Support\Str::random(10);
}
return $code;
}
/**
* Get the commissions for the affiliate.
*/
public function commissions()
{
return $this->hasMany(AffiliateCommission::class, 'affiliate_commissions_affiliate_id', 'affiliates_id');
}
/**
* Get the banners created by the affiliate.
*/
public function banners()
{
return $this->hasMany(AffiliateBanner::class, 'affiliate_banners_affiliate_id', 'affiliates_id');
}
/**
* Get the clicks for the affiliate's banners.
*/
public function clicks()
{
return $this->hasMany(AffiliateClick::class, 'affiliate_clicks_affiliate_id', 'affiliates_id');
}
}
+38
View File
@@ -0,0 +1,38 @@
<?php
namespace Modules\AffiliatesAdmin\Models;
use Illuminate\Database\Eloquent\Model;
class AffiliateBanner extends Model
{
protected $table = 'affiliate_banners';
protected $primaryKey = 'affiliate_banners_id';
protected $fillable = [
'affiliate_banners_name',
'affiliate_banners_description',
'affiliate_banners_image_url',
'affiliate_banners_target_url',
'affiliate_banners_width',
'affiliate_banners_height',
'affiliate_banners_is_active',
];
/**
* Get the affiliate that owns the banner.
*/
public function affiliate()
{
return $this->belongsTo(Affiliate::class, 'affiliate_banners_affiliate_id', 'affiliates_id');
}
/**
* Get the clicks for the banner.
*/
public function clicks()
{
return $this->hasMany(AffiliateClick::class, 'affiliate_clicks_banner_id', 'affiliate_banners_id');
}
}
+36
View File
@@ -0,0 +1,36 @@
<?php
namespace Modules\AffiliatesAdmin\Models;
use Illuminate\Database\Eloquent\Model;
class AffiliateClick extends Model
{
protected $table = 'affiliate_clicks';
protected $primaryKey = 'affiliate_clicks_id';
protected $fillable = [
'affiliate_clicks_affiliate_id',
'affiliate_clicks_banner_id',
'affiliate_clicks_ip_address',
'affiliate_clicks_user_agent',
'affiliate_clicks_referrer',
];
/**
* Get the affiliate that generated the click.
*/
public function affiliate()
{
return $this->belongsTo(Affiliate::class, 'affiliate_clicks_affiliate_id', 'affiliates_id');
}
/**
* Get the banner that was clicked.
*/
public function banner()
{
return $this->belongsTo(AffiliateBanner::class, 'affiliate_clicks_banner_id', 'affiliate_banners_id');
}
}
@@ -0,0 +1,29 @@
<?php
namespace Modules\AffiliatesAdmin\Models;
use Illuminate\Database\Eloquent\Model;
class AffiliateCommission extends Model
{
protected $table = 'affiliate_commissions';
protected $primaryKey = 'affiliate_commissions_id';
protected $fillable = [
'affiliate_commissions_affiliate_id',
'affiliate_commissions_order_id',
'affiliate_commissions_amount',
'affiliate_commissions_commission_rate',
'affiliate_commissions_status',
'affiliate_commissions_notes',
];
/**
* Get the affiliate that owns the commission.
*/
public function affiliate()
{
return $this->belongsTo(Affiliate::class, 'affiliate_commissions_affiliate_id', 'affiliates_id');
}
}