From 05ca4f8ee6fa7e7821ba3dd80ef1b462acd74bfd Mon Sep 17 00:00:00 2001 From: ngtin Date: Fri, 18 Oct 2024 17:15:12 +0700 Subject: [PATCH] =?UTF-8?q?Th=C3=B4ng=20b=C3=A1o=20b=E1=BA=B1ng=20Email,?= =?UTF-8?q?=20Notify?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Api/Candidates/JobSeekersController.php | 176 ++++++++++++++++++ .../Api/Companies/JobsController.php | 90 ++++++++- .../Api/Employer/CandidatesController.php | 60 ++++++ .../Api/Employer/EmployerMailController.php | 56 ++++++ .../Api/Job/JobApplicationController.php | 97 +++++++--- app/Http/Controllers/WorkplacesController.php | 10 - app/Mail/ApplicationApproved.php | 33 +++- app/Mail/ApplicationContacted.php | 45 +++++ app/Mail/ApplicationInterview.php | 43 +++++ app/Mail/ApplicationRejected.php | 29 ++- app/Mail/ApplicationTestRound.php | 46 +++++ app/Mail/EmployerToApplicantMail.php | 47 +++++ app/Mail/JobApplied.php | 13 +- app/Models/Company.php | 3 + app/Models/job_user.php | 3 + app/Notifications/JobApplicationSubmitted.php | 74 ++++++++ ...24_10_16_114826_create_job_users_table.php | 13 +- ...0_17_152006_create_notifications_table.php | 31 +++ .../emails/application_approved.blade.php | 96 +++++++--- .../emails/application_contacted.blade.php | 94 ++++++++++ .../emails/application_interview.blade.php | 76 ++++++++ .../emails/application_rejected.blade.php | 140 ++++++++------ .../emails/application_test_round.blade.php | 50 +++++ .../emails/employer_to_applicant.blade.php | 119 ++++++++++++ resources/views/emails/job_applied.blade.php | 87 +++++++-- routes/api.php | 20 +- 26 files changed, 1372 insertions(+), 179 deletions(-) create mode 100644 app/Http/Controllers/Api/Candidates/JobSeekersController.php create mode 100644 app/Http/Controllers/Api/Employer/EmployerMailController.php delete mode 100644 app/Http/Controllers/WorkplacesController.php create mode 100644 app/Mail/ApplicationContacted.php create mode 100644 app/Mail/ApplicationInterview.php create mode 100644 app/Mail/ApplicationTestRound.php create mode 100644 app/Mail/EmployerToApplicantMail.php create mode 100644 app/Notifications/JobApplicationSubmitted.php create mode 100644 database/migrations/2024_10_17_152006_create_notifications_table.php create mode 100644 resources/views/emails/application_contacted.blade.php create mode 100644 resources/views/emails/application_interview.blade.php create mode 100644 resources/views/emails/application_test_round.blade.php create mode 100644 resources/views/emails/employer_to_applicant.blade.php diff --git a/app/Http/Controllers/Api/Candidates/JobSeekersController.php b/app/Http/Controllers/Api/Candidates/JobSeekersController.php new file mode 100644 index 0000000..9c67680 --- /dev/null +++ b/app/Http/Controllers/Api/Candidates/JobSeekersController.php @@ -0,0 +1,176 @@ +user(); + + // Kiểm tra xem công việc đã được thêm vào danh sách yêu thích của người dùng chưa + if ($user->favorites()->where('job_id', $job->id)->exists()) { + return response()->json(['message' => 'Công việc đã lưu trước đó'], 200); + } else { + // Nếu công việc chưa được thêm vào danh sách yêu thích, thực hiện thêm mới + $user->favorites()->syncWithoutDetaching([$job->id]); + return response()->json([ + 'success' => 'true', + 'message' => 'Đã lưu công việc vào danh sách yêu thích', + 'status_code' => 200 + ], 200); + } + } + + + /** + * Unsave Job from favorite table + */ + public function unsaveJob(Request $request, $id) + { + $job = Job::findOrFail($id); + $user = $request->user(); + $user->favorites()->detach($job->id); + + return response()->json([ + 'success' => 'true', + 'message' => 'Xóa công việc đã lưu thành công', + 'status_code' => 200, + ], 200); + } + + /** + * Get list of saved jobs + */ + public function savedJobs(Request $request) + { + $user = Auth::user(); + $savedJobs = $user->favorites; + + $savedJobsData = $savedJobs->map(function ($job) { + return [ + 'id' => $job->id, + 'title' => $job->title, + 'featured' => ($job->featured == 1) ? 'Tuyển gấp' : 'Không có', + 'is_hot' => ($job->views > 100) ? 'HOT' : 'Không hot', // Kiểm tra lượt xem + + 'company' => $job->company->company_name, + 'logo' => $job->company->logo, + 'salary' => [ + 'salary_from' => $job->salary_from, + 'salary_to' => $job->salary_to + ], + 'city' => $job->city->name, + 'last_date' => \Carbon\Carbon::parse($job->last_date)->format('d-m-Y'), + ]; + }); + + return response()->json([ + 'success' => true, + 'message' => 'Saved jobs', + 'data' => $savedJobsData, + 'status_code' => 200 + ], 200); + } + + public function apply(Request $request, $id) + { + $job = Job::find($id); + if (!$job) { + return response()->json(['message' => 'Công việc không tồn tại.'], 404); + } + + $user = Auth::guard('sanctum')->user(); + if (!$user) { + return response()->json(['message' => 'Unauthorized'], 401); + } + + if ($job->users()->where('users.id', $user->id)->exists()) { + return response()->json([ + 'message' => 'Bạn đã ứng tuyển công việc này rồi.', + 'status_code' => 409 + ], 409); + } + + // Kiểm tra xem người dùng đã chọn CV có sẵn hay upload CV mới + if ($request->has('selected_cv_id')) { + // Người dùng chọn một CV có sẵn + $selectedCv = Objective::find($request->selected_cv_id); + + $cvFileName = $selectedCv->file; + } elseif ($request->hasFile('cv')) { + // Người dùng tải lên CV mới + $cv = $request->file('cv'); + $cvFileName = time() . '_' . $cv->getClientOriginalName(); + $cv->storeAs('public/cv', $cvFileName); + } else { + // Không có CV nào được chọn hoặc tải lên + return response()->json(['message' => 'Vui lòng chọn hoặc tải lên CV.'], 400); + } + + // Lấy thông tin name, phone, email từ request + $name = $request->input('name'); + $phone = $request->input('phone'); + $email = $request->input('email'); + + // Tiếp tục quá trình ứng tuyển + Mail::to($user->email)->send(new JobApplied($job, $user, $cvFileName)); + $job->company->notify(new JobApplicationSubmitted($job, $user, $name, $phone, $email)); + + // Thêm thông tin vào bảng job_user + $job->users()->attach($user->id, [ + 'status' => 'pending', + 'cv' => $cvFileName, + 'name' => $name, // Thêm trường name + 'phone' => $phone, // Thêm trường phone + 'email' => $email // Thêm trường email + ]); + + // Gửi thông báo cho nhà tuyển dụng + // $job->company->notify(new JobApplicationSubmitted($job, $user)); + + return response()->json([ + 'success' => true, + 'message' => 'Ứng tuyển công việc thành công.', + 'status_code' => 200, + ], 200); + } + + public function getUserCvs(Request $request) + { + $user = $request->user(); + + // Lấy danh sách CV từ profile của người dùng + $cvs = $user->profile->objectives; + + // Tạo một mảng để chứa dữ liệu CV + $customData = $cvs->map(function ($cv) { + return [ + 'id' => $cv->id, + 'desired_position' => $cv->desired_position, + 'attached_file' => $cv->file ? 'Hồ sơ đính kèm' : 'Hồ sơ trực tuyến', // Lấy URL tệp đính kèm + ]; + }); + + + return response()->json([ + 'success' => true, + 'data' => $customData, + 'message' => 'Danh sách CV của người dùng đã được lấy thành công.' + ]); + } + + + +} diff --git a/app/Http/Controllers/Api/Companies/JobsController.php b/app/Http/Controllers/Api/Companies/JobsController.php index bd74fc6..736a9e5 100755 --- a/app/Http/Controllers/Api/Companies/JobsController.php +++ b/app/Http/Controllers/Api/Companies/JobsController.php @@ -4,6 +4,7 @@ use App\Http\Controllers\Controller; use App\Mail\JobApplied; +use App\Models\Company; use App\Models\Job; use App\Utillities\Constant; use Auth; @@ -422,8 +423,8 @@ public function showJob(Job $job) 'title' => $relatedJob['job']->title, 'city' => $relatedJob['job']->city->name, 'company' => $relatedJob['job']->company->company_name, - 'created_at' => $relatedJob['job']->created_at->format('Y-m-d'), - 'score' => $relatedJob['score'], // Hiển thị điểm số + 'last_date' =>$relatedJob['job']->last_date, + 'score' => $relatedJob['score'], ]; }); @@ -565,7 +566,7 @@ public function search(Request $request) 'salary_to' => $job->salary_to ], 'city' => $job->city->name, - 'created_at' => \Carbon\Carbon::parse($job->created_at)->format('Y-m-d'), + 'last_date' => \Carbon\Carbon::parse($job->last_date)->format('d-m-Y'), ]; }), 'suggested_jobs' => $suggestedJobs, @@ -642,9 +643,90 @@ private function getSuggestedJobs() 'salary_to' => $job->salary_to ], 'city' => $job->city->name, - 'created_at' => \Carbon\Carbon::parse($job->created_at)->format('Y-m-d'), + 'last_date' => \Carbon\Carbon::parse($job->last_date)->format('d-m-Y'), + ]; + }); + } + + public function getNotifications() + { + $user = auth()->user(); + $companyId = $user->companies->id; + // Tìm công ty dựa trên ID + $company = Company::find($companyId); + if (!$company) { + return response()->json(['message' => 'Công ty không tồn tại.'], 404); + } + + // Lấy tất cả thông báo cho công ty + $notifications = $company->notifications; // Hoặc sử dụng phương thức `notifications` nếu có + + // Nếu không có thông báo nào + if ($notifications->isEmpty()) { + return response()->json(['message' => 'Không có thông báo nào.'], 204); + } + + $customData = $notifications->map(function ($notification) { + return [ + 'job_id' => $notification->data['job_id'], + 'job_title' => $notification->data['job_title'], + 'user_id' => $notification->data['user_id'], + 'user_name' => $notification->data['user_name'], + 'applicant_name' => $notification->data['applicant_name'], + 'applicant_phone' => $notification->data['applicant_phone'], + 'applicant_email' => $notification->data['applicant_email'], + + 'notification_id' => $notification->id, + 'read_at' => $notification->read_at, + 'created_at' => $notification->created_at->format('Y-m-d H:i:s'), ]; }); + + return response()->json([ + 'success' => true, + 'message' => 'Lấy thông báo thành công', + 'data' => $customData, + 'status_code' => 200 + ]); + } + + + public function markAsRead(Request $request) + { + $user = auth()->user(); + $companyId = $user->companies->id; + + // Tìm công ty dựa trên ID + $company = Company::find($companyId); + if (!$company) { + return response()->json(['message' => 'Công ty không tồn tại.'], 404); + } + + // Lấy thông báo cụ thể nếu có ID được gửi lên + $notificationId = $request->input('notification_id'); + + if ($notificationId) { + // Tìm thông báo dựa trên ID + $notification = $company->notifications()->where('id', $notificationId)->first(); + + if (!$notification) { + return response()->json(['message' => 'Thông báo không tồn tại.'], 404); + } + + // Đánh dấu thông báo này là đã đọc + $notification->markAsRead(); + } else { + // Đánh dấu tất cả thông báo là đã đọc nếu không có ID nào được gửi lên + $company->notifications()->whereNull('read_at')->get()->markAsRead(); + } + + return response()->json([ + 'success' => true, + 'message' => 'Đánh dấu thông báo đã đọc thành công', + 'status_code' => 200 + ]); } + + } diff --git a/app/Http/Controllers/Api/Employer/CandidatesController.php b/app/Http/Controllers/Api/Employer/CandidatesController.php index 902e7bc..e33b74b 100644 --- a/app/Http/Controllers/Api/Employer/CandidatesController.php +++ b/app/Http/Controllers/Api/Employer/CandidatesController.php @@ -6,6 +6,7 @@ use App\Models\Candidate; use App\Models\Objective; use Illuminate\Http\Request; +use Illuminate\Support\Facades\Auth; class CandidatesController extends Controller { @@ -173,5 +174,64 @@ public function show(Request $request, $id) ]); } + public function apply(Request $request, $id) + { + $job = Job::find($id); + if (!$job) { + return response()->json(['message' => 'Công việc không tồn tại.'], 404); + } + + $user = Auth::guard('sanctum')->user(); + if (!$user) { + return response()->json(['message' => 'Unauthorized'], 401); + } + + if ($job->users()->where('users.id', $user->id)->exists()) { + return response()->json([ + 'message' => 'Bạn đã ứng tuyển công việc này rồi.', + 'status_code' => 409 + ], 409); + } + + // Kiểm tra xem người dùng đã chọn CV có sẵn hay upload CV mới + if ($request->has('selected_cv_id')) { + // Người dùng chọn một CV có sẵn + $selectedCv = Cv::find($request->selected_cv_id); + if (!$selectedCv || $selectedCv->users_id != $user->id) { + return response()->json(['message' => 'CV không hợp lệ.'], 400); + } + $cvFileName = $selectedCv->file_path; + } elseif ($request->hasFile('cv')) { + // Người dùng tải lên CV mới + $cv = $request->file('cv'); + $cvFileName = time() . '_' . $cv->getClientOriginalName(); + $cv->storeAs('public/cv', $cvFileName); + } else { + // Không có CV nào được chọn hoặc tải lên + return response()->json(['message' => 'Vui lòng chọn hoặc tải lên CV.'], 400); + } + + // Tiếp tục quá trình ứng tuyển + Mail::to($user->email)->send(new JobApplied($job, $user, $cvFileName)); + $job->users()->attach($user->id, ['status' => 'pending', 'cv' => $cvFileName]); + + return response()->json([ + 'success' => true, + 'message' => 'Ứng tuyển công việc thành công.', + 'status_code' => 200, + ], 200); + } + + public function getUserCvs(Request $request) + { + $user = $request->user(); + $cvs = $user->cvs; // Giả sử người dùng có mối quan hệ hasMany với CVs + + return response()->json([ + 'success' => true, + 'cvs' => $cvs + ]); + } + } diff --git a/app/Http/Controllers/Api/Employer/EmployerMailController.php b/app/Http/Controllers/Api/Employer/EmployerMailController.php new file mode 100644 index 0000000..7e8fecf --- /dev/null +++ b/app/Http/Controllers/Api/Employer/EmployerMailController.php @@ -0,0 +1,56 @@ +where('job_id', $jobId) + ->first(); + + // Kiểm tra xem có bản ghi applicant hay không + if (!$job || !$applicant) { + return response()->json(['message' => 'Công việc hoặc ứng viên không tồn tại.'], 404); + } + + // Lấy email từ pivot table + $email = $applicant->email; + + // Check if the email is valid + if (empty($email)) { + return response()->json(['message' => 'Email của ứng viên không tồn tại.'], 400); + } + + // Lấy thông tin user từ bảng User + $user = User::find($userId); + + // Lấy nội dung email từ request + $subject = $request->input('subject'); + $messageContent = $request->input('message'); + + if (!$subject || !$messageContent) { + return response()->json(['message' => 'Vui lòng cung cấp tiêu đề và nội dung email.'], 400); + } + + // Gửi email + Mail::to($email)->send(new EmployerToApplicantMail($job, $user, $subject, $messageContent)); + + return response()->json([ + 'success' => true, + 'message' => 'Email đã được gửi đến ứng viên thành công.', + ], 200); + }} diff --git a/app/Http/Controllers/Api/Job/JobApplicationController.php b/app/Http/Controllers/Api/Job/JobApplicationController.php index b4898c2..6648cb6 100755 --- a/app/Http/Controllers/Api/Job/JobApplicationController.php +++ b/app/Http/Controllers/Api/Job/JobApplicationController.php @@ -3,27 +3,32 @@ namespace App\Http\Controllers\Api\Job; use App\Mail\ApplicationApproved; +use App\Mail\ApplicationContacted; +use App\Mail\ApplicationTestRound; +use App\Mail\ApplicationInterview; + use App\Mail\ApplicationRejected; use App\Models\Job; use App\Models\job_user; use App\Http\Controllers\Controller; use App\Models\User; +use Carbon\Carbon; use Illuminate\Http\Request; use Illuminate\Support\Facades\Auth; use Illuminate\Support\Facades\Mail; use Illuminate\Support\Facades\Storage; -class JobApplicationController extends Controller +class JobApplicationController extends Controller { /** * Display a listing of the resource. */ public function index() { - $user = Auth::user(); // Get the currently authenticated user + $user = Auth::user(); // Lấy người dùng hiện tại - // Check if the user has a company associated with them + // Kiểm tra nếu người dùng không có công ty liên kết if (!$user->companies) { return response()->json([ 'success' => false, @@ -31,27 +36,38 @@ public function index() ], 403); } - // Get the company ID of the authenticated user + // Lấy công ty đầu tiên của người dùng (nếu có nhiều công ty, điều này cần được điều chỉnh) $companyId = $user->companies->id; - // Load only jobs that belong to the company of the authenticated user + // Lấy tất cả các công việc thuộc về công ty của người dùng hiện tại $jobs = Job::with(['applicants' => function ($query) { - $query->withPivot('status', 'cv'); // Include pivot table fields + // Bao gồm các trường trong bảng pivot + $query->withPivot('status', 'cv', 'name', 'phone', 'email', 'created_at'); }])->where('company_id', $companyId)->get(); + + // Chuyển đổi dữ liệu công việc và ứng viên $jobsData = $jobs->map(function ($job) { return [ 'id' => $job->id, 'title' => $job->title, - 'last_date' => $job->last_date, - 'created_at' => $job->created_at->diffForHumans(), 'applicants' => $job->applicants->map(function ($applicant) { + $statusMap = [ + 'pending' => 'Chờ xác nhận', + 'contacted' => 'Đã liên hệ', + 'test_round' => 'Vòng test', + 'interview' => 'Vòng phỏng vấn', + 'hired' => 'Trúng tuyển', + 'not_selected' => 'Không trúng tuyển' + ]; + return [ 'id' => $applicant->id, - 'name' => $applicant->name, - 'email' => $applicant->email, - 'status' => $applicant->pivot->status, + 'name' => $applicant->pivot->name, // Lấy tên từ bảng pivot nếu có + 'email' => $applicant->pivot->email, // Lấy email từ bảng pivot nếu có + 'status' => $statusMap[$applicant->pivot->status] ?? $applicant->pivot->status, // Chuyển đổi trạng thái theo enum 'cv' => $applicant->pivot->cv ? url('storage/cv/' . $applicant->pivot->cv) : null, + 'created_at' => $applicant->pivot->created_at ? Carbon::parse($applicant->pivot->created_at)->format('Y-m-d H:i:s') : null, // Định dạng created_at ]; }), ]; @@ -59,7 +75,7 @@ public function index() return response()->json([ 'success' => true, - 'message' => 'success', + 'message' => 'Lấy dữ liệu thành công', 'data' => $jobsData, 'status_code' => 200 ]); @@ -125,7 +141,7 @@ public function show($jobId) 'name' => $applicant->name, 'email' => $applicant->email, 'status' => $applicant->pivot->status, - 'cv' => $applicant->pivot->cv ? asset('path/to/cv/' . $applicant->pivot->cv) : null, + 'cv' => $applicant->pivot->cv ? asset('app/public/to/cv/' . $applicant->pivot->cv) : null, ]; }), ]; @@ -165,7 +181,10 @@ public function destroy(job_user $job_user) public function processApplication(Request $request, $jobId, $userId) { - $job = Job::find($jobId); + $job = Job::with(['applicants' => function ($query) use ($userId) { + $query->where('users.id', $userId)->withPivot('status', 'cv', 'name', 'email'); // Include necessary pivot fields + }])->find($jobId); + if (!$job) { return response()->json([ 'success' => false, @@ -182,17 +201,21 @@ public function processApplication(Request $request, $jobId, $userId) 'status_code' => 403, ], 403); } - $user = User::find($userId); - if (!$user) { + + // Retrieve the applicant and their pivot data + $applicant = $job->applicants->first(); + $email = $applicant->pivot->email; + $name = $applicant->pivot->name; + if (!$applicant) { return response()->json([ 'success' => false, - 'message' => 'Người dùng không tồn tại.', + 'message' => 'Người dùng không tồn tại hoặc không ứng tuyển vào công việc này.', 'status_code' => 404 ], 404); } $status = $request->input('status'); - if (!in_array($status, ['approved', 'rejected'])) { + if (!in_array($status, ['pending', 'contacted', 'test_round', 'interview', 'hired', 'not_selected'])) { return response()->json([ 'success' => false, 'message' => 'Trạng thái không hợp lệ.', @@ -201,36 +224,52 @@ public function processApplication(Request $request, $jobId, $userId) ], 400); } - // Update the status of the application - $job->users()->updateExistingPivot($user->id, ['status' => $status]); + // Update the status of the application in the pivot table + $job->users()->updateExistingPivot($userId, ['status' => $status]); // Send notification email to the applicant - if ($status === 'approved') { - Mail::to($user->email)->send(new ApplicationApproved($job, $user)); - } elseif ($status === 'rejected') { - Mail::to($user->email)->send(new ApplicationRejected($job, $user)); + if ($status === 'hired') { + Mail::to($email)->send(new ApplicationApproved($job, $name, $email)); + } elseif ($status === 'not_selected') { + Mail::to($applicant->pivot->email)->send(new ApplicationRejected($job, $applicant->pivot->name)); + } elseif ($status === 'contacted') { + Mail::to($applicant->pivot->email)->send(new ApplicationContacted($job, $applicant->pivot->name)); + } elseif ($status === 'test_round') { + Mail::to($applicant->pivot->email)->send(new ApplicationTestRound($job, $applicant->pivot->name)); + } elseif ($status === 'interview') { + Mail::to($applicant->pivot->email)->send(new ApplicationInterview($job, $applicant->pivot->name)); } // Fetch updated job data $job = Job::with(['applicants' => function ($query) { - $query->withPivot('status', 'cv'); // Include pivot table fields + $query->withPivot('status', 'cv', 'name', 'email'); // Include pivot table fields }])->find($jobId); + $statusMap = [ + 'pending' => 'Chờ xác nhận', + 'contacted' => 'Đã liên hệ', + 'test_round' => 'Vòng test', + 'interview' => 'Vòng phỏng vấn', + 'hired' => 'Trúng tuyển', + 'not_selected' => 'Không trúng tuyển' + ]; + $jobData = [ 'id' => $job->id, 'title' => $job->title, 'created_at' => $job->created_at->diffForHumans(), - 'applicants' => $job->applicants->map(function ($applicant) { + 'applicants' => $job->applicants->map(function ($applicant) use ($statusMap) { return [ 'id' => $applicant->id, - 'name' => $applicant->name, - 'email' => $applicant->email, - 'status' => $applicant->pivot->status, + 'name' => $applicant->pivot->name, + 'email' => $applicant->pivot->email, + 'status' => $statusMap[$applicant->pivot->status] ?? 'Chưa xác định', // Thêm trạng thái đã được dịch 'cv' => $applicant->pivot->cv ? asset('path/to/cv/' . $applicant->pivot->cv) : null, ]; }), ]; + return response()->json([ 'success' => true, 'message' => 'Xử lí đơn ứng tuyển thành công.', diff --git a/app/Http/Controllers/WorkplacesController.php b/app/Http/Controllers/WorkplacesController.php deleted file mode 100644 index 12f5917..0000000 --- a/app/Http/Controllers/WorkplacesController.php +++ /dev/null @@ -1,10 +0,0 @@ -job = $job; + $this->name = $name; + $this->email = $email; + } /** @@ -25,8 +38,16 @@ public function __construct() */ public function build() { - return $this->subject('Application Approved') - ->view('emails.application_approved'); - // Nếu bạn có dữ liệu để truyền vào view, sử dụng ->with(['key' => $value]) + // Lấy tên công ty từ công việc liên quan + $companyName = $this->job->company->company_name ?? 'Company'; // Nếu không có, sử dụng giá trị mặc định 'Company' + + return $this->from('ngtin590@gmail.com', $companyName) + ->subject('Cập nhật trạng thái công việc - ' . $this->job->title) + ->view('emails.application_approved') + ->with([ + 'jobTitle' => $this->job->title, + 'applicantName' => $this->name, // Lấy tên từ bảng pivot + 'applicantEmail' => $this->email, // Lấy email từ bảng pivot + ]); } } diff --git a/app/Mail/ApplicationContacted.php b/app/Mail/ApplicationContacted.php new file mode 100644 index 0000000..7087dfc --- /dev/null +++ b/app/Mail/ApplicationContacted.php @@ -0,0 +1,45 @@ +job = $job; + $this->name = $name; + } + + /** + * Get the message envelope. + */ + + + public function build() + { + // Lấy tên công ty từ công việc liên quan + $companyName = $this->job->company->company_name ?? 'Company'; // Nếu không có, sử dụng giá trị mặc định 'Company' + + return $this->from('ngtin590@gmail.com', $companyName) + ->subject('Cập Nhật Trạng Thái Đơn Xin Việc') + ->view('emails.application_contacted') + ->with([ + 'jobTitle' => $this->job->title, + 'applicantName' => $this->name, // Lấy tên từ bảng pivot + 'applicantEmail' => $this->email, // Lấy email từ bảng pivot + ]); + } +} + diff --git a/app/Mail/ApplicationInterview.php b/app/Mail/ApplicationInterview.php new file mode 100644 index 0000000..df7dbe3 --- /dev/null +++ b/app/Mail/ApplicationInterview.php @@ -0,0 +1,43 @@ +job = $job; + $this->name = $name; + } + + /** + * Get the message envelope. + */ + + public function build() + { + // Lấy tên công ty từ công việc liên quan + $companyName = $this->job->company->company_name ?? 'Company'; // Nếu không có, sử dụng giá trị mặc định 'Company' + + return $this->from('ngtin590@gmail.com', $companyName) + ->subject('Cập Nhật Trạng Thái Đơn Xin Việc') + ->view('mails.application_interview') + ->with([ + 'jobTitle' => $this->job->title, + 'applicantName' => $this->name, // Lấy tên từ bảng pivot + 'applicantEmail' => $this->email, // Lấy email từ bảng pivot + ]); + } +} diff --git a/app/Mail/ApplicationRejected.php b/app/Mail/ApplicationRejected.php index 938d2b8..2561f7e 100755 --- a/app/Mail/ApplicationRejected.php +++ b/app/Mail/ApplicationRejected.php @@ -9,13 +9,19 @@ class ApplicationRejected extends Mailable { use Queueable, SerializesModels; - + public $job; + public $name; + public $email; /** * Create a new message instance. */ - public function __construct() + public function __construct($job, $name,$email) { - // Bạn có thể truyền dữ liệu vào constructor nếu cần + // Truyền dữ liệu công việc và ứng viên vào mail class + $this->job = $job; + $this->name = $name; + $this->email = $email; + } /** @@ -23,10 +29,21 @@ public function __construct() * * @return $this */ + + + public function build() { - return $this->subject('Application Rejected') - ->view('emails.application_rejected'); - // Nếu bạn có dữ liệu để truyền vào view, sử dụng ->with(['key' => $value]) + // Lấy tên công ty từ công việc liên quan + $companyName = $this->job->company->company_name ?? 'Company'; // Nếu không có, sử dụng giá trị mặc định 'Company' + + return $this->from('ngtin590@gmail.com', $companyName) + ->subject('Cập nhật trạng thái xin việc - ' . $this->job->title) + ->view('emails.application_rejected') + ->with([ + 'jobTitle' => $this->job->title, + 'applicantName' => $this->name, // Lấy tên từ bảng pivot + 'applicantEmail' => $this->email, // Lấy email từ bảng pivot + ]); } } diff --git a/app/Mail/ApplicationTestRound.php b/app/Mail/ApplicationTestRound.php new file mode 100644 index 0000000..73aad9c --- /dev/null +++ b/app/Mail/ApplicationTestRound.php @@ -0,0 +1,46 @@ +job = $job; + $this->name = $name; + } + + /** + * Get the message envelope. + */ + + public function build() + { + // Lấy tên công ty từ công việc liên quan + $companyName = $this->job->company->company_name ?? 'Company'; // Nếu không có, sử dụng giá trị mặc định 'Company' + + return $this->from('ngtin590@gmail.com', $companyName) + ->subject('Cập Nhật Trạng Thái Đơn Xin Việc') + ->view('emails.application_test_round') + ->with([ + 'jobTitle' => $this->job->title, + 'applicantName' => $this->name, // Lấy tên từ bảng pivot + 'applicantEmail' => $this->email, // Lấy email từ bảng pivot + ]); + } + + +} diff --git a/app/Mail/EmployerToApplicantMail.php b/app/Mail/EmployerToApplicantMail.php new file mode 100644 index 0000000..edb2523 --- /dev/null +++ b/app/Mail/EmployerToApplicantMail.php @@ -0,0 +1,47 @@ +job = $job; + $this->user = $user; + $this->subject = $subject; + $this->messageContent = $messageContent; + } + + /** + * Build the message. + */ + public function build() + { + return $this->from('ngtin590@gmail.com', $this->job->company->company_name) // Correct reference to $this->job + ->view('emails.employer_to_applicant') // View này sẽ chứa nội dung của email + ->with([ + 'jobTitle' => $this->job->title, + 'userName' => $this->user->name, + 'messageContent' => $this->messageContent, + ]); + } + +} + + diff --git a/app/Mail/JobApplied.php b/app/Mail/JobApplied.php index 03ed0b3..9498842 100755 --- a/app/Mail/JobApplied.php +++ b/app/Mail/JobApplied.php @@ -29,17 +29,16 @@ public function __construct(Job $job) public function build() { $job = $this->job; - $company = $job->company; -// $jobtype = $job->jobtype->first()->name; - return $this->from('ngtin590@gmail.com', $company->name) + $company = $job->Company; + return $this->from('ngtin590@gmail.com', $company->company_name) ->subject('Xác nhận tuyển dụng') ->view('emails.job_applied') ->with([ 'jobTitle' => $job->title, - 'companyName' => $company->name, - 'address' => $job->address, - 'salary' => $job->salary, -// 'jobtype' => $jobtype, + 'companyName' => $job->Company->company_name, + 'address' => $job->work_address, + 'salary_from' => $job->salary_from, + 'salary_to' => $job->salary_to, 'userName' => Auth::user()->name, ]); } diff --git a/app/Models/Company.php b/app/Models/Company.php index 94e431d..1e3fbc8 100755 --- a/app/Models/Company.php +++ b/app/Models/Company.php @@ -6,10 +6,13 @@ use App\Http\Controllers\Api\Admin\CompanytypesController; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\Model; +use Illuminate\Notifications\Notifiable; class Company extends Model { use HasFactory; + use Notifiable; + protected $table = 'companies'; diff --git a/app/Models/job_user.php b/app/Models/job_user.php index 8f50a5e..2268291 100755 --- a/app/Models/job_user.php +++ b/app/Models/job_user.php @@ -9,4 +9,7 @@ class job_user extends Model { use HasFactory; + protected $table = 'job_user'; + protected $primaryKey = 'id'; + protected $guarded = []; } diff --git a/app/Notifications/JobApplicationSubmitted.php b/app/Notifications/JobApplicationSubmitted.php new file mode 100644 index 0000000..c6236b2 --- /dev/null +++ b/app/Notifications/JobApplicationSubmitted.php @@ -0,0 +1,74 @@ +job = $job; + $this->user = $user; + $this->name = $name; + $this->phone = $phone; + $this->email = $email; + } + + /** + * Get the notification's delivery channels. + * + * @return array + */ + public function via($notifiable) + { + return ['database', 'mail']; // Có thể thêm 'mail' nếu bạn muốn gửi qua email + } + + public function toArray($notifiable) + { + return [ + 'job_id' => $this->job->id, + 'job_title' => $this->job->title, + 'user_id' => $this->user->id, + 'user_name' => $this->user->name, + 'applicant_name' => $this->name, // Thông tin name + 'applicant_phone' => $this->phone, // Thông tin phone + 'applicant_email' => $this->email // Thông tin email + ]; + } + + public function toMail($notifiable) + { + return (new MailMessage) + ->subject('Thông báo ứng tuyển mới') + ->line('Ứng viên ' . $this->name . ' (' . $this->email . ', ' . $this->phone . ') đã ứng tuyển vào công việc ' . $this->job->title) + ->action('Xem chi tiết', url('/jobs/' . $this->job->id)) + ->line('Cảm ơn bạn đã sử dụng dịch vụ của chúng tôi!'); + } + + /** + * Get the mail representation of the notification. + */ + + + /** + * Get the array representation of the notification. + * + * @return array + */ + +} diff --git a/database/migrations/2024_10_16_114826_create_job_users_table.php b/database/migrations/2024_10_16_114826_create_job_users_table.php index 506f625..4b7e9cd 100755 --- a/database/migrations/2024_10_16_114826_create_job_users_table.php +++ b/database/migrations/2024_10_16_114826_create_job_users_table.php @@ -15,8 +15,17 @@ public function up(): void $table->id(); $table->foreignId('user_id')->constrained()->cascadeOnDelete(); $table->foreignId('job_id')->constrained()->cascadeOnDelete(); - $table->enum('status', ['pending', 'approved', 'rejected'])->default('pending'); - $table->timestamps(); + $table->string("name"); + $table->string("email"); + $table->string("phone"); + $table->enum('status', [ + 'pending', // Đang chờ + 'contacted', // Đã liên hệ + 'test_round', // Vòng test + 'interview', // Vòng phỏng vấn + 'hired', // Trúng tuyển + 'not_selected' // Không đúng tuyển + ])->default('pending'); $table->timestamps(); }); } diff --git a/database/migrations/2024_10_17_152006_create_notifications_table.php b/database/migrations/2024_10_17_152006_create_notifications_table.php new file mode 100644 index 0000000..d738032 --- /dev/null +++ b/database/migrations/2024_10_17_152006_create_notifications_table.php @@ -0,0 +1,31 @@ +uuid('id')->primary(); + $table->string('type'); + $table->morphs('notifiable'); + $table->text('data'); + $table->timestamp('read_at')->nullable(); + $table->timestamps(); + }); + } + + /** + * Reverse the migrations. + */ + public function down(): void + { + Schema::dropIfExists('notifications'); + } +}; diff --git a/resources/views/emails/application_approved.blade.php b/resources/views/emails/application_approved.blade.php index d95c3b0..4e78b99 100755 --- a/resources/views/emails/application_approved.blade.php +++ b/resources/views/emails/application_approved.blade.php @@ -3,34 +3,74 @@ - Đơn Ứng Tuyển Đã Được Chấp Nhận + Cập nhật thông tin trúng tuyển + - - - - - -
- - - - - - - - - - -
-

