feat(interviews): animate sidebar collapse when candidate selected

This commit is contained in:
Gabriel Ramos 2026-06-10 17:18:49 -04:00
parent 628faa7e24
commit 5f87b58baa

View file

@ -415,269 +415,288 @@ export default function InterviewsPage() {
</p>
</div>
) : (
<div className="grid grid-cols-1 lg:grid-cols-4 gap-6 items-start">
<div className="flex flex-col lg:flex-row items-start w-full overflow-hidden">
{/* COLUMN 1: Open Positions Sidebar */}
<div className="lg:col-span-1 bg-white dark:bg-slate-900 p-4 rounded-lg shadow-sm border border-slate-200 dark:border-slate-800 flex flex-col gap-1.5">
<h3 className="text-xs font-semibold text-slate-400 dark:text-slate-500 uppercase tracking-wider px-2 mb-1">
{t.openPositions}
</h3>
<button
onClick={() => handleSelectJob(null)}
className={`w-full text-left px-3 py-2 rounded-md text-sm transition-colors duration-150 flex items-center justify-between cursor-pointer ${
selectedJobId === null
? "bg-slate-100 dark:bg-slate-800 text-slate-900 dark:text-white font-medium"
: "text-slate-600 dark:text-slate-300 hover:bg-slate-50 dark:hover:bg-slate-800/50"
}`}
>
<span>{t.allPositions}</span>
<span className="text-xs px-2 py-0.5 bg-slate-200 dark:bg-slate-700 text-slate-700 dark:text-slate-300 rounded-full font-medium">
{interviews.length}
</span>
</button>
<div className={`transition-all duration-300 ease-in-out flex-shrink-0 ${
selectedInterviewId
? "w-0 opacity-0 overflow-hidden lg:pr-0 pointer-events-none"
: "w-full lg:w-1/4 lg:pr-6 mb-6 lg:mb-0"
}`}>
<div className="bg-white dark:bg-slate-900 p-4 rounded-lg shadow-sm border border-slate-200 dark:border-slate-800 flex flex-col gap-1.5">
<h3 className="text-xs font-semibold text-slate-400 dark:text-slate-500 uppercase tracking-wider px-2 mb-1">
{t.openPositions}
</h3>
<button
onClick={() => handleSelectJob(null)}
className={`w-full text-left px-3 py-2 rounded-md text-sm transition-colors duration-150 flex items-center justify-between cursor-pointer ${
selectedJobId === null
? "bg-slate-100 dark:bg-slate-800 text-slate-900 dark:text-white font-medium"
: "text-slate-600 dark:text-slate-300 hover:bg-slate-50 dark:hover:bg-slate-800/50"
}`}
>
<span>{t.allPositions}</span>
<span className="text-xs px-2 py-0.5 bg-slate-200 dark:bg-slate-700 text-slate-700 dark:text-slate-300 rounded-full font-medium">
{interviews.length}
</span>
</button>
{jobs.map((job) => {
const count = getInterviewCountForJob(job.id);
return (
<button
key={job.id}
onClick={() => handleSelectJob(job.id)}
className={`w-full text-left px-3 py-2 rounded-md text-sm transition-colors duration-150 flex items-center justify-between cursor-pointer ${
selectedJobId === job.id
? "bg-slate-100 dark:bg-slate-800 text-slate-900 dark:text-white font-medium"
: "text-slate-600 dark:text-slate-300 hover:bg-slate-50 dark:hover:bg-slate-800/50"
}`}
>
<span className="truncate mr-2" title={job.title}>{job.title}</span>
<span className="text-xs px-2 py-0.5 bg-slate-200 dark:bg-slate-700 text-slate-700 dark:text-slate-300 rounded-full font-medium flex-shrink-0">
{count}
</span>
</button>
);
})}
{jobs.map((job) => {
const count = getInterviewCountForJob(job.id);
return (
<button
key={job.id}
onClick={() => handleSelectJob(job.id)}
className={`w-full text-left px-3 py-2 rounded-md text-sm transition-colors duration-150 flex items-center justify-between cursor-pointer ${
selectedJobId === job.id
? "bg-slate-100 dark:bg-slate-800 text-slate-900 dark:text-white font-medium"
: "text-slate-600 dark:text-slate-300 hover:bg-slate-50 dark:hover:bg-slate-800/50"
}`}
>
<span className="truncate mr-2" title={job.title}>{job.title}</span>
<span className="text-xs px-2 py-0.5 bg-slate-200 dark:bg-slate-700 text-slate-700 dark:text-slate-300 rounded-full font-medium flex-shrink-0">
{count}
</span>
</button>
);
})}
</div>
</div>
{/* COLUMN 2: Candidates List */}
<div className="lg:col-span-1 bg-white dark:bg-slate-900 p-4 rounded-lg shadow-sm border border-slate-200 dark:border-slate-800 flex flex-col gap-4">
<div className="border-b border-slate-200 dark:border-slate-800 pb-2">
<h3 className="text-sm font-bold text-slate-900 dark:text-white">
{t.candidates}
</h3>
</div>
<div className={`transition-all duration-300 ease-in-out flex-shrink-0 ${
selectedInterviewId
? "w-full lg:w-1/3 lg:pr-6 mb-6 lg:mb-0"
: "w-full lg:w-1/4 lg:pr-6 mb-6 lg:mb-0"
}`}>
<div className="bg-white dark:bg-slate-900 p-4 rounded-lg shadow-sm border border-slate-200 dark:border-slate-800 flex flex-col gap-4">
<div className="border-b border-slate-200 dark:border-slate-800 pb-2">
<h3 className="text-sm font-bold text-slate-900 dark:text-white">
{t.candidates}
</h3>
</div>
{sortedInterviews.length === 0 ? (
<p className="text-slate-500 dark:text-slate-400 text-sm py-4 italic">
{t.noCandidatesInStage}
</p>
) : (
<div className="flex flex-col gap-3">
{sortedInterviews.map((interview) => (
<div
key={interview.id}
onClick={() => setSelectedInterviewId(interview.id)}
className={`cursor-pointer p-4 rounded-lg shadow-sm border transition duration-200 relative ${
selectedInterviewId === interview.id
? "bg-slate-50 dark:bg-slate-800/40 border-blue-300 dark:border-blue-800"
: "bg-white dark:bg-slate-900 border-slate-200 dark:border-slate-800 hover:border-slate-300 dark:hover:border-slate-700"
} ${
interview.pinned
? "border-l-4 border-l-blue-600 pl-3"
: "border-l border-l-slate-200 dark:border-l-slate-800 pl-4"
}`}
>
<div className="flex items-start justify-between gap-2">
<div className="flex-1 min-w-0">
<h4 className="text-sm font-bold text-slate-900 dark:text-white truncate">
{interview.candidates?.name || t.unknownCandidate}
</h4>
<p className="text-xs text-slate-500 dark:text-slate-400 truncate mt-0.5">
{interview.jobs?.title || t.unknownJob}
</p>
</div>
<div className="flex items-center gap-1.5 flex-shrink-0">
<span className={`px-2 py-0.5 text-[10px] font-semibold rounded-full ${
interview.stage === "Hired"
? "bg-green-100 dark:bg-green-950/40 text-green-800 dark:text-green-300"
: interview.stage === "Rejected"
? "bg-red-100 dark:bg-red-950/40 text-red-800 dark:text-red-300"
: "bg-slate-100 dark:bg-slate-800 text-slate-700 dark:text-slate-300"
}`}>
{translateStage(interview.stage, lang)}
</span>
{sortedInterviews.length === 0 ? (
<p className="text-slate-500 dark:text-slate-400 text-sm py-4 italic">
{t.noCandidatesInStage}
</p>
) : (
<div className="flex flex-col gap-3">
{sortedInterviews.map((interview) => (
<div
key={interview.id}
onClick={() => setSelectedInterviewId(interview.id)}
className={`cursor-pointer p-4 rounded-lg shadow-sm border transition duration-200 relative ${
selectedInterviewId === interview.id
? "bg-slate-50 dark:bg-slate-800/40 border-blue-300 dark:border-blue-800"
: "bg-white dark:bg-slate-900 border-slate-200 dark:border-slate-800 hover:border-slate-300 dark:hover:border-slate-700"
} ${
interview.pinned
? "border-l-4 border-l-blue-600 pl-3"
: "border-l border-l-slate-200 dark:border-l-slate-800 pl-4"
}`}
>
<div className="flex items-start justify-between gap-2">
<div className="flex-1 min-w-0">
<h4 className="text-sm font-bold text-slate-900 dark:text-white truncate">
{interview.candidates?.name || t.unknownCandidate}
</h4>
<p className="text-xs text-slate-500 dark:text-slate-400 truncate mt-0.5">
{interview.jobs?.title || t.unknownJob}
</p>
</div>
<div className="flex items-center gap-1.5 flex-shrink-0">
<span className={`px-2 py-0.5 text-[10px] font-semibold rounded-full ${
interview.stage === "Hired"
? "bg-green-100 dark:bg-green-950/40 text-green-800 dark:text-green-300"
: interview.stage === "Rejected"
? "bg-red-100 dark:bg-red-950/40 text-red-800 dark:text-red-300"
: "bg-slate-100 dark:bg-slate-800 text-slate-700 dark:text-slate-300"
}`}>
{translateStage(interview.stage, lang)}
</span>
<button
onClick={(e) => {
e.stopPropagation();
togglePin(interview.id, interview.pinned);
}}
className="p-1 rounded hover:bg-slate-100 dark:hover:bg-slate-800 text-slate-400 hover:text-slate-600 dark:hover:text-slate-300 transition-colors cursor-pointer"
title={interview.pinned ? t.unpinCandidate : t.pinCandidate}
>
{interview.pinned ? (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" className="w-3.5 h-3.5 text-blue-600 dark:text-blue-400">
<path d="M12 17v5M9 10.76a2 2 0 0 1-1.11 1.79l-1.78.89A.5.5 0 0 0 6.36 14h11.27a.5.5 0 0 0 .25-.56l-1.78-.89a2 2 0 0 1-1.11-1.79V4a2 2 0 0 1 2-2h-10a2 2 0 0 1 2 2v6.76z" />
</svg>
) : (
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={2} stroke="currentColor" className="w-3.5 h-3.5">
<path strokeLinecap="round" strokeLinejoin="round" d="M12 17v5M9 10.76a2 2 0 0 1-1.11 1.79l-1.78.89A.5.5 0 0 0 6.36 14h11.27a.5.5 0 0 0 .25-.56l-1.78-.89a2 2 0 0 1-1.11-1.79V4a2 2 0 0 1 2-2h-10a2 2 0 0 1 2 2v6.76z" />
</svg>
)}
</button>
</div>
</div>
</div>
))}
</div>
)}
</div>
</div>
{/* COLUMN 3: Candidate Details Pane */}
<div className={`transition-all duration-300 ease-in-out flex-1 ${
selectedInterviewId
? "w-full lg:w-2/3"
: "w-full lg:w-2/4"
}`}>
<div className="bg-white dark:bg-slate-900 p-6 rounded-lg shadow-sm border border-slate-200 dark:border-slate-800 flex flex-col gap-6 min-h-[300px]">
{!selectedInterview ? (
<div className="flex-1 flex flex-col items-center justify-center text-center py-12">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="w-10 h-10 text-slate-300 dark:text-slate-600 mb-3">
<path strokeLinecap="round" strokeLinejoin="round" d="M17.982 18.725A7.488 7.488 0 0 0 12 15.75a7.488 7.488 0 0 0-5.982 2.975m11.963 0a9 9 0 1 0-11.963 0m11.963 0A8.966 8.966 0 0 1 12 21a8.966 8.966 0 0 1-5.982-2.275M15 9.75a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" />
</svg>
<p className="text-slate-500 dark:text-slate-400 text-sm font-medium">
{t.selectCandidateDetails}
</p>
</div>
) : (
<div className="flex flex-col gap-6">
{/* Details Header */}
<div className="border-b border-slate-200 dark:border-slate-800 pb-4 flex flex-col gap-1.5">
<h2 className="text-xl font-bold text-slate-900 dark:text-white">
{selectedInterview.candidates?.name || t.unknownCandidate}
</h2>
<div className="flex flex-wrap gap-x-4 gap-y-1 text-sm text-slate-600 dark:text-slate-300 font-medium">
<span>
{t.role}: <span className="font-bold text-slate-800 dark:text-slate-200">{selectedInterview.jobs?.title || t.unknownJob}</span>
</span>
<span className="text-slate-300 dark:text-slate-700 hidden sm:inline">|</span>
<span>
{t.dateScheduled}: <span className="text-slate-500 dark:text-slate-400">{new Date(selectedInterview.interview_date).toLocaleString()}</span>
</span>
</div>
</div>
{/* Stage Dropdown Selector */}
<div className="flex flex-col gap-1.5">
<label className="text-xs font-semibold text-slate-500 dark:text-slate-400 uppercase tracking-wider">
{t.stage}:
</label>
<select
value={selectedInterview.stage}
onChange={(e) => updateInterviewField(selectedInterview.id, { stage: e.target.value })}
className="w-full px-3 py-2 bg-white dark:bg-slate-800 border border-slate-200 dark:border-slate-700 rounded-md text-slate-900 dark:text-white focus:outline-none focus:border-blue-500 transition-colors"
>
<option value="Screening">{t.screening}</option>
<option value="Technical">{t.technical}</option>
<option value="Cultural">{t.cultural}</option>
<option value="Offer">{t.offer}</option>
<option value="Hired">{t.hired}</option>
<option value="Rejected">{t.rejected}</option>
</select>
</div>
{/* Comments Thread System */}
<div className="flex flex-col gap-4 border-t border-slate-200 dark:border-slate-800 pt-4">
<div className="flex items-center justify-between gap-4">
<h3 className="text-xs font-bold text-slate-400 dark:text-slate-500 uppercase tracking-wider">
{t.interviewFeedback}
</h3>
{/* AI Suggestion Button */}
<button
onClick={handleGetAiSuggestion}
disabled={suggesting || cooldownActive}
title={cooldownActive ? t.cooldownMessage.replace("{hours}", String(cooldownHours)) : undefined}
className={`px-3 py-1.5 text-xs font-bold rounded-md border transition-all duration-200 cursor-pointer flex items-center gap-1.5 select-none ${
cooldownActive
? "bg-slate-50 dark:bg-slate-800/50 text-slate-400 border-slate-200 dark:border-slate-800 cursor-not-allowed opacity-60"
: "bg-blue-50 hover:bg-blue-100 dark:bg-blue-950/20 dark:hover:bg-blue-950/45 text-blue-600 dark:text-blue-400 border-blue-200 dark:border-blue-800/80"
}`}
>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={2} stroke="currentColor" className="w-3.5 h-3.5">
<path strokeLinecap="round" strokeLinejoin="round" d="M9.813 15.904 9 21m0-12h.008v.008H9V9Zm.375 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0ZM12 10.5h.008v.008H12V10.5Zm.375 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Zm-.75 3h.008v.008H11.625V13.5Zm.375 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0ZM15 9.75h.008v.008H15V9.75Zm.375 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0ZM18 12h.008v.008H18V12Zm.375 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Zm-.75 3h.008v.008H17.625V15Zm.375 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0ZM2.25 12c0-5.385 4.365-9.75 9.75-9.75s9.75 4.365 9.75 9.75-4.365 9.75-9.75 9.75S2.25 17.385 2.25 12Zm11.379-3.379a3 3 0 0 0-4.242 4.242l4.242-4.242Z" />
</svg>
{suggesting ? t.suggesting : t.aiSuggestButton}
</button>
</div>
{/* Comment List */}
<div className="max-h-72 overflow-y-auto pr-1 flex flex-col gap-3">
{selectedInterviewComments.length === 0 ? (
<p className="text-slate-500 dark:text-slate-400 text-xs italic py-2">
{t.noCommentsYet}
</p>
) : (
selectedInterviewComments.map((comment) => {
const isCollapsed = collapsedComments[comment.id] || false;
const isAiComment = comment.isAi || comment.author === "AI Assistant" || comment.author === "Asistente IA";
return (
<div
key={comment.id}
className={`p-3 rounded-lg border flex flex-col gap-1.5 transition-colors ${
isAiComment
? "bg-blue-50/20 dark:bg-blue-950/10 border-blue-100 dark:border-blue-900/50"
: "bg-slate-50 dark:bg-slate-800/40 border-slate-100 dark:border-slate-800/60"
}`}
>
<div className="flex items-center justify-between text-[11px] text-slate-500 dark:text-slate-400">
<span className={`font-bold ${isAiComment ? "text-blue-600 dark:text-blue-400" : "text-slate-700 dark:text-slate-300"}`}>
{comment.author || t.postedByAgent}
</span>
<div className="flex items-center gap-2">
<span>
{new Date(comment.timestamp).toLocaleString()}
</span>
<button
onClick={() => toggleCommentCollapse(comment.id)}
className="text-xs text-slate-400 hover:text-slate-600 dark:hover:text-slate-300 font-semibold cursor-pointer select-none"
>
{isCollapsed ? "[+]" : "[-]"}
</button>
</div>
</div>
{!isCollapsed && (
<div className="text-xs text-slate-700 dark:text-slate-300 leading-relaxed markdown-content">
<ReactMarkdown>{comment.text}</ReactMarkdown>
</div>
)}
</div>
);
})
)}
</div>
{/* Add Comment Input */}
<div className="flex flex-col gap-2 mt-2">
<textarea
value={newComment}
onChange={(e) => setNewComment(e.target.value)}
placeholder={t.commentPlaceholder}
rows={3}
className="w-full px-3 py-2 border border-slate-200 dark:border-slate-700 rounded-md text-slate-900 dark:text-white bg-white dark:bg-slate-800 placeholder:text-slate-500 dark:placeholder:text-slate-400 text-sm focus:outline-none focus:border-blue-500 transition-colors"
/>
<div className="flex justify-end">
<button
onClick={(e) => {
e.stopPropagation();
togglePin(interview.id, interview.pinned);
}}
className="p-1 rounded hover:bg-slate-100 dark:hover:bg-slate-800 text-slate-400 hover:text-slate-600 dark:hover:text-slate-300 transition-colors cursor-pointer"
title={interview.pinned ? t.unpinCandidate : t.pinCandidate}
onClick={handleAddComment}
disabled={!newComment.trim()}
className="py-1.5 px-3.5 bg-blue-600 hover:bg-blue-700 disabled:opacity-50 text-white text-xs font-semibold rounded-md transition duration-200 cursor-pointer"
>
{interview.pinned ? (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" className="w-3.5 h-3.5 text-blue-600 dark:text-blue-400">
<path d="M12 17v5M9 10.76a2 2 0 0 1-1.11 1.79l-1.78.89A.5.5 0 0 0 6.36 14h11.27a.5.5 0 0 0 .25-.56l-1.78-.89a2 2 0 0 1-1.11-1.79V4a2 2 0 0 1 2-2h-10a2 2 0 0 1 2 2v6.76z" />
</svg>
) : (
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={2} stroke="currentColor" className="w-3.5 h-3.5">
<path strokeLinecap="round" strokeLinejoin="round" d="M12 17v5M9 10.76a2 2 0 0 1-1.11 1.79l-1.78.89A.5.5 0 0 0 6.36 14h11.27a.5.5 0 0 0 .25-.56l-1.78-.89a2 2 0 0 1-1.11-1.79V4a2 2 0 0 1 2-2h-10a2 2 0 0 1 2 2v6.76z" />
</svg>
)}
{t.addComment}
</button>
</div>
</div>
</div>
))}
</div>
)}
</div>
{/* COLUMN 3: Candidate Details Pane */}
<div className="lg:col-span-2 bg-white dark:bg-slate-900 p-6 rounded-lg shadow-sm border border-slate-200 dark:border-slate-800 flex flex-col gap-6 min-h-[300px]">
{!selectedInterview ? (
<div className="flex-1 flex flex-col items-center justify-center text-center py-12">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="w-10 h-10 text-slate-300 dark:text-slate-600 mb-3">
<path strokeLinecap="round" strokeLinejoin="round" d="M17.982 18.725A7.488 7.488 0 0 0 12 15.75a7.488 7.488 0 0 0-5.982 2.975m11.963 0a9 9 0 1 0-11.963 0m11.963 0A8.966 8.966 0 0 1 12 21a8.966 8.966 0 0 1-5.982-2.275M15 9.75a3 3 0 1 1-6 0 3 3 0 0 1 6 0Z" />
</svg>
<p className="text-slate-500 dark:text-slate-400 text-sm font-medium">
{t.selectCandidateDetails}
</p>
</div>
) : (
<div className="flex flex-col gap-6">
{/* Details Header */}
<div className="border-b border-slate-200 dark:border-slate-800 pb-4 flex flex-col gap-1.5">
<h2 className="text-xl font-bold text-slate-900 dark:text-white">
{selectedInterview.candidates?.name || t.unknownCandidate}
</h2>
<div className="flex flex-wrap gap-x-4 gap-y-1 text-sm text-slate-600 dark:text-slate-300 font-medium">
<span>
{t.role}: <span className="font-bold text-slate-800 dark:text-slate-200">{selectedInterview.jobs?.title || t.unknownJob}</span>
</span>
<span className="text-slate-300 dark:text-slate-700 hidden sm:inline">|</span>
<span>
{t.dateScheduled}: <span className="text-slate-500 dark:text-slate-400">{new Date(selectedInterview.interview_date).toLocaleString()}</span>
</span>
</div>
</div>
{/* Stage Dropdown Selector */}
<div className="flex flex-col gap-1.5">
<label className="text-xs font-semibold text-slate-500 dark:text-slate-400 uppercase tracking-wider">
{t.stage}:
</label>
<select
value={selectedInterview.stage}
onChange={(e) => updateInterviewField(selectedInterview.id, { stage: e.target.value })}
className="w-full px-3 py-2 bg-white dark:bg-slate-800 border border-slate-200 dark:border-slate-700 rounded-md text-slate-900 dark:text-white focus:outline-none focus:border-blue-500 transition-colors"
>
<option value="Screening">{t.screening}</option>
<option value="Technical">{t.technical}</option>
<option value="Cultural">{t.cultural}</option>
<option value="Offer">{t.offer}</option>
<option value="Hired">{t.hired}</option>
<option value="Rejected">{t.rejected}</option>
</select>
</div>
{/* Comments Thread System */}
<div className="flex flex-col gap-4 border-t border-slate-200 dark:border-slate-800 pt-4">
<div className="flex items-center justify-between gap-4">
<h3 className="text-xs font-bold text-slate-400 dark:text-slate-500 uppercase tracking-wider">
{t.interviewFeedback}
</h3>
{/* AI Suggestion Button */}
<button
onClick={handleGetAiSuggestion}
disabled={suggesting || cooldownActive}
title={cooldownActive ? t.cooldownMessage.replace("{hours}", String(cooldownHours)) : undefined}
className={`px-3 py-1.5 text-xs font-bold rounded-md border transition-all duration-200 cursor-pointer flex items-center gap-1.5 select-none ${
cooldownActive
? "bg-slate-50 dark:bg-slate-800/50 text-slate-400 border-slate-200 dark:border-slate-800 cursor-not-allowed opacity-60"
: "bg-blue-50 hover:bg-blue-100 dark:bg-blue-950/20 dark:hover:bg-blue-950/45 text-blue-600 dark:text-blue-400 border-blue-200 dark:border-blue-800/80"
}`}
>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={2} stroke="currentColor" className="w-3.5 h-3.5">
<path strokeLinecap="round" strokeLinejoin="round" d="M9.813 15.904 9 21m0-12h.008v.008H9V9Zm.375 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0ZM12 10.5h.008v.008H12V10.5Zm.375 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Zm-.75 3h.008v.008H11.625V13.5Zm.375 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0ZM15 9.75h.008v.008H15V9.75Zm.375 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0ZM18 12h.008v.008H18V12Zm.375 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0Zm-.75 3h.008v.008H17.625V15Zm.375 0a.375.375 0 1 1-.75 0 .375.375 0 0 1 .75 0ZM2.25 12c0-5.385 4.365-9.75 9.75-9.75s9.75 4.365 9.75 9.75-4.365 9.75-9.75 9.75S2.25 17.385 2.25 12Zm11.379-3.379a3 3 0 0 0-4.242 4.242l4.242-4.242Z" />
</svg>
{suggesting ? t.suggesting : t.aiSuggestButton}
</button>
</div>
{/* Comment List */}
<div className="max-h-72 overflow-y-auto pr-1 flex flex-col gap-3">
{selectedInterviewComments.length === 0 ? (
<p className="text-slate-500 dark:text-slate-400 text-xs italic py-2">
{t.noCommentsYet}
</p>
) : (
selectedInterviewComments.map((comment) => {
const isCollapsed = collapsedComments[comment.id] || false;
const isAiComment = comment.isAi || comment.author === "AI Assistant" || comment.author === "Asistente IA";
return (
<div
key={comment.id}
className={`p-3 rounded-lg border flex flex-col gap-1.5 transition-colors ${
isAiComment
? "bg-blue-50/20 dark:bg-blue-950/10 border-blue-100 dark:border-blue-900/50"
: "bg-slate-50 dark:bg-slate-800/40 border-slate-100 dark:border-slate-800/60"
}`}
>
<div className="flex items-center justify-between text-[11px] text-slate-500 dark:text-slate-400">
<span className={`font-bold ${isAiComment ? "text-blue-600 dark:text-blue-400" : "text-slate-700 dark:text-slate-300"}`}>
{comment.author || t.postedByAgent}
</span>
<div className="flex items-center gap-2">
<span>
{new Date(comment.timestamp).toLocaleString()}
</span>
<button
onClick={() => toggleCommentCollapse(comment.id)}
className="text-xs text-slate-400 hover:text-slate-600 dark:hover:text-slate-300 font-semibold cursor-pointer select-none"
>
{isCollapsed ? "[+]" : "[-]"}
</button>
</div>
</div>
{!isCollapsed && (
<div className="text-xs text-slate-700 dark:text-slate-300 leading-relaxed markdown-content">
<ReactMarkdown>{comment.text}</ReactMarkdown>
</div>
)}
</div>
);
})
)}
</div>
{/* Add Comment Input */}
<div className="flex flex-col gap-2 mt-2">
<textarea
value={newComment}
onChange={(e) => setNewComment(e.target.value)}
placeholder={t.commentPlaceholder}
rows={3}
className="w-full px-3 py-2 border border-slate-200 dark:border-slate-700 rounded-md text-slate-900 dark:text-white bg-white dark:bg-slate-800 placeholder:text-slate-500 dark:placeholder:text-slate-400 text-sm focus:outline-none focus:border-blue-500 transition-colors"
/>
<div className="flex justify-end">
<button
onClick={handleAddComment}
disabled={!newComment.trim()}
className="py-1.5 px-3.5 bg-blue-600 hover:bg-blue-700 disabled:opacity-50 text-white text-xs font-semibold rounded-md transition duration-200 cursor-pointer"
>
{t.addComment}
</button>
</div>
</div>
</div>
</div>
)}
)}
</div>
</div>
</div>
)}
</div>
);
}