feat: link quizzes to job positions and filter candidate dashboard accordingly
This commit is contained in:
@@ -13,8 +13,9 @@ class JobPositionController extends Controller
|
|||||||
$this->authorizeAdmin();
|
$this->authorizeAdmin();
|
||||||
|
|
||||||
return Inertia::render('Admin/JobPositions/Index', [
|
return Inertia::render('Admin/JobPositions/Index', [
|
||||||
'jobPositions' => JobPosition::with('tenant')->get(),
|
'jobPositions' => JobPosition::with(['tenant', 'quizzes'])->get(),
|
||||||
'tenants' => \App\Models\Tenant::orderBy('name')->get()
|
'tenants' => \App\Models\Tenant::orderBy('name')->get(),
|
||||||
|
'quizzes' => \App\Models\Quiz::all()
|
||||||
]);
|
]);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -28,9 +29,11 @@ class JobPositionController extends Controller
|
|||||||
'requirements' => 'nullable|array',
|
'requirements' => 'nullable|array',
|
||||||
'ai_prompt' => 'nullable|string',
|
'ai_prompt' => 'nullable|string',
|
||||||
'tenant_id' => 'nullable|exists:tenants,id',
|
'tenant_id' => 'nullable|exists:tenants,id',
|
||||||
|
'quiz_ids' => 'nullable|array',
|
||||||
|
'quiz_ids.*' => 'exists:quizzes,id',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
JobPosition::create([
|
$jobPosition = JobPosition::create([
|
||||||
'title' => $request->title,
|
'title' => $request->title,
|
||||||
'description' => $request->description,
|
'description' => $request->description,
|
||||||
'requirements' => $request->requirements,
|
'requirements' => $request->requirements,
|
||||||
@@ -38,6 +41,10 @@ class JobPositionController extends Controller
|
|||||||
'tenant_id' => auth()->user()->isSuperAdmin() ? $request->tenant_id : auth()->user()->tenant_id,
|
'tenant_id' => auth()->user()->isSuperAdmin() ? $request->tenant_id : auth()->user()->tenant_id,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
if ($request->has('quiz_ids')) {
|
||||||
|
$jobPosition->quizzes()->sync($request->quiz_ids);
|
||||||
|
}
|
||||||
|
|
||||||
return back()->with('success', 'Fiche de poste créée avec succès.');
|
return back()->with('success', 'Fiche de poste créée avec succès.');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,6 +58,8 @@ class JobPositionController extends Controller
|
|||||||
'requirements' => 'nullable|array',
|
'requirements' => 'nullable|array',
|
||||||
'ai_prompt' => 'nullable|string',
|
'ai_prompt' => 'nullable|string',
|
||||||
'tenant_id' => 'nullable|exists:tenants,id',
|
'tenant_id' => 'nullable|exists:tenants,id',
|
||||||
|
'quiz_ids' => 'nullable|array',
|
||||||
|
'quiz_ids.*' => 'exists:quizzes,id',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$jobPosition->update([
|
$jobPosition->update([
|
||||||
@@ -61,6 +70,10 @@ class JobPositionController extends Controller
|
|||||||
'tenant_id' => auth()->user()->isSuperAdmin() ? $request->tenant_id : auth()->user()->tenant_id,
|
'tenant_id' => auth()->user()->isSuperAdmin() ? $request->tenant_id : auth()->user()->tenant_id,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
if ($request->has('quiz_ids')) {
|
||||||
|
$jobPosition->quizzes()->sync($request->quiz_ids);
|
||||||
|
}
|
||||||
|
|
||||||
return back()->with('success', 'Fiche de poste mise à jour.');
|
return back()->with('success', 'Fiche de poste mise à jour.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,4 +22,9 @@ class JobPosition extends Model
|
|||||||
{
|
{
|
||||||
return $this->hasMany(Candidate::class);
|
return $this->hasMany(Candidate::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function quizzes()
|
||||||
|
{
|
||||||
|
return $this->belongsToMany(Quiz::class);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,4 +19,9 @@ class Quiz extends Model
|
|||||||
{
|
{
|
||||||
return $this->hasMany(Question::class);
|
return $this->hasMany(Question::class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function jobPositions()
|
||||||
|
{
|
||||||
|
return $this->belongsToMany(JobPosition::class);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
use Illuminate\Database\Migrations\Migration;
|
||||||
|
use Illuminate\Database\Schema\Blueprint;
|
||||||
|
use Illuminate\Support\Facades\Schema;
|
||||||
|
|
||||||
|
return new class extends Migration
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Run the migrations.
|
||||||
|
*/
|
||||||
|
public function up(): void
|
||||||
|
{
|
||||||
|
Schema::create('job_position_quiz', function (Blueprint $table) {
|
||||||
|
$table->id();
|
||||||
|
$table->foreignId('job_position_id')->constrained()->cascadeOnDelete();
|
||||||
|
$table->foreignId('quiz_id')->constrained()->cascadeOnDelete();
|
||||||
|
$table->timestamps();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reverse the migrations.
|
||||||
|
*/
|
||||||
|
public function down(): void
|
||||||
|
{
|
||||||
|
Schema::dropIfExists('job_position_quiz');
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -10,7 +10,8 @@ import InputError from '@/Components/InputError.vue';
|
|||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
jobPositions: Array,
|
jobPositions: Array,
|
||||||
tenants: Array
|
tenants: Array,
|
||||||
|
quizzes: Array
|
||||||
});
|
});
|
||||||
|
|
||||||
const showingModal = ref(false);
|
const showingModal = ref(false);
|
||||||
@@ -22,6 +23,7 @@ const form = useForm({
|
|||||||
requirements: [],
|
requirements: [],
|
||||||
ai_prompt: '',
|
ai_prompt: '',
|
||||||
tenant_id: '',
|
tenant_id: '',
|
||||||
|
quiz_ids: [],
|
||||||
});
|
});
|
||||||
|
|
||||||
const openModal = (position = null) => {
|
const openModal = (position = null) => {
|
||||||
@@ -32,6 +34,7 @@ const openModal = (position = null) => {
|
|||||||
form.requirements = position.requirements || [];
|
form.requirements = position.requirements || [];
|
||||||
form.ai_prompt = position.ai_prompt || '';
|
form.ai_prompt = position.ai_prompt || '';
|
||||||
form.tenant_id = position.tenant_id || '';
|
form.tenant_id = position.tenant_id || '';
|
||||||
|
form.quiz_ids = position.quizzes ? position.quizzes.map(q => q.id) : [];
|
||||||
} else {
|
} else {
|
||||||
form.reset();
|
form.reset();
|
||||||
}
|
}
|
||||||
@@ -203,6 +206,32 @@ const removeRequirement = (index) => {
|
|||||||
<InputError :message="form.errors.ai_prompt" />
|
<InputError :message="form.errors.ai_prompt" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div v-if="quizzes && quizzes.length > 0">
|
||||||
|
<label class="block text-xs font-black uppercase tracking-widest text-slate-400 mb-4">Tests techniques associés</label>
|
||||||
|
<div class="grid grid-cols-1 sm:grid-cols-2 gap-3">
|
||||||
|
<div
|
||||||
|
v-for="quiz in quizzes"
|
||||||
|
:key="quiz.id"
|
||||||
|
class="flex items-center p-3 bg-slate-50 dark:bg-slate-900 rounded-2xl cursor-pointer hover:bg-slate-100 dark:hover:bg-slate-800 transition-colors"
|
||||||
|
@click="form.quiz_ids.includes(quiz.id) ? form.quiz_ids = form.quiz_ids.filter(id => id !== quiz.id) : form.quiz_ids.push(quiz.id)"
|
||||||
|
>
|
||||||
|
<div
|
||||||
|
class="w-5 h-5 rounded-md border-2 mr-3 flex items-center justify-center transition-all"
|
||||||
|
:class="form.quiz_ids.includes(quiz.id) ? 'bg-indigo-600 border-indigo-600' : 'border-slate-300 dark:border-slate-600'"
|
||||||
|
>
|
||||||
|
<svg v-if="form.quiz_ids.includes(quiz.id)" xmlns="http://www.w3.org/2000/svg" class="h-3 w-3 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||||
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="3" d="M5 13l4 4L19 7" />
|
||||||
|
</svg>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="text-xs font-bold leading-tight">{{ quiz.title }}</div>
|
||||||
|
<div class="text-[9px] text-slate-400 uppercase tracking-tighter">{{ quiz.duration_minutes }} min</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<InputError :message="form.errors.quiz_ids" />
|
||||||
|
</div>
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<div class="flex justify-between items-center mb-4">
|
<div class="flex justify-between items-center mb-4">
|
||||||
<label class="text-xs font-black uppercase tracking-widest text-slate-400">Compétences clés / Pré-requis</label>
|
<label class="text-xs font-black uppercase tracking-widest text-slate-400">Compétences clés / Pré-requis</label>
|
||||||
|
|||||||
@@ -48,8 +48,13 @@ Route::get('/dashboard', function () {
|
|||||||
->values()
|
->values()
|
||||||
->all();
|
->all();
|
||||||
} else {
|
} else {
|
||||||
$candidate = auth()->user()->candidate;
|
$candidate = auth()->user()->candidate?->load('jobPosition.quizzes');
|
||||||
$quizzes = \App\Models\Quiz::all()->map(function($quiz) use ($candidate) {
|
|
||||||
|
$quizzes = ($candidate && $candidate->jobPosition)
|
||||||
|
? $candidate->jobPosition->quizzes
|
||||||
|
: collect();
|
||||||
|
|
||||||
|
$quizzes = $quizzes->map(function($quiz) use ($candidate) {
|
||||||
$quiz->has_finished_attempt = $candidate
|
$quiz->has_finished_attempt = $candidate
|
||||||
? $candidate->attempts()->where('quiz_id', $quiz->id)->whereNotNull('finished_at')->exists()
|
? $candidate->attempts()->where('quiz_id', $quiz->id)->whereNotNull('finished_at')->exists()
|
||||||
: false;
|
: false;
|
||||||
|
|||||||
Reference in New Issue
Block a user