Đơn Ứng Tuyển Đã Được Chấp Nhận!

-
-

Xin chúc mừng!

-

Đơn ứng tuyển của bạn đã được chấp nhận. Chúng tôi rất hào hứng chào đón bạn vào đội ngũ của chúng tôi.

-

Hãy chuẩn bị cho một hành trình tuyệt vời phía trước!

- Xem Chi Tiết -
-

© 2024 Công Ty Của Bạn. Tất cả các quyền được bảo lưu.

-
-
+ +
+

Chúc mừng, {{ $applicantName }}!

+

Chúng tôi xin thông báo rằng đơn ứng tuyển của bạn cho vị trí {{ $jobTitle }} đã được phê duyệt!

+

Bạn sẽ nhận được thông tin chi tiết và các bước tiếp theo từ nhà tuyển dụng trong thời gian sớm nhất.

+

Chúng tôi rất mong chờ việc bạn sẽ gia nhập đội ngũ của chúng tôi!

+

Trân trọng,

+

Đội ngũ quản lý công việc

+
diff --git a/resources/views/emails/application_contacted.blade.php b/resources/views/emails/application_contacted.blade.php new file mode 100644 index 0000000..87f3823 --- /dev/null +++ b/resources/views/emails/application_contacted.blade.php @@ -0,0 +1,94 @@ + + + + + + Bạn sẽ sớm được liên hệ + + + +
+

