* @created 2025-08-08 * @location Made in Germany 🇩🇪 */ namespace App\Services; use App\Models\Resume; use App\Models\User; use Illuminate\Database\Eloquent\Collection; use Illuminate\Http\Response; use Barryvdh\DomPDF\Facade\Pdf; /** * Resume Service * Handles resume creation, management, and operations */ class ResumeService { /** * Available resume templates */ protected array $templates = [ 'professional' => [ 'id' => 'professional', 'name' => 'Professional Classic', 'description' => 'Clean and professional design perfect for corporate environments', 'preview' => 'templates/professional-preview.jpg', 'category' => 'Professional' ], 'modern' => [ 'id' => 'modern', 'name' => 'Modern Creative', 'description' => 'Contemporary design with creative elements for modern companies', 'preview' => 'templates/modern-preview.jpg', 'category' => 'Creative' ], 'executive' => [ 'id' => 'executive', 'name' => 'Executive', 'description' => 'Sophisticated layout for senior-level positions', 'preview' => 'templates/executive-preview.jpg', 'category' => 'Executive' ], 'minimal' => [ 'id' => 'minimal', 'name' => 'Minimal Clean', 'description' => 'Simple and clean design focusing on content', 'preview' => 'templates/minimal-preview.jpg', 'category' => 'Minimal' ], 'technical' => [ 'id' => 'technical', 'name' => 'Technical', 'description' => 'Optimized for technical and engineering roles', 'preview' => 'templates/technical-preview.jpg', 'category' => 'Technical' ] ]; /** * Get all resumes for a specific user */ public function getUserResumes(int $userId): Collection { return Resume::where('user_id', $userId) ->orderBy('updated_at', 'desc') ->get(); } /** * Create a new resume */ public function createResume(array $resumeData): Resume { // Set default values $resumeData['is_active'] = true; $resumeData['is_completed'] = false; $resumeData['is_public'] = false; // Initialize empty sections $resumeData['personal_info'] = $resumeData['personal_info'] ?? []; $resumeData['work_experiences'] = $resumeData['work_experiences'] ?? []; $resumeData['education'] = $resumeData['education'] ?? []; $resumeData['skills'] = $resumeData['skills'] ?? []; $resumeData['languages'] = $resumeData['languages'] ?? []; $resumeData['certifications'] = $resumeData['certifications'] ?? []; $resumeData['projects'] = $resumeData['projects'] ?? []; $resumeData['references'] = $resumeData['references'] ?? []; $resumeData['custom_sections'] = $resumeData['custom_sections'] ?? []; $resumeData['settings'] = $resumeData['settings'] ?? $this->getDefaultSettings(); $resume = Resume::create($resumeData); logger()->info('Resume created', [ 'resume_id' => $resume->id, 'user_id' => $resume->user_id, 'title' => $resume->title, ]); return $resume; } /** * Update an existing resume */ public function updateResume(Resume $resume, array $resumeData): Resume { // Update completion status based on content if ($this->shouldMarkAsCompleted($resumeData)) { $resumeData['is_completed'] = true; } $resume->update($resumeData); logger()->info('Resume updated', [ 'resume_id' => $resume->id, 'user_id' => $resume->user_id, 'completion' => $resume->completion_percentage, ]); return $resume; } /** * Delete a resume */ public function deleteResume(Resume $resume): bool { $resumeId = $resume->id; $userId = $resume->user_id; $deleted = $resume->delete(); if ($deleted) { logger()->info('Resume deleted', [ 'resume_id' => $resumeId, 'user_id' => $userId, ]); } return $deleted; } /** * Duplicate a resume */ public function duplicateResume(Resume $resume, string $newTitle): Resume { $resumeData = $resume->toArray(); // Remove unique identifiers unset($resumeData['id'], $resumeData['slug'], $resumeData['public_url']); // Set new title and reset status $resumeData['title'] = $newTitle; $resumeData['is_public'] = false; $resumeData['created_at'] = now(); $resumeData['updated_at'] = now(); $newResume = Resume::create($resumeData); logger()->info('Resume duplicated', [ 'original_resume_id' => $resume->id, 'new_resume_id' => $newResume->id, 'user_id' => $resume->user_id, ]); return $newResume; } /** * Generate PDF for a resume */ public function generatePdf(Resume $resume): Response { $template = $this->getTemplate($resume->template_id ?? 'professional'); $pdf = Pdf::loadView('pdf.resume.' . $template['id'], [ 'resume' => $resume, 'template' => $template ]); $fileName = str()->slug($resume->title) . '-resume.pdf'; logger()->info('PDF generated', [ 'resume_id' => $resume->id, 'user_id' => $resume->user_id, 'template' => $template['id'], ]); return $pdf->download($fileName); } /** * Make resume public */ public function makePublic(Resume $resume): Resume { if (!$resume->public_url) { $resume->public_url = $resume->generatePublicUrl(); } $resume->update(['is_public' => true]); logger()->info('Resume made public', [ 'resume_id' => $resume->id, 'user_id' => $resume->user_id, 'public_url' => $resume->public_url, ]); return $resume; } /** * Make resume private */ public function makePrivate(Resume $resume): Resume { $resume->update(['is_public' => false]); logger()->info('Resume made private', [ 'resume_id' => $resume->id, 'user_id' => $resume->user_id, ]); return $resume; } /** * Get available templates */ public function getAvailableTemplates(): array { return $this->templates; } /** * Get specific template */ public function getTemplate(string $templateId): array { return $this->templates[$templateId] ?? $this->templates['professional']; } /** * Get resume analytics */ public function getResumeAnalytics(Resume $resume): array { return [ 'completion_percentage' => $resume->completion_percentage, 'sections_completed' => $this->getCompletedSections($resume), 'total_sections' => 7, 'word_count' => $this->calculateWordCount($resume), 'last_updated' => $resume->updated_at, 'views' => 0, // Implement view tracking if needed ]; } /** * Get completed sections count */ protected function getCompletedSections(Resume $resume): int { $sections = ['personal_info', 'professional_summary', 'work_experiences', 'education', 'skills', 'languages', 'certifications']; $completed = 0; foreach ($sections as $section) { if ($resume->isSectionCompleted($section)) { $completed++; } } return $completed; } /** * Calculate total word count in resume */ protected function calculateWordCount(Resume $resume): int { $wordCount = 0; // Professional summary if ($resume->professional_summary) { $wordCount += str_word_count(strip_tags($resume->professional_summary)); } // Work experiences if ($resume->work_experiences) { foreach ($resume->work_experiences as $experience) { $wordCount += str_word_count(strip_tags($experience['description'] ?? '')); } } // Education if ($resume->education) { foreach ($resume->education as $education) { $wordCount += str_word_count(strip_tags($education['description'] ?? '')); } } // Projects if ($resume->projects) { foreach ($resume->projects as $project) { $wordCount += str_word_count(strip_tags($project['description'] ?? '')); } } return $wordCount; } /** * Check if resume should be marked as completed */ protected function shouldMarkAsCompleted(array $resumeData): bool { $requiredSections = ['personal_info', 'work_experiences', 'education']; foreach ($requiredSections as $section) { if (empty($resumeData[$section])) { return false; } } return true; } /** * Get default resume settings */ protected function getDefaultSettings(): array { return [ 'theme' => 'light', 'font_family' => 'Roboto', 'font_size' => 14, 'color_scheme' => 'blue', 'show_photo' => false, 'show_references' => true, 'show_certifications' => true, 'show_projects' => true, 'section_order' => [ 'personal_info', 'professional_summary', 'work_experiences', 'education', 'skills', 'languages', 'certifications', 'projects', 'references' ] ]; } /** * Get resume statistics for user */ public function getUserResumeStats(int $userId): array { $resumes = $this->getUserResumes($userId); return [ 'total_resumes' => $resumes->count(), 'completed_resumes' => $resumes->where('is_completed', true)->count(), 'public_resumes' => $resumes->where('is_public', true)->count(), 'avg_completion' => $resumes->avg('completion_percentage') ?? 0, 'last_updated' => $resumes->max('updated_at'), ]; } }