* @created 2025-08-08 * @location Made in Germany 🇩🇪 */ namespace App\Http\Controllers; use App\Http\Requests\Resume\StoreResumeRequest; use App\Http\Requests\Resume\UpdateResumeRequest; use App\Models\Resume; use App\Interfaces\ResumeRepositoryInterface; use App\Interfaces\UserRepositoryInterface; use App\Services\ResumeService; use Illuminate\Http\RedirectResponse; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; use Illuminate\View\View; /** * Resume Builder Controller * Handles resume creation, editing, and management with repository pattern */ class ResumeBuilderController extends Controller { /** * Resume repository instance */ protected ResumeRepositoryInterface $resumeRepository; /** * User repository instance */ protected UserRepositoryInterface $userRepository; /** * ResumeService instance */ protected ResumeService $resumeService; /** * Create a new controller instance with dependency injection */ public function __construct( ResumeRepositoryInterface $resumeRepository, UserRepositoryInterface $userRepository, ResumeService $resumeService ) { $this->resumeRepository = $resumeRepository; $this->userRepository = $userRepository; $this->resumeService = $resumeService; $this->middleware('auth'); } /** * Display a listing of user's resumes */ public function index(Request $request): View { $userId = Auth::id(); // Get paginated resumes using repository $resumes = $this->resumeRepository->getPaginatedUserResumes($userId, 12); // Get resume statistics using repository $resumeStats = $this->resumeRepository->getResumeStatistics($userId); return view('resume-builder.index', [ 'title' => 'My Resumes - Resume Builder', 'resumes' => $resumes, 'resumeStats' => $resumeStats ]); } /** * Show the form for creating a new resume */ public function create(): View { $userId = Auth::id(); // Check if user can create more resumes if (!$this->resumeRepository->canUserCreateMoreResumes($userId, 10)) { return redirect()->route('resume-builder.index') ->with('error', 'You have reached the maximum number of resumes allowed.'); } $templates = $this->resumeService->getAvailableTemplates(); return view('resume-builder.create', [ 'title' => 'Create New Resume', 'templates' => $templates ]); } /** * Store a newly created resume */ public function store(StoreResumeRequest $request): RedirectResponse { try { $userId = Auth::id(); // Check if user can create more resumes if (!$this->resumeRepository->canUserCreateMoreResumes($userId, 10)) { return back()->withErrors([ 'title' => 'You have reached the maximum number of resumes allowed.' ])->withInput(); } $resumeData = $request->validated(); $resumeData['user_id'] = $userId; // Create resume using repository $resume = $this->resumeRepository->create($resumeData); // Update completion percentage $this->resumeRepository->updateCompletionPercentage($resume->id); logger()->info('Resume created', [ 'user_id' => $userId, 'resume_id' => $resume->id, 'title' => $resume->title ]); return redirect()->route('resume-builder.edit', $resume) ->with('success', 'Resume created successfully! Start building your professional CV.'); } catch (\Exception $e) { logger()->error('Resume creation error', [ 'error' => $e->getMessage(), 'user_id' => Auth::id() ]); return back()->withErrors([ 'title' => 'Unable to create resume. Please try again.' ])->withInput(); } } /** * Show the form for editing the specified resume */ public function edit(Resume $resume): View { $this->authorize('update', $resume); return view('resume-builder.edit', [ 'title' => 'Edit Resume - ' . $resume->title, 'resume' => $resume, 'templates' => $this->resumeService->getAvailableTemplates() ]); } /** * Update the specified resume */ public function update(UpdateResumeRequest $request, Resume $resume): RedirectResponse { $this->authorize('update', $resume); try { $resumeData = $request->validated(); // Update resume using repository $this->resumeRepository->update($resume->id, $resumeData); // Update completion percentage $this->resumeRepository->updateCompletionPercentage($resume->id); logger()->info('Resume updated', [ 'user_id' => Auth::id(), 'resume_id' => $resume->id ]); return back()->with('success', 'Resume updated successfully!'); } catch (\Exception $e) { logger()->error('Resume update error', [ 'error' => $e->getMessage(), 'user_id' => Auth::id(), 'resume_id' => $resume->id ]); return back()->withErrors([ 'title' => 'Unable to update resume. Please try again.' ])->withInput(); } } /** * Display the specified resume preview */ public function preview(Resume $resume): View { $this->authorize('view', $resume); // Increment view count using repository $this->resumeRepository->incrementViewCount($resume->id); return view('resume-builder.preview', [ 'title' => 'Preview - ' . $resume->title, 'resume' => $resume ]); } /** * Remove the specified resume */ public function destroy(Resume $resume): RedirectResponse { $this->authorize('delete', $resume); try { // Delete resume using repository $this->resumeRepository->delete($resume->id); logger()->info('Resume deleted', [ 'user_id' => Auth::id(), 'resume_id' => $resume->id, 'title' => $resume->title ]); return redirect()->route('resume-builder.index') ->with('success', 'Resume deleted successfully.'); } catch (\Exception $e) { logger()->error('Resume deletion error', [ 'error' => $e->getMessage(), 'user_id' => Auth::id(), 'resume_id' => $resume->id ]); return back()->withErrors([ 'error' => 'Unable to delete resume. Please try again.' ]); } } /** * Download resume as PDF */ public function downloadPdf(Resume $resume) { $this->authorize('view', $resume); try { // Increment download count using repository $this->resumeRepository->incrementDownloadCount($resume->id); return $this->resumeService->generatePdf($resume); } catch (\Exception $e) { logger()->error('PDF generation error', [ 'error' => $e->getMessage(), 'user_id' => Auth::id(), 'resume_id' => $resume->id ]); return back()->withErrors([ 'error' => 'Unable to generate PDF. Please try again.' ]); } } /** * Duplicate an existing resume */ public function duplicate(Resume $resume): RedirectResponse { $this->authorize('view', $resume); try { $userId = Auth::id(); // Check if user can create more resumes if (!$this->resumeRepository->canUserCreateMoreResumes($userId, 10)) { return back()->withErrors([ 'error' => 'You have reached the maximum number of resumes allowed.' ]); } $newTitle = $resume->title . ' (Copy)'; $duplicatedResume = $this->resumeRepository->duplicateResume($resume->id, $userId, $newTitle); logger()->info('Resume duplicated', [ 'user_id' => $userId, 'original_resume_id' => $resume->id, 'new_resume_id' => $duplicatedResume->id ]); return redirect()->route('resume-builder.edit', $duplicatedResume) ->with('success', 'Resume duplicated successfully!'); } catch (\Exception $e) { logger()->error('Resume duplication error', [ 'error' => $e->getMessage(), 'user_id' => Auth::id(), 'resume_id' => $resume->id ]); return back()->withErrors([ 'error' => 'Unable to duplicate resume. Please try again.' ]); } } /** * Search user's resumes */ public function search(Request $request): \Illuminate\Http\JsonResponse { $query = $request->input('query', ''); $userId = Auth::id(); if (empty($query)) { return response()->json([]); } $resumes = $this->resumeRepository ->getUserResumes($userId) ->filter(function ($resume) use ($query) { return stripos($resume->title, $query) !== false || stripos($resume->description, $query) !== false; }) ->take(10) ->values(); return response()->json($resumes); } /** * Get resume analytics */ public function analytics(Resume $resume): \Illuminate\Http\JsonResponse { $this->authorize('view', $resume); $analytics = [ 'view_count' => $resume->view_count, 'download_count' => $resume->download_count, 'completion_percentage' => $resume->completion_percentage, 'last_viewed_at' => $resume->last_viewed_at, 'last_downloaded_at' => $resume->last_downloaded_at, 'created_at' => $resume->created_at, 'updated_at' => $resume->updated_at, ]; return response()->json($analytics); } }