Chào {{ $applicantName }},

+

Cảm ơn bạn đã nộp đơn ứng tuyển cho vị trí {{ $jobTitle }} tại {{ $companyName }}.

+ +
+

Chúng tôi muốn thông báo rằng hồ sơ của bạn đang được xem xét và bạn sẽ sớm nhận được thông tin từ chúng tôi về các bước tiếp theo trong quá trình ứng tuyển.

+
+ +

Vui lòng kiểm tra hộp thư của bạn thường xuyên để không bỏ lỡ thông tin quan trọng từ chúng tôi.

+ +

Nếu bạn có bất kỳ câu hỏi nào, đừng ngần ngại liên hệ với chúng tôi qua email này.

+ +

Cảm ơn bạn đã quan tâm đến cơ hội làm việc với chúng tôi!

+ + Xem Trạng Thái Ứng Tuyển + +

Trân trọng,
{{ $companyName }}

+
+ + + diff --git a/resources/views/emails/application_interview.blade.php b/resources/views/emails/application_interview.blade.php new file mode 100644 index 0000000..5054163 --- /dev/null +++ b/resources/views/emails/application_interview.blade.php @@ -0,0 +1,76 @@ + + + + + + Cập Nhật Trạng Thái Đơn Xin Việc + + + +
+

Cập Nhật Trạng Thái Đơn Xin Việc

