getCurrentUser(); // Allow access to SuperAdmin, Admin, or Member with company.view permission $hasAccess = $user && ( $user->isSuperAdmin() || $user->isAdmin() || ($user->isMember() && $user->hasPermission('company.view')) ); if (!$hasAccess) { return new RedirectResponse(url('admin/dashboard?error=unauthorized')); } if ($user->isSuperAdmin()) { $companies = Company::all(); } else { $companies = Company::where('bus_id', $user->users_bus_id)->get(); } return $this->renderAdminView('company/views/admin/index.twig', [ 'companies' => $companies, 'title' => 'Companies Management', 'can_create_company' => $user->isSuperAdmin() && License::canCreateCompany() ]); } public function settings(Request $request): Response { $user = $this->getCurrentUser(); if (!$user) { return new RedirectResponse(url('login')); } if ($user->isSuperAdmin()) { return new RedirectResponse(url('admin/companies')); } return $this->edit($request, $user->users_bus_id); } public function create(Request $request): Response { $user = $this->getCurrentUser(); if (!$user || !$user->isSuperAdmin()) { return new RedirectResponse(url('admin/dashboard?error=unauthorized')); } if (!License::canCreateCompany()) { return new RedirectResponse(url('admin/companies?error=license_limit')); } $error = $request->query->get('error'); $errorMessage = $error ? $this->getErrorMessage($error) : null; $modules = Module::active()->ordered()->get(); return $this->renderAdminView('company/views/admin/form.twig', [ 'title' => 'Create Company', 'error' => $errorMessage, 'target_company' => null, 'modules' => $modules ]); } public function store(Request $request): Response { $user = $this->getCurrentUser(); if (!$user || !$user->isSuperAdmin()) { return new RedirectResponse(url('admin/dashboard?error=unauthorized')); } $currentUserId = $_SESSION['user_id'] ?? null; $currentUserRole = $_SESSION['user_role'] ?? null; $currentUserBus = $_SESSION['user_idbus'] ?? null; $data = $request->request->all(); // Basic validation if (empty($data['name']) || empty($data['email'])) { return new RedirectResponse(url('admin/companies/create?error=missing_fields')); } // Email format validation if (!filter_var($data['email'], FILTER_VALIDATE_EMAIL)) { return new RedirectResponse(url('admin/companies/create?error=invalid_email')); } // Check if email exists if (Company::where('bus_email', $data['email'])->exists()) { return new RedirectResponse(url('admin/companies/create?error=email_exists')); } // Generate slug from name $slug = strtolower(trim(preg_replace('/[^A-Za-z0-9-]+/', '-', $data['name']))); // Check if slug exists if (Company::where('bus_slug', $slug)->exists()) { $slug .= '-' . time(); } $company = new Company(); $company->bus_name = $data['name']; $company->bus_slug = $slug; $company->bus_description = $data['description'] ?? ''; $company->bus_email = $data['email']; $company->bus_phone = $data['phone'] ?? ''; $company->bus_address = $data['address'] ?? ''; $company->bus_city = $data['city'] ?? ''; $company->bus_state = $data['state'] ?? ''; $company->bus_country = $data['country'] ?? ''; $company->bus_postal_code = $data['postal_code'] ?? ''; $company->bus_website = $data['website'] ?? ''; $company->bus_tax_id = $data['tax_id'] ?? ''; $company->bus_type = $data['bus_type'] ?? 'business'; $company->bus_status = $data['status'] ?? 'active'; $company->platform_name = $data['platform_name'] ?? ''; // Encrypt OpenRouter API Key if present $newApiKey = $data['openrouter_api_key'] ?? ''; if (!empty($newApiKey) && strpos($newApiKey, '***') === false) { $company->openrouter_api_key = Crypto::encrypt($newApiKey); } $company->ai_provider = $data['ai_provider'] ?? 'openrouter'; $company->ai_model = $data['ai_model'] ?? 'google/gemini-2.0-flash-001'; $company->ai_endpoint = $data['ai_endpoint'] ?? ''; // Settings are now handled via modules table // $company->bus_settings = $data['bus_settings'] ?? []; $company->save(); // Handle modules - ONLY Super Admin can change which modules a company has access to if ($user->isSuperAdmin()) { $modulesData = $request->request->all()['modules'] ?? []; $syncData = []; foreach ($modulesData as $moduleId => $settings) { $syncData[$moduleId] = [ 'bus_modules_is_enabled' => isset($settings['enabled']), 'bus_modules_enabled_by' => $currentUserId, 'bus_modules_enabled_at' => \Carbon\Carbon::now(), 'bus_modules_show_public_nav' => isset($settings['public']), 'bus_modules_show_dashboard_nav' => isset($settings['dashboard']) ]; } if (!empty($syncData)) { $company->modules()->sync($syncData); } else { $company->modules()->detach(); } } if ($request->files->has('logo')) { $file = $request->files->get('logo'); if ($file && $file->isValid()) { $allowedMimes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp']; if (in_array($file->getMimeType(), $allowedMimes)) { $uploadDir = dirname(__DIR__, 4) . '/storage/uploads/company/logos'; if (!file_exists($uploadDir)) { mkdir($uploadDir, 0755, true); } $formatSetting = strtolower((string) get_env('IMG_FORMAT', 'webp')); $format = $formatSetting === 'auto' ? 'webp' : $formatSetting; $quality = env_int('IMG_QUALITY', 80); $resolutionPercent = env_int('IMG_RESOLUTION', 100); $image = Image::make($file->getPathname()); $width = $image->width(); $height = $image->height(); if ($resolutionPercent > 0 && $resolutionPercent < 100) { $newW = (int) round($width * $resolutionPercent / 100); $newH = (int) round($height * $resolutionPercent / 100); $image->resize($newW, $newH, function ($constraint) { $constraint->aspectRatio(); $constraint->upsize(); }); } $ext = $format === 'jpeg' || $format === 'jpg' ? 'jpg' : ($format === 'png' ? 'png' : ($format === 'gif' ? 'gif' : 'webp')); $filename = 'logo_' . $company->bus_id . '_' . time() . '.' . $ext; if ($format === 'jpeg' || $format === 'jpg') { $image->interlace(true); $image->encode('jpeg', max(1, min(100, $quality))); } elseif ($format === 'png') { $pngCompression = max(0, min(9, (int) round((100 - max(1, min(100, $quality))) / 10))); $image->encode('png', $pngCompression); } elseif ($format === 'gif') { $image->encode('gif'); } else { $image->encode('webp', max(1, min(100, $quality))); } $image->save($uploadDir . '/' . $filename); // Delete old logo if exists (check both old and new directories) if ($company->bus_logo) { $oldDirs = [ dirname(__DIR__, 4) . '/storage/uploads/logos', dirname(__DIR__, 4) . '/storage/uploads/company/logos' ]; foreach ($oldDirs as $dir) { $oldFile = $dir . '/' . $company->bus_logo; if (file_exists($oldFile)) { unlink($oldFile); break; // Delete only once } } } $company->bus_logo = $filename; } } } // Handle business card upload if ($request->files->has('business_card')) { $file = $request->files->get('business_card'); if ($file && $file->isValid()) { $allowedMimes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp']; if (in_array($file->getMimeType(), $allowedMimes)) { $uploadDir = dirname(__DIR__, 4) . '/storage/uploads/company/business-cards'; if (!file_exists($uploadDir)) { mkdir($uploadDir, 0755, true); } $formatSetting = strtolower((string) get_env('IMG_FORMAT', 'webp')); $format = $formatSetting === 'auto' ? 'webp' : $formatSetting; $quality = env_int('IMG_QUALITY', 80); $resolutionPercent = env_int('IMG_RESOLUTION', 100); $image = Image::make($file->getPathname()); $width = $image->width(); $height = $image->height(); if ($resolutionPercent > 0 && $resolutionPercent < 100) { $newW = (int) round($width * $resolutionPercent / 100); $newH = (int) round($height * $resolutionPercent / 100); $image->resize($newW, $newH, function ($constraint) { $constraint->aspectRatio(); $constraint->upsize(); }); } $ext = $format === 'jpeg' || $format === 'jpg' ? 'jpg' : ($format === 'png' ? 'png' : ($format === 'gif' ? 'gif' : 'webp')); $filename = 'business_card_' . $company->bus_id . '_' . time() . '.' . $ext; if ($format === 'jpeg' || $format === 'jpg') { $image->interlace(true); $image->encode('jpeg', max(1, min(100, $quality))); } elseif ($format === 'png') { $pngCompression = max(0, min(9, (int) round((100 - max(1, min(100, $quality))) / 10))); $image->encode('png', $pngCompression); } elseif ($format === 'gif') { $image->encode('gif'); } else { $image->encode('webp', max(1, min(100, $quality))); } $image->save($uploadDir . '/' . $filename); // Delete old business card if exists (check both old and new directories) if ($company->bus_business_card) { $oldDirs = [ dirname(__DIR__, 4) . '/storage/uploads/business-cards', dirname(__DIR__, 4) . '/storage/uploads/company/business-cards' ]; foreach ($oldDirs as $dir) { $oldFile = $dir . '/' . $company->bus_business_card; if (file_exists($oldFile)) { unlink($oldFile); break; // Delete only once } } } $company->bus_business_card = $filename; } } } // Handle document upload if ($request->files->has('document')) { $file = $request->files->get('document'); if ($file && $file->isValid()) { $allowedMimes = [ 'application/pdf', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'application/vnd.ms-powerpoint', 'application/vnd.openxmlformats-officedocument.presentationml.presentation' ]; if (in_array($file->getMimeType(), $allowedMimes)) { $uploadDir = dirname(__DIR__, 4) . '/storage/uploads/company/documents'; if (!file_exists($uploadDir)) { mkdir($uploadDir, 0755, true); } $originalName = $file->getClientOriginalName(); $filename = 'document_' . $company->bus_id . '_' . time() . '_' . $originalName; $file->move($uploadDir, $filename); // Delete old document if exists (check both old and new directories) if ($company->bus_document) { $oldDirs = [ dirname(__DIR__, 4) . '/storage/uploads/documents', dirname(__DIR__, 4) . '/storage/uploads/company/documents' ]; foreach ($oldDirs as $dir) { $oldFile = $dir . '/' . $company->bus_document; if (file_exists($oldFile)) { unlink($oldFile); break; // Delete only once } } } $company->bus_document = $filename; } } } $company->save(); // Restore session to ensure super_admin remains logged $_SESSION['user_id'] = $currentUserId; $_SESSION['user_role'] = $currentUserRole; $_SESSION['user_idbus'] = $currentUserBus; return new RedirectResponse(url('admin/companies')); } public function show(Request $request, string $id): Response { $user = $this->getCurrentUser(); if (!$user || (!$user->isSuperAdmin() && $user->users_bus_id != $id)) { return new RedirectResponse(url('admin/dashboard?error=unauthorized')); } $company = Company::find($id); if (!$company) { return new RedirectResponse(url('admin/companies')); } return $this->renderAdminView('company/views/admin/show.twig', [ 'company' => $company, 'title' => 'Company Details' ]); } public function edit(Request $request, string $id): Response { $user = $this->getCurrentUser(); if (!$user || (!$user->isSuperAdmin() && $user->users_bus_id != $id)) { return new RedirectResponse(url('admin/dashboard?error=unauthorized')); } $company = Company::find($id); if (!$company) { return new RedirectResponse(url('admin/companies')); } // Mask the API Key for the view if ($company->openrouter_api_key) { $company->openrouter_api_key = substr(Crypto::decrypt($company->openrouter_api_key), 0, 8) . '****************'; } $error = $request->query->get('error'); $errorMessage = $error ? $this->getErrorMessage($error) : null; $modules = Module::active()->ordered()->get(); return $this->renderAdminView('company/views/admin/form.twig', [ 'target_company' => $company, 'title' => 'Edit Company', 'error' => $errorMessage, 'modules' => $modules ]); } public function update(Request $request, string $id): Response { $user = $this->getCurrentUser(); if (!$user || (!$user->isSuperAdmin() && $user->users_bus_id != $id)) { return new RedirectResponse(url('admin/dashboard?error=unauthorized')); } $currentUserId = $_SESSION['user_id'] ?? null; $currentUserRole = $_SESSION['user_role'] ?? null; $currentUserBus = $_SESSION['user_idbus'] ?? null; $company = Company::find($id); if (!$company) { return new RedirectResponse(url('admin/companies')); } $data = $request->request->all(); // Basic validation if (empty($data['name']) || empty($data['email'])) { return new RedirectResponse(url('admin/companies/' . $id . '/edit?error=missing_fields')); } // Email format validation if (!filter_var($data['email'], FILTER_VALIDATE_EMAIL)) { return new RedirectResponse(url('admin/companies/' . $id . '/edit?error=invalid_email')); } // Check if email exists (exclude current company) if ($data['email'] !== $company->bus_email && Company::where('bus_email', $data['email'])->exists()) { return new RedirectResponse(url('admin/companies/' . $id . '/edit?error=email_exists')); } $company->bus_name = $data['name']; $company->bus_description = $data['description'] ?? ''; $company->bus_email = $data['email']; $company->bus_phone = $data['phone'] ?? ''; $company->bus_address = $data['address'] ?? ''; $company->bus_city = $data['city'] ?? ''; $company->bus_state = $data['state'] ?? ''; $company->bus_country = $data['country'] ?? ''; $company->bus_postal_code = $data['postal_code'] ?? ''; $company->bus_website = $data['website'] ?? ''; $company->bus_tax_id = $data['tax_id'] ?? ''; $company->bus_type = $data['bus_type'] ?? 'business'; $company->bus_status = $data['status'] ?? 'active'; $company->platform_name = $data['platform_name'] ?? ''; // Encrypt OpenRouter API Key if present $newApiKey = $data['openrouter_api_key'] ?? ''; if (!empty($newApiKey) && strpos($newApiKey, '***') === false) { $company->openrouter_api_key = Crypto::encrypt($newApiKey); } $company->ai_provider = $data['ai_provider'] ?? 'openrouter'; $company->ai_model = $data['ai_model'] ?? 'google/gemini-2.0-flash-001'; $company->ai_endpoint = $data['ai_endpoint'] ?? ''; // Settings are now handled via modules table // $company->bus_settings = $data['bus_settings'] ?? []; // Update slug if name changed if ($data['name'] !== $company->getOriginal('name')) { $slug = strtolower(trim(preg_replace('/[^A-Za-z0-9-]+/', '-', $data['name']))); // Check if slug exists (exclude current company) if ($slug !== $company->bus_slug && Company::where('bus_slug', $slug)->exists()) { $slug .= '-' . time(); } $company->bus_slug = $slug; } $company->save(); // Handle modules - ONLY Super Admin can change which modules a company has access to if ($user->isSuperAdmin()) { $modulesData = $request->request->all()['modules'] ?? []; $syncData = []; foreach ($modulesData as $moduleId => $settings) { $syncData[$moduleId] = [ 'bus_modules_is_enabled' => isset($settings['enabled']), 'bus_modules_enabled_by' => $currentUserId, 'bus_modules_enabled_at' => \Carbon\Carbon::now(), 'bus_modules_show_public_nav' => isset($settings['public']), 'bus_modules_show_dashboard_nav' => isset($settings['dashboard']) ]; } if (!empty($syncData)) { $company->modules()->sync($syncData); } else { $company->modules()->detach(); } } if ($request->files->has('logo')) { $file = $request->files->get('logo'); if ($file && $file->isValid()) { $allowedMimes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp']; if (in_array($file->getMimeType(), $allowedMimes)) { $uploadDir = dirname(__DIR__, 4) . '/storage/uploads/company/logos'; if (!file_exists($uploadDir)) { mkdir($uploadDir, 0755, true); } $formatSetting = strtolower((string) get_env('IMG_FORMAT', 'webp')); $format = $formatSetting === 'auto' ? 'webp' : $formatSetting; $quality = env_int('IMG_QUALITY', 80); $resolutionPercent = env_int('IMG_RESOLUTION', 100); $image = Image::make($file->getPathname()); $width = $image->width(); $height = $image->height(); if ($resolutionPercent > 0 && $resolutionPercent < 100) { $newW = (int) round($width * $resolutionPercent / 100); $newH = (int) round($height * $resolutionPercent / 100); $image->resize($newW, $newH, function ($constraint) { $constraint->aspectRatio(); $constraint->upsize(); }); } $ext = $format === 'jpeg' || $format === 'jpg' ? 'jpg' : ($format === 'png' ? 'png' : ($format === 'gif' ? 'gif' : 'webp')); $filename = 'logo_' . $company->bus_id . '_' . time() . '.' . $ext; if ($format === 'jpeg' || $format === 'jpg') { $image->interlace(true); $image->encode('jpeg', max(1, min(100, $quality))); } elseif ($format === 'png') { $pngCompression = max(0, min(9, (int) round((100 - max(1, min(100, $quality))) / 10))); $image->encode('png', $pngCompression); } elseif ($format === 'gif') { $image->encode('gif'); } else { $image->encode('webp', max(1, min(100, $quality))); } $image->save($uploadDir . '/' . $filename); if ($company->bus_logo) { $oldDirs = [ dirname(__DIR__, 4) . '/storage/uploads/logos', dirname(__DIR__, 4) . '/storage/uploads/company/logos' ]; foreach ($oldDirs as $dir) { $oldFile = $dir . '/' . $company->bus_logo; if (file_exists($oldFile)) { unlink($oldFile); break; } } } $company->bus_logo = $filename; } } } // Handle business card upload if ($request->files->has('business_card')) { $file = $request->files->get('business_card'); if ($file && $file->isValid()) { $allowedMimes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp']; if (in_array($file->getMimeType(), $allowedMimes)) { $uploadDir = dirname(__DIR__, 4) . '/storage/uploads/company/business-cards'; if (!file_exists($uploadDir)) { mkdir($uploadDir, 0755, true); } $formatSetting = strtolower((string) get_env('IMG_FORMAT', 'webp')); $format = $formatSetting === 'auto' ? 'webp' : $formatSetting; $quality = env_int('IMG_QUALITY', 80); $resolutionPercent = env_int('IMG_RESOLUTION', 100); $image = Image::make($file->getPathname()); $width = $image->width(); $height = $image->height(); if ($resolutionPercent > 0 && $resolutionPercent < 100) { $newW = (int) round($width * $resolutionPercent / 100); $newH = (int) round($height * $resolutionPercent / 100); $image->resize($newW, $newH, function ($constraint) { $constraint->aspectRatio(); $constraint->upsize(); }); } $ext = $format === 'jpeg' || $format === 'jpg' ? 'jpg' : ($format === 'png' ? 'png' : ($format === 'gif' ? 'gif' : 'webp')); $filename = 'business_card_' . $company->bus_id . '_' . time() . '.' . $ext; if ($format === 'jpeg' || $format === 'jpg') { $image->interlace(true); $image->encode('jpeg', max(1, min(100, $quality))); } elseif ($format === 'png') { $pngCompression = max(0, min(9, (int) round((100 - max(1, min(100, $quality))) / 10))); $image->encode('png', $pngCompression); } elseif ($format === 'gif') { $image->encode('gif'); } else { $image->encode('webp', max(1, min(100, $quality))); } $image->save($uploadDir . '/' . $filename); if ($company->bus_business_card) { $oldDirs = [ dirname(__DIR__, 4) . '/storage/uploads/business-cards', dirname(__DIR__, 4) . '/storage/uploads/company/business-cards' ]; foreach ($oldDirs as $dir) { $oldFile = $dir . '/' . $company->bus_business_card; if (file_exists($oldFile)) { unlink($oldFile); break; } } } $company->bus_business_card = $filename; } } } // Handle document upload if ($request->files->has('document')) { $file = $request->files->get('document'); if ($file && $file->isValid()) { $allowedMimes = [ 'application/pdf', 'application/msword', 'application/vnd.openxmlformats-officedocument.wordprocessingml.document', 'application/vnd.ms-excel', 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet', 'application/vnd.ms-powerpoint', 'application/vnd.openxmlformats-officedocument.presentationml.presentation' ]; if (in_array($file->getMimeType(), $allowedMimes)) { $uploadDir = dirname(__DIR__, 4) . '/storage/uploads/company/documents'; if (!file_exists($uploadDir)) { mkdir($uploadDir, 0755, true); } $originalName = $file->getClientOriginalName(); $filename = 'document_' . $company->bus_id . '_' . time() . '_' . $originalName; $file->move($uploadDir, $filename); if ($company->bus_document) { $oldDirs = [ dirname(__DIR__, 4) . '/storage/uploads/documents', dirname(__DIR__, 4) . '/storage/uploads/company/documents' ]; foreach ($oldDirs as $dir) { $oldFile = $dir . '/' . $company->bus_document; if (file_exists($oldFile)) { unlink($oldFile); break; } } } $company->bus_document = $filename; } } } $company->save(); // Restore session to ensure super_admin remains logged $_SESSION['user_id'] = $currentUserId; $_SESSION['user_role'] = $currentUserRole; $_SESSION['user_idbus'] = $currentUserBus; return new RedirectResponse(url('admin/companies')); } private function getErrorMessage(string $error): string { $messages = [ 'missing_fields' => 'Por favor, preencha todos os campos obrigatórios.', 'email_exists' => 'Este endereço de e-mail já está registrado.', 'invalid_email' => 'Por favor, insira um endereço de e-mail válido.', ]; return $messages[$error] ?? 'Ocorreu um erro.'; } public function destroy(Request $request, string $id): Response { $user = $this->getCurrentUser(); if (!$user || !$user->isSuperAdmin()) { return new RedirectResponse(url('admin/dashboard?error=unauthorized')); } $company = Company::find($id); if ($company) { // Delete logo file if exists if ($company->bus_logo) { $uploadDir = dirname(__DIR__, 4) . '/storage/uploads/company/logos'; if (file_exists($uploadDir . '/' . $company->bus_logo)) { unlink($uploadDir . '/' . $company->bus_logo); } } // Delete business card file if exists if ($company->bus_business_card) { $uploadDir = dirname(__DIR__, 4) . '/storage/uploads/company/business-cards'; if (file_exists($uploadDir . '/' . $company->bus_business_card)) { unlink($uploadDir . '/' . $company->bus_business_card); } } // Delete document file if exists if ($company->bus_document) { $uploadDir = dirname(__DIR__, 4) . '/storage/uploads/company/documents'; if (file_exists($uploadDir . '/' . $company->bus_document)) { unlink($uploadDir . '/' . $company->bus_document); } } $company->delete(); } return new RedirectResponse(url('admin/companies')); } }