refactor: improve layout spacing and smooth scrolling in calibration views

This commit is contained in:
averel10
2026-03-27 13:18:50 +01:00
parent afcbdc115d
commit 5359685413
6 changed files with 86 additions and 74 deletions

View File

@@ -13,7 +13,7 @@ export default function RootLayout({
<body className="flex flex-col min-h-screen"> <body className="flex flex-col min-h-screen">
<AudioProvider> <AudioProvider>
<Header /> <Header />
<main className="flex-grow"> <main className="flex-grow py-4">
{children} {children}
</main> </main>
<Footer /> <Footer />

View File

@@ -46,7 +46,7 @@ export default async function HomePage() {
); );
return ( return (
<div className="max-w-2xl mx-auto py-4 px-4 sm:px-6 lg:px-8"> <div className="max-w-2xl mx-auto px-4 sm:px-6 lg:px-8">
<h1 className="text-2xl font-bold text-gray-800 mb-2">Experimente</h1> <h1 className="text-2xl font-bold text-gray-800 mb-2">Experimente</h1>
<p className="text-gray-500 mb-8"> <p className="text-gray-500 mb-8">
Wählen Sie ein Experiment aus, um mit der Annotation zu beginnen oder fortzufahren. Wählen Sie ein Experiment aus, um mit der Annotation zu beginnen oder fortzufahren.

View File

@@ -47,6 +47,7 @@ export default function AnnotationPageView({
loadScores(); loadScores();
}, [experimentId]); }, [experimentId]);
const getAnnotationConfig = (dialectLabel: string) => { const getAnnotationConfig = (dialectLabel: string) => {
switch (viewType) { switch (viewType) {
case 'quality-choice': case 'quality-choice':
@@ -118,6 +119,12 @@ export default function AnnotationPageView({
const currentEntry = entries[currentIndex]; const currentEntry = entries[currentIndex];
const isComplete = entries.every((e) => e.annotation !== null); const isComplete = entries.every((e) => e.annotation !== null);
// Smooth scroll to top when current entry changes
useEffect(() => {
window.scrollTo({ top: 0, behavior: 'smooth' });
}, [currentIndex]);
const handlePrevious = () => { const handlePrevious = () => {
if (currentIndex > 0) { if (currentIndex > 0) {
setCurrentIndex((i) => i - 1); setCurrentIndex((i) => i - 1);
@@ -245,7 +252,7 @@ export default function AnnotationPageView({
{/* Main content area */} {/* Main content area */}
<div className="flex-1 flex flex-col overflow-hidden"> <div className="flex-1 flex flex-col overflow-hidden">
{/* Top bar */} {/* Top bar */}
<div className="sticky top-0 z-40 bg-white border-b border-gray-200 p-4"> <div className="sticky top-0 z-40 bg-white border-b border-gray-200 pb-4 px-4">
<div className="flex items-center justify-between max-w-4xl mx-auto"> <div className="flex items-center justify-between max-w-4xl mx-auto">
{/* Toggle and Home buttons */} {/* Toggle and Home buttons */}
<div className="flex gap-2"> <div className="flex gap-2">

View File

@@ -49,6 +49,8 @@ export default function CalibrationEntryView({
setSelectedDialect(existingAnswer?.dialectLabel || null); setSelectedDialect(existingAnswer?.dialectLabel || null);
setSelectedConfidence(existingAnswer?.confidence || null); setSelectedConfidence(existingAnswer?.confidence || null);
setFullyPlayed(existingAnswer ? true : false); setFullyPlayed(existingAnswer ? true : false);
// Scroll to top when entry changes with smooth animation
window.scrollTo({ top: 0, behavior: 'smooth' });
}, [entry, existingAnswer]); }, [entry, existingAnswer]);
return ( return (
@@ -85,79 +87,82 @@ export default function CalibrationEntryView({
</p> </p>
)} )}
{/* Dialect question */} {/* Questions container */}
<div className="mt-6 mb-4"> <div className="mt-6 flex flex-col lg:flex-row gap-6">
<div className="text-sm font-semibold text-gray-700 mb-3"> {/* Dialect question */}
Welcher Dialekt ist es? <div className="flex-1">
</div> <div className="text-sm font-semibold text-gray-700 mb-3">
<div className="flex flex-col gap-2"> Welcher Dialekt ist es?
{Object.entries(DIALECT_LABELS_WITHOUT_DE).map(([dialectKey, dialectName]) => { </div>
const selected = selectedDialect === dialectKey; <div className="flex flex-col gap-2">
const disabled = !fullyPlayed || isSaving; {Object.entries(DIALECT_LABELS_WITHOUT_DE).map(([dialectKey, dialectName]) => {
return ( const selected = selectedDialect === dialectKey;
<button const disabled = !fullyPlayed || isSaving;
key={dialectKey} return (
disabled={disabled} <button
onClick={() => handleDialectChange(dialectKey)} key={dialectKey}
className={`flex items-center gap-3 w-full text-left px-4 py-2.5 rounded-lg border text-sm transition-colors ${ disabled={disabled}
disabled onClick={() => handleDialectChange(dialectKey)}
? 'opacity-40 cursor-not-allowed border-gray-200 bg-gray-50 text-gray-500' className={`flex items-center gap-3 w-full text-left px-4 py-2.5 rounded-lg border text-sm transition-colors ${
: selected disabled
? 'border-blue-500 bg-blue-50 text-blue-800 font-medium' ? 'opacity-40 cursor-not-allowed border-gray-200 bg-gray-50 text-gray-500'
: 'border-gray-200 hover:border-blue-300 hover:bg-blue-50 text-gray-700' : selected
}`} ? 'border-blue-500 bg-blue-50 text-blue-800 font-medium'
> : 'border-gray-200 hover:border-blue-300 hover:bg-blue-50 text-gray-700'
<span
className={`flex-shrink-0 w-6 h-6 rounded-full border-2 flex items-center justify-center text-xs font-bold ${
selected
? 'border-blue-500 bg-blue-500 text-white'
: 'border-gray-300 text-gray-400'
}`} }`}
> >
{selected ? '✓' : ''} <span
</span> className={`flex-shrink-0 w-6 h-6 rounded-full border-2 flex items-center justify-center text-xs font-bold ${
<span>{dialectName}</span> selected
</button> ? 'border-blue-500 bg-blue-500 text-white'
); : 'border-gray-300 text-gray-400'
})} }`}
>
{selected ? '✓' : ''}
</span>
<span>{dialectName}</span>
</button>
);
})}
</div>
</div> </div>
</div>
{/* Confidence question */} {/* Confidence question */}
<div className="mt-6"> <div className="flex-1">
<div className="text-sm font-semibold text-gray-700 mb-3"> <div className="text-sm font-semibold text-gray-700 mb-3">
Wie sicher sind Sie? Wie sicher sind Sie?
</div> </div>
<div className="flex flex-col gap-2"> <div className="flex flex-col gap-2">
{CONFIDENCE_OPTIONS.map(({ value, label }) => { {CONFIDENCE_OPTIONS.map(({ value, label }) => {
const selected = selectedConfidence === value; const selected = selectedConfidence === value;
const disabled = !fullyPlayed || isSaving; const disabled = !fullyPlayed || isSaving;
return ( return (
<button <button
key={value} key={value}
disabled={disabled} disabled={disabled}
onClick={() => handleConfidenceChange(value)} onClick={() => handleConfidenceChange(value)}
className={`flex items-center gap-3 w-full text-left px-4 py-2.5 rounded-lg border text-sm transition-colors ${ className={`flex items-center gap-3 w-full text-left px-4 py-2.5 rounded-lg border text-sm transition-colors ${
disabled disabled
? 'opacity-40 cursor-not-allowed border-gray-200 bg-gray-50 text-gray-500' ? 'opacity-40 cursor-not-allowed border-gray-200 bg-gray-50 text-gray-500'
: selected : selected
? 'border-blue-500 bg-blue-50 text-blue-800 font-medium' ? 'border-blue-500 bg-blue-50 text-blue-800 font-medium'
: 'border-gray-200 hover:border-blue-300 hover:bg-blue-50 text-gray-700' : 'border-gray-200 hover:border-blue-300 hover:bg-blue-50 text-gray-700'
}`}
>
<span
className={`flex-shrink-0 w-6 h-6 rounded-full border-2 flex items-center justify-center text-xs font-bold ${
selected
? 'border-blue-500 bg-blue-500 text-white'
: 'border-gray-300 text-gray-400'
}`} }`}
> >
{selected ? '✓' : ''} <span
</span> className={`flex-shrink-0 w-6 h-6 rounded-full border-2 flex items-center justify-center text-xs font-bold ${
<span>{label}</span> selected
</button> ? 'border-blue-500 bg-blue-500 text-white'
); : 'border-gray-300 text-gray-400'
})} }`}
>
{selected ? '✓' : ''}
</span>
<span>{label}</span>
</button>
);
})}
</div>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -18,7 +18,7 @@ export default function CalibrationInfoView({
<div className="flex-1 flex items-center justify-center px-4"> <div className="flex-1 flex items-center justify-center px-4">
<div className="max-w-2xl w-full"> <div className="max-w-2xl w-full">
{/* Header */} {/* Header */}
<div className="text-center mb-12 mt-8"> <div className="text-center mb-4">
<h1 className="text-3xl font-bold text-gray-900 mb-2">Kalibrierungsphase</h1> <h1 className="text-3xl font-bold text-gray-900 mb-2">Kalibrierungsphase</h1>
</div> </div>

View File

@@ -169,7 +169,7 @@ export default function CalibrationPageView({
<div className="flex flex-col h-full bg-white"> <div className="flex flex-col h-full bg-white">
<div className="flex-1 flex flex-col overflow-hidden"> <div className="flex-1 flex flex-col overflow-hidden">
<div className="flex-1 overflow-y-auto"> <div className="flex-1 overflow-y-auto">
<div className="max-w-3xl mx-auto py-20 px-4"> <div className="max-w-3xl mx-auto px-4">
<div className="text-center mb-12"> <div className="text-center mb-12">
<div className="text-5xl mb-4">🎉</div> <div className="text-5xl mb-4">🎉</div>
<h1 className="text-3xl font-bold text-green-600 mb-3"> <h1 className="text-3xl font-bold text-green-600 mb-3">
@@ -210,7 +210,7 @@ export default function CalibrationPageView({
<div className="flex flex-col h-full bg-white"> <div className="flex flex-col h-full bg-white">
<div className="flex-1 flex flex-col overflow-hidden"> <div className="flex-1 flex flex-col overflow-hidden">
{/* Header with progress */} {/* Header with progress */}
<div className="sticky top-0 z-50 bg-white border-b border-gray-200 px-6 py-4"> <div className="sticky top-0 z-10 bg-white border-b border-gray-200 px-6 pb-4">
<div className="flex items-center justify-between mb-3"> <div className="flex items-center justify-between mb-3">
<h1 className="text-xl font-bold text-gray-800">Kalibrierungsphase</h1> <h1 className="text-xl font-bold text-gray-800">Kalibrierungsphase</h1>
<span className="text-sm text-gray-600"> <span className="text-sm text-gray-600">
@@ -241,7 +241,7 @@ export default function CalibrationPageView({
</div> </div>
{/* Navigation footer */} {/* Navigation footer */}
<div className="sticky bottom-0 bg-white border-t border-gray-200 px-6 py-4 flex gap-3 justify-between"> <div className="sticky bottom-0 bg-white border-t border-gray-200 px-6 pt-4 flex gap-3 justify-between">
<button <button
onClick={handlePrevious} onClick={handlePrevious}
disabled={currentIndex === 0 || isPending} disabled={currentIndex === 0 || isPending}