+

Chào {{ $applicantName }},

+ +

Chúng tôi xin thông báo rằng hồ sơ của bạn đã được xem xét và bạn đã được mời tham gia phỏng vấn cho vị trí {{ $jobTitle }} tại {{ $companyName }}.

+ +
+

Chúng tôi sẽ sớm liên hệ với bạn để cung cấp thông tin chi tiết về thời gian, địa điểm và hình thức phỏng vấn.

+
+ +

Chúng tôi rất mong chờ được gặp bạn và chúc bạn thành công trong buổi phỏng vấn!

+

Trân trọng,
{{ $companyName }}

+
+ + + diff --git a/resources/views/emails/application_rejected.blade.php b/resources/views/emails/application_rejected.blade.php index e2ce91d..31c84d8 100755 --- a/resources/views/emails/application_rejected.blade.php +++ b/resources/views/emails/application_rejected.blade.php @@ -3,68 +3,88 @@ - Đơn Ứng Tuyển Bị Từ Chối - + Thông Báo Từ Chối Đơn Xin Việc - - - - - -
- - - - - - - - - - -
- Company Logo -

Thông Báo Về Đơn Ứng Tuyển

-
-

Kính gửi ứng viên,

-

Chúng tôi rất tiếc phải thông báo rằng đơn ứng tuyển của bạn đã không được chấp nhận cho vị trí hiện tại. Chúng tôi đánh giá cao sự quan tâm và nỗ lực của bạn trong quá trình ứng tuyển.

