Ajout de la section notes d'entretien pour les candidats

This commit is contained in:
jeremy bayse
2026-03-20 10:10:24 +01:00
parent 3019aa1229
commit 27200a6839
5 changed files with 89 additions and 1 deletions

View File

@@ -116,6 +116,19 @@ class CandidateController extends Controller
return back()->with('success', 'Documents mis à jour avec succès.');
}
public function updateNotes(Request $request, Candidate $candidate)
{
$request->validate([
'notes' => 'nullable|string',
]);
$candidate->update([
'notes' => $request->notes,
]);
return back()->with('success', 'Notes mises à jour avec succès.');
}
public function resetPassword(Candidate $candidate)
{
$password = Str::random(10);

View File

@@ -9,7 +9,7 @@ use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Attributes\Fillable;
use Illuminate\Database\Eloquent\Factories\HasFactory;
#[Fillable(['user_id', 'phone', 'linkedin_url', 'status'])]
#[Fillable(['user_id', 'phone', 'linkedin_url', 'status', 'notes'])]
class Candidate extends Model
{
use HasFactory;

View File

@@ -0,0 +1,28 @@
<?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::table('candidates', function (Blueprint $table) {
$table->longText('notes')->nullable()->after('status');
});
}
/**
* Reverse the migrations.
*/
public function down(): void
{
Schema::table('candidates', function (Blueprint $table) {
$table->dropColumn('notes');
});
}
};

View File

@@ -23,6 +23,10 @@ const docForm = useForm({
_method: 'PUT' // For file upload via PUT in Laravel
});
const notesForm = useForm({
notes: props.candidate.notes || ''
});
const openAttempts = ref([]);
const toggleAttempt = (id) => {
@@ -71,6 +75,12 @@ const updateDocuments = () => {
});
};
const saveNotes = () => {
notesForm.patch(route('admin.candidates.update-notes', props.candidate.id), {
preserveScroll: true,
});
};
const openPreview = (doc) => {
selectedDocument.value = doc;
};
@@ -204,6 +214,42 @@ const openPreview = (doc) => {
<!-- Main: Attempts -->
<div class="xl:col-span-2 space-y-8">
<!-- Notes Section -->
<div class="bg-white dark:bg-slate-800 rounded-2xl shadow-sm border border-slate-200 dark:border-slate-700 p-8">
<div class="flex items-center justify-between mb-6">
<h4 class="text-xl font-bold flex items-center gap-2">
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-amber-500" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" />
</svg>
Notes d'entretien & Préparation
</h4>
<div v-if="notesForm.isDirty" class="flex items-center gap-2 animate-pulse">
<span class="text-[10px] font-black uppercase tracking-widest text-amber-600">Modifications non enregistrées</span>
<PrimaryButton @click="saveNotes" class="!px-4 !py-1 text-[10px]" :disabled="notesForm.processing">
Enregistrer
</PrimaryButton>
</div>
<div v-else class="flex items-center gap-2 text-emerald-500">
<svg xmlns="http://www.w3.org/2000/svg" class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7" />
</svg>
<span class="text-[10px] font-black uppercase tracking-widest">Enregistré</span>
</div>
</div>
<div class="relative group">
<textarea
v-model="notesForm.notes"
rows="12"
class="w-full bg-slate-50 dark:bg-slate-900 border-none rounded-2xl p-6 text-sm selection:bg-indigo-100 dark:selection:bg-indigo-900 focus:ring-2 focus:ring-indigo-500/20 transition-all placeholder:text-slate-300 dark:placeholder:text-slate-600 leading-relaxed font-mono"
placeholder="Rédigez ici vos questions, observations ou notes pendant l'entretien..."
></textarea>
<div class="absolute bottom-4 right-4 text-[10px] font-bold text-slate-400 opacity-0 group-hover:opacity-100 transition-opacity">
Supporte le texte brut
</div>
</div>
</div>
<div class="bg-white dark:bg-slate-800 rounded-2xl shadow-sm border border-slate-200 dark:border-slate-700 p-8">
<h3 class="text-xl font-bold mb-8 flex items-center justify-between">
Historique des Tests

View File

@@ -52,6 +52,7 @@ Route::middleware('auth')->group(function () {
Route::middleware('admin')->prefix('admin')->name('admin.')->group(function () {
Route::get('/comparative', [\App\Http\Controllers\CandidateController::class, 'comparative'])->name('comparative');
Route::resource('candidates', \App\Http\Controllers\CandidateController::class)->only(['index', 'store', 'show', 'destroy', 'update']);
Route::patch('/candidates/{candidate}/notes', [\App\Http\Controllers\CandidateController::class, 'updateNotes'])->name('candidates.update-notes');
Route::post('/candidates/{candidate}/reset-password', [\App\Http\Controllers\CandidateController::class, 'resetPassword'])->name('candidates.reset-password');
Route::get('/documents/{document}', [\App\Http\Controllers\DocumentController::class, 'show'])->name('documents.show');