|
|
@@ -12,10 +12,12 @@ import {
|
|
|
FileText,
|
|
|
Star,
|
|
|
Award,
|
|
|
- Trophy
|
|
|
+ Trophy,
|
|
|
+ Trash2
|
|
|
} from 'lucide-react';
|
|
|
import { motion, AnimatePresence } from 'framer-motion';
|
|
|
import { useLanguage } from '../../contexts/LanguageContext';
|
|
|
+import { useConfirm } from '../../contexts/ConfirmContext';
|
|
|
import { assessmentService, AssessmentSession, AssessmentState } from '../../services/assessmentService';
|
|
|
import { knowledgeGroupService } from '../../services/knowledgeGroupService';
|
|
|
import { templateService } from '../../services/templateService';
|
|
|
@@ -34,6 +36,7 @@ export const AssessmentView: React.FC<AssessmentViewProps> = ({
|
|
|
isAdmin
|
|
|
}) => {
|
|
|
const { language, t } = useLanguage();
|
|
|
+ const { confirm } = useConfirm();
|
|
|
const [groups, setGroups] = useState<KnowledgeGroup[]>([]);
|
|
|
const [selectedGroup, setSelectedGroup] = useState<string | null>(null);
|
|
|
const [session, setSession] = useState<AssessmentSession | null>(null);
|
|
|
@@ -119,6 +122,24 @@ export const AssessmentView: React.FC<AssessmentViewProps> = ({
|
|
|
}
|
|
|
};
|
|
|
|
|
|
+ const handleDeleteHistory = async (e: React.MouseEvent, histId: string) => {
|
|
|
+ e.stopPropagation();
|
|
|
+ const confirmed = await confirm(t('confirmDeleteAssessment'));
|
|
|
+ if (!confirmed) return;
|
|
|
+
|
|
|
+ try {
|
|
|
+ await assessmentService.deleteSession(histId);
|
|
|
+ setHistory(prev => prev.filter(h => h.id !== histId));
|
|
|
+ if (session?.id === histId) {
|
|
|
+ setSession(null);
|
|
|
+ setState(null);
|
|
|
+ }
|
|
|
+ } catch (err: any) {
|
|
|
+ console.error('Failed to delete history:', err);
|
|
|
+ setError(t('deleteAssessmentFailed'));
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
const handleStartAssessment = async () => {
|
|
|
if (!selectedTemplate) return;
|
|
|
|
|
|
@@ -391,42 +412,55 @@ export const AssessmentView: React.FC<AssessmentViewProps> = ({
|
|
|
</h3>
|
|
|
<div className="space-y-3 custom-scrollbar">
|
|
|
{history.map(hist => (
|
|
|
- <button
|
|
|
+ <div
|
|
|
key={hist.id}
|
|
|
- type="button"
|
|
|
- onClick={() => handleSelectHistory(hist)}
|
|
|
- disabled={isLoading}
|
|
|
- className={cn(
|
|
|
- "w-full text-left p-4 rounded-2xl bg-slate-50 border border-slate-100 transition-all flex items-center justify-between group",
|
|
|
- isLoading ? "opacity-50 cursor-not-allowed" : "hover:border-indigo-200 hover:bg-indigo-50/30 cursor-pointer"
|
|
|
- )}
|
|
|
+ className="w-full text-left p-4 rounded-2xl bg-slate-50 border border-slate-100 flex items-center justify-between group"
|
|
|
>
|
|
|
- <div className="flex flex-col">
|
|
|
- <span className="text-sm font-bold text-slate-800 truncate max-w-[180px]">
|
|
|
- {hist.knowledgeBase?.name || hist.knowledgeGroup?.name || t('assessmentTitle')}
|
|
|
- </span>
|
|
|
- <div className="flex items-center gap-2 mt-1">
|
|
|
- <span className="text-[10px] font-black text-indigo-400 px-1.5 py-0.5 bg-indigo-50 rounded">
|
|
|
- {hist.finalScore !== null && hist.finalScore !== undefined ? `${Math.round(hist.finalScore * 10) / 10}/10` : t('inProgress')}
|
|
|
- </span>
|
|
|
- <span className="text-[10px] text-slate-400 font-bold uppercase tracking-wider">
|
|
|
- {new Date(hist.createdAt).toLocaleDateString()}
|
|
|
+ <div className="flex flex-col">
|
|
|
+ <span className="text-sm font-bold text-slate-800 truncate max-w-[180px]">
|
|
|
+ {hist.knowledgeBase?.name || hist.knowledgeGroup?.name || t('assessmentTitle')}
|
|
|
</span>
|
|
|
+ <div className="flex items-center gap-2 mt-1">
|
|
|
+ <span className="text-[10px] font-black text-indigo-400 px-1.5 py-0.5 bg-indigo-50 rounded">
|
|
|
+ {hist.finalScore !== null && hist.finalScore !== undefined ? `${Math.round(hist.finalScore * 10) / 10}/10` : t('inProgress')}
|
|
|
+ </span>
|
|
|
+ <span className="text-[10px] text-slate-400 font-bold uppercase tracking-wider">
|
|
|
+ {new Date(hist.createdAt).toLocaleDateString()}
|
|
|
+ </span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div className="flex items-center gap-2">
|
|
|
+ <button
|
|
|
+ type="button"
|
|
|
+ onClick={(e) => handleDeleteHistory(e, hist.id)}
|
|
|
+ className="w-8 h-8 rounded-full bg-white border border-slate-100 flex items-center justify-center text-slate-400 hover:text-rose-600 hover:border-rose-100 transition-all opacity-0 group-hover:opacity-100"
|
|
|
+ title={t('delete')}
|
|
|
+ >
|
|
|
+ <Trash2 size={14} />
|
|
|
+ </button>
|
|
|
+ <button
|
|
|
+ type="button"
|
|
|
+ onClick={() => !isLoading && handleSelectHistory(hist)}
|
|
|
+ disabled={isLoading}
|
|
|
+ className={cn(
|
|
|
+ "w-8 h-8 rounded-full bg-white border border-slate-100 flex items-center justify-center transition-all shrink-0",
|
|
|
+ isLoading ? "opacity-50 cursor-not-allowed" : "hover:bg-indigo-600 hover:text-white"
|
|
|
+ )}
|
|
|
+ title={t('view')}
|
|
|
+ >
|
|
|
+ {loadingHistoryId === hist.id ? (
|
|
|
+ <Loader2 size={14} className="animate-spin text-indigo-600 group-hover:text-white" />
|
|
|
+ ) : (
|
|
|
+ <FileText size={14} />
|
|
|
+ )}
|
|
|
+ </button>
|
|
|
</div>
|
|
|
</div>
|
|
|
- <div className="w-8 h-8 rounded-full bg-white border border-slate-100 flex items-center justify-center transition-all group-hover:bg-indigo-600 group-hover:text-white shrink-0">
|
|
|
- {loadingHistoryId === hist.id ? (
|
|
|
- <Loader2 size={14} className="animate-spin text-indigo-600 group-hover:text-white" />
|
|
|
- ) : (
|
|
|
- <FileText size={14} />
|
|
|
- )}
|
|
|
- </div>
|
|
|
- </button>
|
|
|
- ))}
|
|
|
+ ))}
|
|
|
+ </div>
|
|
|
</div>
|
|
|
- </div>
|
|
|
- )}
|
|
|
- </div>
|
|
|
+ )}
|
|
|
+ </div>
|
|
|
);
|
|
|
|
|
|
const renderAssessment = () => {
|