-

Mặc dù kết quả này có thể không như mong đợi, chúng tôi khuyến khích bạn tiếp tục phát triển kỹ năng và theo đuổi đam mê của mình. Sự nghiệp là một hành trình dài, và chúng tôi tin rằng bạn sẽ tìm được cơ hội phù hợp trong tương lai.

-

Chúng tôi mời bạn tiếp tục theo dõi các cơ hội mới tại công ty của chúng tôi và không ngần ngại ứng tuyển cho các vị trí phù hợp khác trong tương lai.

- - - - -
- Xem Cơ Hội Khác -
-
-

Kết nối với chúng tôi

- - - - - - -
- Facebook - - Twitter - - LinkedIn -
-

© 2024 Công Ty Của Bạn. Tất cả các quyền được bảo lưu.

-
-
+ +
+

Chào {{ $applicantName }},

+
+

Chúng tôi rất tiếc phải thông báo rằng đơn xin việc của bạn cho vị trí {{ $jobTitle }} tại {{ $companyName }} đã không được chấp nhận.

+
+ +

Chúng tôi rất trân trọng thời gian và công sức bạn đã dành cho việc ứng tuyển này. Mặc dù bạn không được chọn cho vị trí này, chúng tôi khuyến khích bạn tiếp tục theo dõi các cơ hội việc làm khác tại công ty của chúng tôi trong tương lai.

