feat: animation de confirmation sur le bouton save
Ajoute 3 états visuels au bouton save : repos (gris), saving (spinner), saved (fond vert + checkmark animé pendant 2 secondes). Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -25,3 +25,14 @@ input:focus, select:focus, textarea:focus {
|
|||||||
outline: none;
|
outline: none;
|
||||||
ring: 2px;
|
ring: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@keyframes check {
|
||||||
|
0% { stroke-dashoffset: 20; opacity: 0; }
|
||||||
|
50% { opacity: 1; }
|
||||||
|
100% { stroke-dashoffset: 0; opacity: 1; }
|
||||||
|
}
|
||||||
|
|
||||||
|
.check-icon polyline {
|
||||||
|
stroke-dasharray: 20;
|
||||||
|
animation: check 0.3s ease-out forwards;
|
||||||
|
}
|
||||||
|
|||||||
@@ -59,6 +59,7 @@ export function EvaluationEditor({ id, initialEvaluation, templates, users }: Ev
|
|||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const [evaluation, setEvaluation] = useState<Evaluation>(initialEvaluation);
|
const [evaluation, setEvaluation] = useState<Evaluation>(initialEvaluation);
|
||||||
const [saving, setSaving] = useState(false);
|
const [saving, setSaving] = useState(false);
|
||||||
|
const [saved, setSaved] = useState(false);
|
||||||
const [exportOpen, setExportOpen] = useState(false);
|
const [exportOpen, setExportOpen] = useState(false);
|
||||||
const [shareOpen, setShareOpen] = useState(false);
|
const [shareOpen, setShareOpen] = useState(false);
|
||||||
const [deleteConfirmOpen, setDeleteConfirmOpen] = useState(false);
|
const [deleteConfirmOpen, setDeleteConfirmOpen] = useState(false);
|
||||||
@@ -149,6 +150,8 @@ export function EvaluationEditor({ id, initialEvaluation, templates, users }: Ev
|
|||||||
});
|
});
|
||||||
if (result.success) {
|
if (result.success) {
|
||||||
if (!options?.skipRefresh) fetchEval();
|
if (!options?.skipRefresh) fetchEval();
|
||||||
|
setSaved(true);
|
||||||
|
setTimeout(() => setSaved(false), 2000);
|
||||||
} else {
|
} else {
|
||||||
alert(result.error);
|
alert(result.error);
|
||||||
}
|
}
|
||||||
@@ -208,9 +211,20 @@ export function EvaluationEditor({ id, initialEvaluation, templates, users }: Ev
|
|||||||
<button
|
<button
|
||||||
onClick={() => handleSave()}
|
onClick={() => handleSave()}
|
||||||
disabled={saving}
|
disabled={saving}
|
||||||
className="rounded border border-zinc-300 dark:border-zinc-600 bg-zinc-100 dark:bg-zinc-700 px-3 py-1.5 font-mono text-xs text-zinc-700 dark:text-zinc-300 hover:bg-zinc-200 dark:hover:bg-zinc-700 disabled:opacity-50"
|
className={`rounded border px-3 py-1.5 font-mono text-xs disabled:opacity-50 transition-all duration-300 flex items-center gap-1.5 ${
|
||||||
|
saved
|
||||||
|
? "border-emerald-500/50 bg-emerald-500/10 text-emerald-600 dark:text-emerald-400"
|
||||||
|
: "border-zinc-300 dark:border-zinc-600 bg-zinc-100 dark:bg-zinc-700 text-zinc-700 dark:text-zinc-300 hover:bg-zinc-200 dark:hover:bg-zinc-600"
|
||||||
|
}`}
|
||||||
>
|
>
|
||||||
{saving ? "..." : "save"}
|
{saving ? (
|
||||||
|
<span className="inline-block h-3 w-3 animate-spin rounded-full border border-zinc-400 border-t-transparent" />
|
||||||
|
) : saved ? (
|
||||||
|
<svg className="check-icon h-3 w-3" viewBox="0 0 12 12" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
||||||
|
<polyline points="1.5,6 4.5,9 10.5,3" />
|
||||||
|
</svg>
|
||||||
|
) : null}
|
||||||
|
{saving ? "saving" : saved ? "saved" : "save"}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
|||||||
Reference in New Issue
Block a user