Update Dockerfile and package.json to use Prisma migrations, add bcryptjs and next-auth dependencies, and enhance README instructions for database setup. Refactor Prisma schema to include password hashing for users and implement evaluation sharing functionality. Improve admin page with user management features and integrate session handling for authentication. Enhance evaluation detail page with sharing options and update API routes for access control based on user roles.
All checks were successful
Deploy with Docker Compose / deploy (push) Successful in 3m4s
All checks were successful
Deploy with Docker Compose / deploy (push) Successful in 3m4s
This commit is contained in:
@@ -7,6 +7,7 @@ import { CandidateForm } from "@/components/CandidateForm";
|
||||
import { DimensionCard } from "@/components/DimensionCard";
|
||||
import { RadarChart } from "@/components/RadarChart";
|
||||
import { ExportModal } from "@/components/ExportModal";
|
||||
import { ShareModal } from "@/components/ShareModal";
|
||||
import { ConfirmModal } from "@/components/ConfirmModal";
|
||||
import { generateFindings, generateRecommendations, computeAverageScore } from "@/lib/export-utils";
|
||||
|
||||
@@ -35,6 +36,7 @@ interface Evaluation {
|
||||
candidateRole: string;
|
||||
candidateTeam?: string | null;
|
||||
evaluatorName: string;
|
||||
evaluatorId?: string | null;
|
||||
evaluationDate: string;
|
||||
templateId: string;
|
||||
template: { id: string; name: string; dimensions: Dimension[] };
|
||||
@@ -42,6 +44,7 @@ interface Evaluation {
|
||||
findings: string | null;
|
||||
recommendations: string | null;
|
||||
dimensionScores: DimensionScore[];
|
||||
sharedWith?: { id: string; user: { id: string; email: string; name: string | null } }[];
|
||||
}
|
||||
|
||||
export default function EvaluationDetailPage() {
|
||||
@@ -53,17 +56,21 @@ export default function EvaluationDetailPage() {
|
||||
const [saving, setSaving] = useState(false);
|
||||
const [templates, setTemplates] = useState<{ id: string; name: string }[]>([]);
|
||||
const [exportOpen, setExportOpen] = useState(false);
|
||||
const [shareOpen, setShareOpen] = useState(false);
|
||||
const [deleteConfirmOpen, setDeleteConfirmOpen] = useState(false);
|
||||
const [collapseAllTrigger, setCollapseAllTrigger] = useState(0);
|
||||
const [users, setUsers] = useState<{ id: string; email: string; name: string | null }[]>([]);
|
||||
|
||||
const fetchEval = useCallback(() => {
|
||||
setLoading(true);
|
||||
Promise.all([
|
||||
fetch(`/api/evaluations/${id}`).then((r) => r.json()),
|
||||
fetch("/api/templates").then((r) => r.json()),
|
||||
fetch("/api/users").then((r) => r.json()),
|
||||
])
|
||||
.then(([evalData, templatesData]) => {
|
||||
.then(([evalData, templatesData, usersData]) => {
|
||||
setTemplates(Array.isArray(templatesData) ? templatesData : []);
|
||||
setUsers(Array.isArray(usersData) ? usersData : []);
|
||||
if (evalData?.error) {
|
||||
setEvaluation(null);
|
||||
return;
|
||||
@@ -251,6 +258,12 @@ export default function EvaluationDetailPage() {
|
||||
>
|
||||
{saving ? "..." : "save"}
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setShareOpen(true)}
|
||||
className="rounded border border-cyan-500/50 bg-cyan-500/10 px-3 py-1.5 font-mono text-xs text-cyan-600 dark:text-cyan-400 hover:bg-cyan-500/20"
|
||||
>
|
||||
partager
|
||||
</button>
|
||||
<button
|
||||
onClick={() => setExportOpen(true)}
|
||||
className="rounded border border-cyan-500/50 bg-cyan-500/10 px-3 py-1.5 font-mono text-xs text-cyan-600 dark:text-cyan-400 hover:bg-cyan-500/20"
|
||||
@@ -266,8 +279,11 @@ export default function EvaluationDetailPage() {
|
||||
</div>
|
||||
)}
|
||||
|
||||
<section className="rounded-lg border border-zinc-200 dark:border-zinc-600 bg-white dark:bg-zinc-800 p-4 shadow-sm dark:shadow-none">
|
||||
<h2 className="mb-3 font-mono text-xs text-zinc-600 dark:text-zinc-500">Session</h2>
|
||||
<section className="relative overflow-hidden rounded-xl border border-zinc-200 dark:border-zinc-600 bg-gradient-to-br from-zinc-50 to-white dark:from-zinc-800/80 dark:to-zinc-800 p-5 shadow-sm dark:shadow-none">
|
||||
<div className="absolute left-0 top-0 h-full w-1 bg-gradient-to-b from-cyan-500/60 to-cyan-400/40" aria-hidden />
|
||||
<h2 className="mb-4 font-mono text-xs font-medium uppercase tracking-wider text-zinc-500 dark:text-zinc-400">
|
||||
Session
|
||||
</h2>
|
||||
<CandidateForm
|
||||
candidateName={evaluation.candidateName}
|
||||
candidateRole={evaluation.candidateRole}
|
||||
@@ -393,6 +409,16 @@ export default function EvaluationDetailPage() {
|
||||
evaluationId={id}
|
||||
/>
|
||||
|
||||
<ShareModal
|
||||
isOpen={shareOpen}
|
||||
onClose={() => setShareOpen(false)}
|
||||
evaluationId={id}
|
||||
evaluatorId={evaluation.evaluatorId}
|
||||
users={users}
|
||||
sharedWith={evaluation.sharedWith ?? []}
|
||||
onUpdate={fetchEval}
|
||||
/>
|
||||
|
||||
<ConfirmModal
|
||||
isOpen={deleteConfirmOpen}
|
||||
title="Supprimer l'évaluation"
|
||||
|
||||
Reference in New Issue
Block a user