+ +

Cảm ơn bạn đã quan tâm đến cơ hội làm việc với chúng tôi!

+ + Xem Cơ Hội Khác + +

Trân trọng,
{{ $companyName }}

+
+ diff --git a/resources/views/emails/application_test_round.blade.php b/resources/views/emails/application_test_round.blade.php new file mode 100644 index 0000000..a958a69 --- /dev/null +++ b/resources/views/emails/application_test_round.blade.php @@ -0,0 +1,50 @@ + + + + + + Các Bước Tiếp Theo Trong Đơn Xin Việc + + + +
+

Chào {{ $applicantName }},

+

Chúng tôi xin thông báo rằng bạn đã vượt qua vòng đầu tiên trong quá trình ứng tuyển cho vị trí {{ $jobTitle }}.

+

Các bước tiếp theo sẽ như sau:

+
    +
  1. Chúng tôi sẽ liên hệ với bạn để sắp xếp thời làm bài test.
  2. +
  3. Trong khi chờ đợi, bạn có thể chuẩn bị cho các câu hỏi cho bài test.
  4. +
  5. Hãy kiểm tra hộp thư đến của bạn thường xuyên để không bỏ lỡ thông tin từ chúng tôi.
  6. +
+

Cảm ơn bạn đã quan tâm đến cơ hội làm việc với chúng tôi!

