357 lines
11 KiB
PHP
357 lines
11 KiB
PHP
<?php
|
|
|
|
/**
|
|
* Resume Builder Controller
|
|
* Professional Resume Builder - Resume Creation and Management
|
|
*
|
|
* @author David Valera Melendez <david@valera-melendez.de>
|
|
* @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);
|
|
}
|
|
}
|