+

Trân trọng,
{{ $companyName }}

+
+ + + diff --git a/resources/views/emails/employer_to_applicant.blade.php b/resources/views/emails/employer_to_applicant.blade.php new file mode 100644 index 0000000..c05d055 --- /dev/null +++ b/resources/views/emails/employer_to_applicant.blade.php @@ -0,0 +1,119 @@ + + + + + + Thư từ nhà tuyển dụng + + + + + + + + + + + Thư từ nhà tuyển dụng + + + + + + diff --git a/resources/views/emails/job_applied.blade.php b/resources/views/emails/job_applied.blade.php index 2ac30f5..211f353 100755 --- a/resources/views/emails/job_applied.blade.php +++ b/resources/views/emails/job_applied.blade.php @@ -3,64 +3,111 @@ - Application Confirmation + Xác nhận ứng tuyển

Xác nhận ứng tuyển

Chào {{ $userName }},

-

Bạn đã ứng tuyển công việc {{ $jobTitle }} thành công!

-

Thông tin công việc:

+

Chúc mừng! Bạn đã ứng tuyển thành công vào vị trí {{ $jobTitle }}.

+

Thông tin chi tiết về công việc:

  • Công ty: {{ $companyName }}
  • Địa chỉ: {{ $address }}
  • -
  • Mức lương: {{ $salary }}
  • +
  • Mức lương: {{ $salary_from }} - {{ $salary_to }}
-

Chúng tôi sẽ liên hệ với bạn sớm.

+

Chúng tôi đánh giá cao sự quan tâm của bạn và sẽ liên hệ với bạn trong thời gian sớm nhất để thông báo về các bước tiếp theo của quá trình tuyển dụng.

+
+ Xem chi tiết ứng tuyển +
diff --git a/routes/api.php b/routes/api.php index 0af9252..59becf9 100755 --- a/routes/api.php +++ b/routes/api.php @@ -1,6 +1,8 @@ group(function () { - Route::post('/{id}/apply', [JobsController::class, 'apply']); + Route::post('/{id}/apply', [JobSeekersController::class, 'apply']); + Route::get('/user/cvs', [JobSeekersController::class, 'getUserCvs']); + Route::get('/applied', [JobsController::class, 'applicant']); - Route::post('/favorites/{id}/save', [JobsController::class, 'saveJob']); - Route::post('/favorites/{id}/unsave', [JobsController::class, 'unsaveJob']); - Route::get('/favorites/saved', [JobsController::class, 'savedJobs']); + Route::post('/favorites/{id}/save', [JobSeekersController::class, 'saveJob']); + Route::post('/favorites/{id}/un-save', [JobSeekersController::class, 'unsaveJob']); + Route::get('/favorites/saved', [JobSeekersController::class, 'savedJobs']); Route::get('/suggest', [JobsController::class, 'suggestJobs']); }); Route::middleware(CheckUserRole::class)->group(function () { Route::resource('employer/jobs', JobsController::class); + Route::get('employer/companies/notifications', [JobsController::class, 'getNotifications']); + Route::post('employer/companies/notifications/read', [JobsController::class, 'markAsRead']); + + Route::post('/jobs/{jobId}/applicants/{userId}/send-email', [EmployerMailController::class, 'sendEmailToApplicant']); - Route::post('/process_application/{jobId}/{userId}', [JobApplicationController::class, 'processApplication']); + Route::post('/process-application/{jobId}/{userId}', [JobApplicationController::class, 'processApplication']); Route::get('/applications', [JobApplicationController::class, 'index']); Route::post('/{id}/toggle', [JobApplicationController::class, 'toggle']); Route::get('/statistics', [JobApplicationController::class, 'getStatistics']);