Merge branch 'main' into annotation-admin-extension
This commit is contained in:
@@ -82,7 +82,7 @@ function calculateDialectScores(
|
|||||||
if (!answer) continue;
|
if (!answer) continue;
|
||||||
|
|
||||||
const isCorrect = answer.dialectLabel === item.dialectLabel ? 1 : -1;
|
const isCorrect = answer.dialectLabel === item.dialectLabel ? 1 : -1;
|
||||||
const confidence = answer.confidence / 5; // Normalize to 0-1
|
const confidence = answer.confidence / 4; // Normalize to 0-1
|
||||||
|
|
||||||
// Score = correctness * confidence
|
// Score = correctness * confidence
|
||||||
// Correct answers: +confidence (0 to 1)
|
// Correct answers: +confidence (0 to 1)
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ export default async function HomePage() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="max-w-2xl mx-auto 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 mt-4">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.
|
||||||
</p>
|
</p>
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import CalibrationScoresModal from '@/components/CalibrationScoresModal';
|
|||||||
|
|
||||||
export interface EntryViewProps {
|
export interface EntryViewProps {
|
||||||
entry: DatasetEntryForAnnotation;
|
entry: DatasetEntryForAnnotation;
|
||||||
|
index: number;
|
||||||
onSave: (rating: number) => Promise<void>;
|
onSave: (rating: number) => Promise<void>;
|
||||||
isSaving: boolean;
|
isSaving: boolean;
|
||||||
ratingOptions?: { value: number; label: JSX.Element | string }[];
|
ratingOptions?: { value: number; label: JSX.Element | string }[];
|
||||||
@@ -67,7 +68,7 @@ export default function AnnotationPageView({
|
|||||||
{ value: 4, label: `Klingt eher nach ${DIALECT_LABELS[dialectLabel]}` },
|
{ value: 4, label: `Klingt eher nach ${DIALECT_LABELS[dialectLabel]}` },
|
||||||
{ value: 5, label: `Klingt eindeutig nach ${DIALECT_LABELS[dialectLabel]}` },
|
{ value: 5, label: `Klingt eindeutig nach ${DIALECT_LABELS[dialectLabel]}` },
|
||||||
],
|
],
|
||||||
question: <div>Wie authentisch klingt dieses Sample nach dem Dialekt der Region <span className="text-blue-600">{DIALECT_LABELS[dialectLabel]}</span>?</div>,
|
question: <div>Wie authentisch klingt diese Aufnahme nach dem Dialekt der Region <span className="text-blue-600">{DIALECT_LABELS[dialectLabel]}</span>?</div>,
|
||||||
};
|
};
|
||||||
case 'binary':
|
case 'binary':
|
||||||
return {
|
return {
|
||||||
@@ -76,7 +77,7 @@ export default function AnnotationPageView({
|
|||||||
{ value: 2, label: 'Unklar' },
|
{ value: 2, label: 'Unklar' },
|
||||||
{ value: 1, label: 'Nein' },
|
{ value: 1, label: 'Nein' },
|
||||||
],
|
],
|
||||||
question: <div>Klingt dieses Sample nach dem Dialekt der Region <span className="text-blue-600">{DIALECT_LABELS[dialectLabel]}</span>?</div>,
|
question: <div>Klingt diese Aufnahme nach dem Dialekt der Region <span className="text-blue-600">{DIALECT_LABELS[dialectLabel]}</span>?</div>,
|
||||||
};
|
};
|
||||||
default:
|
default:
|
||||||
return {
|
return {
|
||||||
@@ -94,6 +95,7 @@ export default function AnnotationPageView({
|
|||||||
return (
|
return (
|
||||||
<SingleChoiceEntryView
|
<SingleChoiceEntryView
|
||||||
entry={currentEntry}
|
entry={currentEntry}
|
||||||
|
index={currentIndex}
|
||||||
onSave={handleSaveEntry}
|
onSave={handleSaveEntry}
|
||||||
isSaving={isPending}
|
isSaving={isPending}
|
||||||
ratingOptions={config.ratingOptions}
|
ratingOptions={config.ratingOptions}
|
||||||
@@ -104,6 +106,7 @@ export default function AnnotationPageView({
|
|||||||
return (
|
return (
|
||||||
<SingleChoiceBinaryEntryView
|
<SingleChoiceBinaryEntryView
|
||||||
entry={currentEntry}
|
entry={currentEntry}
|
||||||
|
index={currentIndex}
|
||||||
onSave={handleSaveEntry}
|
onSave={handleSaveEntry}
|
||||||
isSaving={isPending}
|
isSaving={isPending}
|
||||||
ratingOptions={config.ratingOptions}
|
ratingOptions={config.ratingOptions}
|
||||||
@@ -260,10 +263,10 @@ 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 pb-4 px-4">
|
<div className="sticky top-1 z-40 bg-white border-b border-gray-200 pb-5 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-3">
|
||||||
<button
|
<button
|
||||||
onClick={() => setSidebarOpen(!sidebarOpen)}
|
onClick={() => setSidebarOpen(!sidebarOpen)}
|
||||||
className="hidden md:block text-sm px-3 py-1.5 bg-gray-100 hover:bg-gray-200 text-gray-700 rounded-md transition-colors"
|
className="hidden md:block text-sm px-3 py-1.5 bg-gray-100 hover:bg-gray-200 text-gray-700 rounded-md transition-colors"
|
||||||
@@ -287,7 +290,7 @@ export default function AnnotationPageView({
|
|||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-3">
|
||||||
<button
|
<button
|
||||||
onClick={handlePrevious}
|
onClick={handlePrevious}
|
||||||
disabled={currentIndex === 0 || isPending}
|
disabled={currentIndex === 0 || isPending}
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ export default function AnnotationSidebarNavigation({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<p className="text-xs text-gray-600">
|
<p className="text-xs text-gray-600">
|
||||||
{annotatedCount}/{entries.length} annotiert ({progressPct}%)
|
{annotatedCount}/{entries.length} bewertet ({progressPct}%)
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -47,7 +47,7 @@ export default function AnnotationSidebarNavigation({
|
|||||||
{/* Entries List - scrollable middle section with max height */}
|
{/* Entries List - scrollable middle section with max height */}
|
||||||
<div className="space-y-1 flex-1 min-h-0 overflow-hidden flex flex-col">
|
<div className="space-y-1 flex-1 min-h-0 overflow-hidden flex flex-col">
|
||||||
<h3 className="text-xs font-semibold text-gray-700 uppercase tracking-wide mb-3 px-2 flex-shrink-0">
|
<h3 className="text-xs font-semibold text-gray-700 uppercase tracking-wide mb-3 px-2 flex-shrink-0">
|
||||||
Samples
|
Aufnahmen
|
||||||
</h3>
|
</h3>
|
||||||
<div className="space-y-1 overflow-y-auto max-h-150">
|
<div className="space-y-1 overflow-y-auto max-h-150">
|
||||||
{entries.map((entry, index) => (
|
{entries.map((entry, index) => (
|
||||||
@@ -77,7 +77,7 @@ export default function AnnotationSidebarNavigation({
|
|||||||
/>
|
/>
|
||||||
{/* Entry number and filename */}
|
{/* Entry number and filename */}
|
||||||
<span className="truncate">
|
<span className="truncate">
|
||||||
{`Sample ${index + 1} - ${DIALECT_LABELS[entry.dialect]}`}
|
{`Aufnahme ${index + 1} - ${DIALECT_LABELS[entry.dialect]}`}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ const AUTO_ADVANCE_DELAY_MS = 600;
|
|||||||
|
|
||||||
export default function SingleChoiceBinaryEntryView({
|
export default function SingleChoiceBinaryEntryView({
|
||||||
entry,
|
entry,
|
||||||
|
index,
|
||||||
onSave,
|
onSave,
|
||||||
isSaving,
|
isSaving,
|
||||||
ratingOptions,
|
ratingOptions,
|
||||||
@@ -39,14 +40,14 @@ export default function SingleChoiceBinaryEntryView({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`border rounded-xl p-5 bg-white shadow-sm transition-colors duration-300 ${
|
className={`border rounded-xl p-5 bg-white shadow-sm transition-colors duration-300 mt-4 ${
|
||||||
fullyPlayed && answer !== null ? 'border-green-300' : 'border-gray-200'
|
fullyPlayed && answer !== null ? 'border-green-300' : 'border-gray-200'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{/* Sample header */}
|
{/* Sample header */}
|
||||||
<div className="flex items-center justify-between mb-3">
|
<div className="flex items-center justify-between mb-3">
|
||||||
<span className="text-xs font-medium text-gray-400 uppercase tracking-wide">
|
<span className="text-xs font-medium text-gray-400 uppercase tracking-wide">
|
||||||
Sample - {entry.externalId}
|
Aufnahme {index + 1}
|
||||||
</span>
|
</span>
|
||||||
{fullyPlayed && (
|
{fullyPlayed && (
|
||||||
<span className="text-xs text-green-600 font-medium">
|
<span className="text-xs text-green-600 font-medium">
|
||||||
@@ -64,14 +65,14 @@ export default function SingleChoiceBinaryEntryView({
|
|||||||
|
|
||||||
{/* Must-listen hint */}
|
{/* Must-listen hint */}
|
||||||
{!fullyPlayed && (
|
{!fullyPlayed && (
|
||||||
<p className="text-xs text-amber-600 mt-2">
|
<p className="text-xs text-gray-400 mt-2">
|
||||||
Bitte das Sample vollständig anhören, bevor Sie eine Bewertung abgeben.
|
Bitte die Aufnahme vollständig anhören, bevor du eine Bewertung abgibst.
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Dialect + question */}
|
{/* Dialect + question */}
|
||||||
<div className="mt-4 mb-3">
|
<div className="mt-4 mb-3">
|
||||||
<div className="text-sm font-semibold text-gray-700">
|
<div className="text-s font-semibold text-gray-700">
|
||||||
{question}
|
{question}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -99,6 +100,11 @@ export default function SingleChoiceBinaryEntryView({
|
|||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Footnote */}
|
||||||
|
<p className="text-xs text-gray-400 mt-8 pt-3 border-t border-gray-100">
|
||||||
|
Vertrau deinem ersten Eindruck – klingt die Aufnahme authentisch nach diesem Dialekt? Es gibt keine richtige oder falsche Antwort.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ const AUTO_ADVANCE_DELAY_MS = 600;
|
|||||||
|
|
||||||
export default function SingleChoiceEntryView({
|
export default function SingleChoiceEntryView({
|
||||||
entry,
|
entry,
|
||||||
|
index,
|
||||||
onSave,
|
onSave,
|
||||||
isSaving,
|
isSaving,
|
||||||
ratingOptions,
|
ratingOptions,
|
||||||
@@ -40,14 +41,14 @@ export default function SingleChoiceEntryView({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className={`border rounded-xl p-5 bg-white shadow-sm transition-colors duration-300 ${
|
className={`border rounded-xl p-5 bg-white shadow-sm transition-colors duration-300 mt-4 ${
|
||||||
fullyPlayed && answer !== null ? 'border-green-300' : 'border-gray-200'
|
fullyPlayed && answer !== null ? 'border-green-300' : 'border-gray-200'
|
||||||
}`}
|
}`}
|
||||||
>
|
>
|
||||||
{/* Sample header */}
|
{/* Sample header */}
|
||||||
<div className="flex items-center justify-between mb-3">
|
<div className="flex items-center justify-between mb-3">
|
||||||
<span className="text-xs font-medium text-gray-400 uppercase tracking-wide">
|
<span className="text-xs font-medium text-gray-400 uppercase tracking-wide">
|
||||||
Sample - {entry.externalId}
|
Aufnahme {index + 1}
|
||||||
</span>
|
</span>
|
||||||
{fullyPlayed && (
|
{fullyPlayed && (
|
||||||
<span className="text-xs text-green-600 font-medium">
|
<span className="text-xs text-green-600 font-medium">
|
||||||
@@ -65,14 +66,14 @@ export default function SingleChoiceEntryView({
|
|||||||
|
|
||||||
{/* Must-listen hint */}
|
{/* Must-listen hint */}
|
||||||
{!fullyPlayed && (
|
{!fullyPlayed && (
|
||||||
<p className="text-xs text-amber-600 mt-2">
|
<p className="text-xs text-gray-400 mt-2">
|
||||||
Bitte das Sample vollständig anhören, bevor Sie eine Bewertung abgeben.
|
Bitte die Aufnahme vollständig anhören, bevor du eine Bewertung abgibst.
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Dialect + question */}
|
{/* Dialect + question */}
|
||||||
<div className="mt-4 mb-3">
|
<div className="mt-4 mb-3">
|
||||||
<div className="text-sm font-semibold text-gray-700">
|
<div className="text-s font-semibold text-gray-700">
|
||||||
{question}
|
{question}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -109,6 +110,11 @@ export default function SingleChoiceEntryView({
|
|||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Footnote */}
|
||||||
|
<p className="text-xs text-gray-400 mt-8 pt-3 border-t border-gray-100">
|
||||||
|
Vertrau deinem ersten Eindruck – klingt die Aufnahme authentisch nach diesem Dialekt? Es gibt keine richtige oder falsche Antwort.
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,9 +26,7 @@ export default function CalibrationScoresDisplay({
|
|||||||
<div key={dialect}>
|
<div key={dialect}>
|
||||||
<div className="flex justify-between items-center mb-2">
|
<div className="flex justify-between items-center mb-2">
|
||||||
<span className="font-medium text-gray-800">{DIALECT_LABELS[dialect]}</span>
|
<span className="font-medium text-gray-800">{DIALECT_LABELS[dialect]}</span>
|
||||||
<span className={`text-sm font-semibold text-blue-600`}>
|
|
||||||
{(score * 100).toFixed(0)}%
|
|
||||||
</span>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="w-full bg-gray-200 rounded-full h-2 overflow-hidden">
|
<div className="w-full bg-gray-200 rounded-full h-2 overflow-hidden">
|
||||||
<div
|
<div
|
||||||
@@ -41,8 +39,7 @@ export default function CalibrationScoresDisplay({
|
|||||||
})}
|
})}
|
||||||
</div>
|
</div>
|
||||||
<p className="text-sm text-gray-500 mt-6">
|
<p className="text-sm text-gray-500 mt-6">
|
||||||
Die Scores basieren auf der Genauigkeit und dem Vertrauen in Ihre Kalibrierergebnisse.
|
Die Ergebnisse basieren auf deiner Erkennungsgenauigkeit und Sicherheit. Dialekte die du gut erkennst, werden bei der Bewertungsphase häufiger angezeigt.
|
||||||
Dialekte mit höheren Scores werden zuerst in der Annotation angezeigt.
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ export default function CalibrationScoresModal({
|
|||||||
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50">
|
||||||
<div className="bg-white rounded-lg shadow-lg max-w-2xl w-full mx-4 max-h-96 overflow-y-auto">
|
<div className="bg-white rounded-lg shadow-lg max-w-2xl w-full mx-4 max-h-96 overflow-y-auto">
|
||||||
<div className="flex justify-between items-center p-6 border-b border-gray-200 sticky top-0 bg-white">
|
<div className="flex justify-between items-center p-6 border-b border-gray-200 sticky top-0 bg-white">
|
||||||
<h2 className="text-xl font-bold">Ihre Kalibrierungsergebnisse</h2>
|
<h2 className="text-xl font-bold">Deine Kalibrierungsergebnisse</h2>
|
||||||
<button
|
<button
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
className="text-gray-500 hover:text-gray-700"
|
className="text-gray-500 hover:text-gray-700"
|
||||||
|
|||||||
@@ -16,9 +16,8 @@ interface CalibrationEntryViewProps {
|
|||||||
const CONFIDENCE_OPTIONS = [
|
const CONFIDENCE_OPTIONS = [
|
||||||
{ value: 1, label: 'Sehr unsicher' },
|
{ value: 1, label: 'Sehr unsicher' },
|
||||||
{ value: 2, label: 'Eher unsicher' },
|
{ value: 2, label: 'Eher unsicher' },
|
||||||
{ value: 3, label: 'Neutral' },
|
{ value: 3, label: 'Eher sicher' },
|
||||||
{ value: 4, label: 'Eher sicher' },
|
{ value: 4, label: 'Sehr sicher' },
|
||||||
{ value: 5, label: 'Sehr sicher' },
|
|
||||||
];
|
];
|
||||||
|
|
||||||
export default function CalibrationEntryView({
|
export default function CalibrationEntryView({
|
||||||
@@ -70,7 +69,7 @@ export default function CalibrationEntryView({
|
|||||||
{/* Sample header */}
|
{/* Sample header */}
|
||||||
<div className="flex items-center justify-between mb-3">
|
<div className="flex items-center justify-between mb-3">
|
||||||
<span className="text-xs font-medium text-gray-400 uppercase tracking-wide">
|
<span className="text-xs font-medium text-gray-400 uppercase tracking-wide">
|
||||||
Kalibrierungssample - {entry.order}
|
Aufnahme {entry.order}
|
||||||
</span>
|
</span>
|
||||||
{fullyPlayed && (
|
{fullyPlayed && (
|
||||||
<span className="text-xs text-green-600 font-medium">
|
<span className="text-xs text-green-600 font-medium">
|
||||||
@@ -88,8 +87,8 @@ export default function CalibrationEntryView({
|
|||||||
|
|
||||||
{/* Must-listen hint */}
|
{/* Must-listen hint */}
|
||||||
{!fullyPlayed && (
|
{!fullyPlayed && (
|
||||||
<p className="text-xs text-amber-600 mt-2">
|
<p className="text-xs text-gray-400 mt-2">
|
||||||
Bitte das Sample vollständig anhören, bevor Sie eine Bewertung abgeben.
|
Bitte die Aufnahme vollständig anhören, bevor du eine Bewertung abgibst.
|
||||||
</p>
|
</p>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
@@ -98,7 +97,7 @@ export default function CalibrationEntryView({
|
|||||||
{/* Dialect question */}
|
{/* Dialect question */}
|
||||||
<div className="flex-1">
|
<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">
|
||||||
Welcher Dialekt ist es?
|
Welchen Dialekt hörst du?
|
||||||
</div>
|
</div>
|
||||||
<div className="flex flex-col gap-2">
|
<div className="flex flex-col gap-2">
|
||||||
{Object.entries(DIALECT_LABELS_WITHOUT_DE).map(([dialectKey, dialectName]) => {
|
{Object.entries(DIALECT_LABELS_WITHOUT_DE).map(([dialectKey, dialectName]) => {
|
||||||
@@ -136,7 +135,7 @@ export default function CalibrationEntryView({
|
|||||||
{/* Confidence question */}
|
{/* Confidence question */}
|
||||||
<div className="flex-1">
|
<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 bist du?
|
||||||
</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 }) => {
|
||||||
|
|||||||
@@ -25,21 +25,17 @@ export default function CalibrationInfoView({
|
|||||||
return (
|
return (
|
||||||
<div className="flex flex-col h-full bg-white">
|
<div className="flex flex-col h-full bg-white">
|
||||||
<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-[700px] w-full">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="text-center mb-4">
|
<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-8 mt-4">Kalibrierungsphase</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Info Section */}
|
{/* Info Section */}
|
||||||
<div className="bg-blue-50 border border-blue-200 rounded-lg p-8 mb-8">
|
<div className="bg-blue-50 border border-blue-200 rounded-lg p-8 mb-8">
|
||||||
<h2 className="text-xl font-semibold text-gray-900 mb-4">Was ist die Kalibrierungsphase?</h2>
|
<h2 className="text-xl font-semibold text-gray-900 mb-4">Was ist die Kalibrierungsphase?</h2>
|
||||||
<p className="text-gray-700 mb-4">
|
<p className="text-gray-700 mb-4">
|
||||||
Die Kalibrierungsphase dient dazu, Ihre individuellen Stärken und Schwächen bei der Dialekterkennung zu verstehen. Sie hören verschiedene Audiosamples und geben an, welchen Dialekt Sie hören und wie sicher Sie sich dabei sind.
|
In der Kalibrierungsphase hörst du echte Dialektaufnahmen und gibst an, welchen Dialekt du erkennst und wie sicher du dir dabei bist. So können wir deine Dialektkenntnisse besser einschätzen und deine späteren Bewertungen entsprechend gewichten.
|
||||||
</p>
|
|
||||||
<p className="text-gray-700 mb-4">
|
|
||||||
Dies hilft uns, Ihre Annotationsergebnisse später besser zu bewerten und zu
|
|
||||||
verstehen. Die Annotationen werden auf Basis Ihrer Kalibrierungsergebnisse gewichtet, sodass Ihre Stärken stärker berücksichtigt werden.
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -51,38 +47,38 @@ export default function CalibrationInfoView({
|
|||||||
<span className="flex-shrink-0 w-6 h-6 bg-blue-600 text-white rounded-full flex items-center justify-center text-sm font-semibold">
|
<span className="flex-shrink-0 w-6 h-6 bg-blue-600 text-white rounded-full flex items-center justify-center text-sm font-semibold">
|
||||||
1
|
1
|
||||||
</span>
|
</span>
|
||||||
<span>Sie hören ein Audiosample</span>
|
<span>Du hörst eine Audioaufnahme</span>
|
||||||
</li>
|
</li>
|
||||||
<li className="flex gap-3">
|
<li className="flex gap-3">
|
||||||
<span className="flex-shrink-0 w-6 h-6 bg-blue-600 text-white rounded-full flex items-center justify-center text-sm font-semibold">
|
<span className="flex-shrink-0 w-6 h-6 bg-blue-600 text-white rounded-full flex items-center justify-center text-sm font-semibold">
|
||||||
2
|
2
|
||||||
</span>
|
</span>
|
||||||
<span>Sie wählen den Dialekt aus, den Sie gehört haben</span>
|
<span>Du wählst den Dialekt aus, den du gehört hast</span>
|
||||||
</li>
|
</li>
|
||||||
<li className="flex gap-3">
|
<li className="flex gap-3">
|
||||||
<span className="flex-shrink-0 w-6 h-6 bg-blue-600 text-white rounded-full flex items-center justify-center text-sm font-semibold">
|
<span className="flex-shrink-0 w-6 h-6 bg-blue-600 text-white rounded-full flex items-center justify-center text-sm font-semibold">
|
||||||
3
|
3
|
||||||
</span>
|
</span>
|
||||||
<span>Sie bewerten Ihr Vertrauen in die Identifikation (von 1-5)</span>
|
<span>Du gibst an, wie sicher du dir bei deiner Wahl bist</span>
|
||||||
</li>
|
</li>
|
||||||
<li className="flex gap-3">
|
<li className="flex gap-3">
|
||||||
<span className="flex-shrink-0 w-6 h-6 bg-blue-600 text-white rounded-full flex items-center justify-center text-sm font-semibold">
|
<span className="flex-shrink-0 w-6 h-6 bg-blue-600 text-white rounded-full flex items-center justify-center text-sm font-semibold">
|
||||||
4
|
4
|
||||||
</span>
|
</span>
|
||||||
<span>Sie wiederholen dies für alle Audiosamples</span>
|
<span>Dies wiederholst du für alle Aufnahmen</span>
|
||||||
</li>
|
</li>
|
||||||
</ol>
|
</ol>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Important Note */}
|
|
||||||
<div className="bg-amber-50 border border-amber-200 rounded-lg p-6 mb-8">
|
{/* Participation Section */}
|
||||||
<h4 className="font-semibold text-amber-900 mb-2">ℹ️ Wichtig</h4>
|
<div className="mb-8">
|
||||||
<p className="text-amber-800 text-sm">
|
<p className="text-gray-700 mb-4">
|
||||||
Sie können diese Seite jederzeit verlassen und später zurückkehren, um Ihre
|
Die Kalibrierung umfasst 28 Aufnahmen und dauert etwa 5–10 Minuten.
|
||||||
Kalibrierung fortzusetzen. Ihre Antworten werden automatisch gespeichert.
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
{/* Status */}
|
{/* Status */}
|
||||||
{hasExistingAnswers && (
|
{hasExistingAnswers && (
|
||||||
<div className="bg-green-50 border border-green-200 rounded-lg p-6 mb-8">
|
<div className="bg-green-50 border border-green-200 rounded-lg p-6 mb-8">
|
||||||
|
|||||||
@@ -170,22 +170,21 @@ export default function CalibrationPageView({
|
|||||||
<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 px-4">
|
<div className="max-w-3xl mx-auto px-4">
|
||||||
<div className="text-center mb-12">
|
<div className="text-center mb-12 mt-4">
|
||||||
<div className="text-5xl mb-4">🎉</div>
|
<h1 className="text-3xl font-bold text-gray-900 mb-4">
|
||||||
<h1 className="text-3xl font-bold text-green-600 mb-3">
|
|
||||||
Kalibrierung abgeschlossen!
|
Kalibrierung abgeschlossen!
|
||||||
</h1>
|
</h1>
|
||||||
<p className="text-gray-600 mb-8">
|
<p className="text-gray-600 mb-8">
|
||||||
Sie haben alle Kalibrierungssamples bewertet. Vielen Dank! Sie können jetzt mit der
|
Du hast alle Aufnahmen bewertet. Vielen Dank! Du kannst jetzt mit der Bewertung beginnen.
|
||||||
Annotation beginnen.
|
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
{/* Dialect Scores Summary */}
|
{/* Dialect Scores Summary */}
|
||||||
{sortedScores.length > 0 && (
|
{sortedScores.length > 0 && (
|
||||||
<div className="mb-12">
|
<div className="mb-12">
|
||||||
<h2 className="text-lg font-semibold text-gray-900 mb-6">Ihre Kalibrierungsergebnisse</h2>
|
<h2 className="text-lg font-semibold text-gray-900 mb-4">Deine Kalibrierungsergebnisse</h2>
|
||||||
|
|
||||||
<CalibrationScoresDisplay dialectScores={dialectScores} />
|
<CalibrationScoresDisplay dialectScores={dialectScores} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
@@ -194,9 +193,9 @@ export default function CalibrationPageView({
|
|||||||
<button
|
<button
|
||||||
onClick={handleCompleteCalibration}
|
onClick={handleCompleteCalibration}
|
||||||
disabled={isPending}
|
disabled={isPending}
|
||||||
className="px-6 py-3 bg-blue-600 hover:bg-blue-700 disabled:bg-gray-400 text-white font-semibold rounded-lg transition-colors"
|
className="px-6 py-3 bg-blue-600 hover:bg-blue-700 disabled:bg-gray-400 text-white font-semibold rounded-lg transition-colors text-lg"
|
||||||
>
|
>
|
||||||
{isPending ? 'Wird gespeichert...' : 'Zur Annotation'}
|
{isPending ? 'Wird gespeichert...' : 'Zur Bewertung'}
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { DIALECT_LABELS_WITHOUT_DE } from '@/lib/dialects';
|
|||||||
|
|
||||||
interface OnboardingFormViewProps {
|
interface OnboardingFormViewProps {
|
||||||
experimentId: number;
|
experimentId: number;
|
||||||
|
onBack: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface ResidenceEntry {
|
interface ResidenceEntry {
|
||||||
@@ -16,6 +17,7 @@ interface ResidenceEntry {
|
|||||||
|
|
||||||
interface FormData {
|
interface FormData {
|
||||||
age?: string;
|
age?: string;
|
||||||
|
email?: string;
|
||||||
residences?: ResidenceEntry[];
|
residences?: ResidenceEntry[];
|
||||||
dialectQualities?: Record<string, string>;
|
dialectQualities?: Record<string, string>;
|
||||||
listeningExperience?: string;
|
listeningExperience?: string;
|
||||||
@@ -24,22 +26,18 @@ interface FormData {
|
|||||||
|
|
||||||
type Step = 1 | 2 | 3;
|
type Step = 1 | 2 | 3;
|
||||||
|
|
||||||
export default function OnboardingFormView({ experimentId }: OnboardingFormViewProps) {
|
export default function OnboardingFormView({ experimentId, onBack }: OnboardingFormViewProps) {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const [isPending, startTransition] = useTransition();
|
const [isPending, startTransition] = useTransition();
|
||||||
const [currentStep, setCurrentStep] = useState<Step>(1);
|
const [currentStep, setCurrentStep] = useState<Step>(1);
|
||||||
|
|
||||||
// Initialize dialect qualities with default value of '3' for each dialect
|
|
||||||
const initializeDialectQualities = (): Record<string, string> => {
|
const initializeDialectQualities = (): Record<string, string> => {
|
||||||
const qualities: Record<string, string> = {};
|
return {};
|
||||||
Object.keys(DIALECT_LABELS_WITHOUT_DE).forEach(key => {
|
|
||||||
qualities[key] = '3';
|
|
||||||
});
|
|
||||||
return qualities;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const [formData, setFormData] = useState<FormData>({
|
const [formData, setFormData] = useState<FormData>({
|
||||||
age: '',
|
age: '',
|
||||||
|
email: '',
|
||||||
residences: [{ region: '', years: '' }],
|
residences: [{ region: '', years: '' }],
|
||||||
dialectQualities: initializeDialectQualities()
|
dialectQualities: initializeDialectQualities()
|
||||||
});
|
});
|
||||||
@@ -116,10 +114,10 @@ export default function OnboardingFormView({ experimentId }: OnboardingFormViewP
|
|||||||
case 3:
|
case 3:
|
||||||
const allDialects = Object.keys(DIALECT_LABELS_WITHOUT_DE);
|
const allDialects = Object.keys(DIALECT_LABELS_WITHOUT_DE);
|
||||||
const hasAllDialectQualities = allDialects.every(
|
const hasAllDialectQualities = allDialects.every(
|
||||||
(region) => formData.dialectQualities?.[region]
|
(region) => !!formData.dialectQualities?.[region]
|
||||||
);
|
);
|
||||||
if (!hasAllDialectQualities) {
|
if (!hasAllDialectQualities) {
|
||||||
setError('Bitte bewerten Sie Ihre Fähigkeit für alle Dialekte.');
|
setError('Bitte bewerte deine Fähigkeit für alle Dialekte.');
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
@@ -138,7 +136,9 @@ export default function OnboardingFormView({ experimentId }: OnboardingFormViewP
|
|||||||
|
|
||||||
const handlePrevious = () => {
|
const handlePrevious = () => {
|
||||||
setError(null);
|
setError(null);
|
||||||
if (currentStep > 1) {
|
if (currentStep === 1) {
|
||||||
|
onBack();
|
||||||
|
} else {
|
||||||
setCurrentStep((currentStep - 1) as Step);
|
setCurrentStep((currentStep - 1) as Step);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -200,8 +200,9 @@ export default function OnboardingFormView({ experimentId }: OnboardingFormViewP
|
|||||||
|
|
||||||
{/* Step 1: Persönliche Informationen */}
|
{/* Step 1: Persönliche Informationen */}
|
||||||
{currentStep === 1 && (
|
{currentStep === 1 && (
|
||||||
|
<div className="space-y-6">
|
||||||
<div>
|
<div>
|
||||||
<label htmlFor="age" className="block text-sm font-medium text-gray-900 mb-2">
|
<label htmlFor="age" className="block text-s font-medium text-gray-900 mb-2">
|
||||||
Altersgruppe <span className="text-red-600">*</span>
|
Altersgruppe <span className="text-red-600">*</span>
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
@@ -217,16 +218,32 @@ export default function OnboardingFormView({ experimentId }: OnboardingFormViewP
|
|||||||
<option value="36-45">36-45 Jahre</option>
|
<option value="36-45">36-45 Jahre</option>
|
||||||
<option value="46-55">46-55 Jahre</option>
|
<option value="46-55">46-55 Jahre</option>
|
||||||
<option value="56-65">56-65 Jahre</option>
|
<option value="56-65">56-65 Jahre</option>
|
||||||
<option value="66+">66+ Jahre</option>
|
<option value="66+">Über 65 Jahre</option>
|
||||||
</select>
|
</select>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className="border-t border-gray-200 pt-6">
|
||||||
|
<label htmlFor="email" className="block text-s font-medium text-gray-900 mb-1">
|
||||||
|
E-Mail-Adresse (optional)
|
||||||
|
</label>
|
||||||
|
<p className="text-sm text-gray-500 mb-2">Wird nur für die Gutschein-Verlosung verwendet.</p>
|
||||||
|
<input
|
||||||
|
type="email"
|
||||||
|
id="email"
|
||||||
|
value={formData.email}
|
||||||
|
onChange={(e) => handleChange('email', e.target.value)}
|
||||||
|
placeholder="beispiel@email.ch"
|
||||||
|
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-transparent"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Step 2: Aufenthaltsorte */}
|
{/* Step 2: Aufenthaltsorte */}
|
||||||
{currentStep === 2 && (
|
{currentStep === 2 && (
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
<p className="text-sm text-gray-600 mb-4">
|
<p className="text-s text-gray-600 mb-8">
|
||||||
Bitte geben Sie die Regionen an, in denen Sie länger als 1 Jahr gelebt haben, sowie die Anzahl der Jahre. Diese Informationen helfen uns, Ihre Dialektkenntnisse besser einzuschätzen.
|
Bitte gib die Regionen an, in denen du länger als 1 Jahr gelebt hast, sowie die Anzahl der Jahre. So können wir deine Dialektkenntnisse besser einschätzen.
|
||||||
</p>
|
</p>
|
||||||
{/* Map Placeholder */}
|
{/* Map Placeholder */}
|
||||||
<div className="bg-gray-100 border-2 border-dashed border-gray-300 rounded-lg p-12 flex items-center justify-center min-h-[300px]">
|
<div className="bg-gray-100 border-2 border-dashed border-gray-300 rounded-lg p-12 flex items-center justify-center min-h-[300px]">
|
||||||
@@ -238,8 +255,8 @@ export default function OnboardingFormView({ experimentId }: OnboardingFormViewP
|
|||||||
|
|
||||||
{/* Residence Entries */}
|
{/* Residence Entries */}
|
||||||
<div>
|
<div>
|
||||||
<label className="block text-sm font-medium text-gray-900 mb-4">
|
<label className="block text-s font-medium text-gray-900 mb-4">
|
||||||
Aufenthaltsorte, die länger als 1 Jahr waren <span className="text-red-600">*</span>
|
Aufenthaltsorte (mind. 1 Jahr) <span className="text-red-600">*</span>
|
||||||
</label>
|
</label>
|
||||||
|
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
@@ -310,62 +327,71 @@ export default function OnboardingFormView({ experimentId }: OnboardingFormViewP
|
|||||||
{/* Step 3: Selbsteinschätzung */}
|
{/* Step 3: Selbsteinschätzung */}
|
||||||
{currentStep === 3 && (
|
{currentStep === 3 && (
|
||||||
<div className="space-y-6">
|
<div className="space-y-6">
|
||||||
<div>
|
<p className="text-s text-gray-600 mb-8">
|
||||||
<p className="text-sm text-gray-600 mb-6">
|
Bewerte deine Fähigkeit, die folgenden Dialekte zu erkennen. Diese Selbsteinschätzung hilft uns, deine Kalibrierungsergebnisse besser zu interpretieren.
|
||||||
Bewerten Sie Ihre Fähigkeit, die folgenden Dialekte zu identifizieren, auf einer Skala von "sehr schlecht" bis "sehr gut". Diese Selbsteinschätzung hilft uns, Ihre nachfolgendenKalibrierungsergebnisse besser zu interpretieren.
|
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div className="space-y-5">
|
<ul className="text-s text-gray-600 mb-8 space-y-1">
|
||||||
{Object.entries(DIALECT_LABELS_WITHOUT_DE).map(([key, label]) => {
|
<li><strong>1</strong> = Sehr schlecht</li>
|
||||||
const qualityLabels: Record<string, string> = {
|
<li><strong>2</strong> = Schlecht</li>
|
||||||
'1': 'Sehr schlecht',
|
<li><strong>3</strong> = Gut</li>
|
||||||
'2': 'Schlecht',
|
<li><strong>4</strong> = Sehr gut</li>
|
||||||
'3': 'Weder noch',
|
<li><strong>N</strong> = Nicht vertraut</li>
|
||||||
'4': 'Gut',
|
</ul>
|
||||||
'5': 'Sehr gut',
|
|
||||||
};
|
<table className="w-full border-collapse">
|
||||||
const currentValue = formData.dialectQualities?.[key] || '3';
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th className="text-sm font-medium text-gray-700 text-left pb-2"></th>
|
||||||
|
<th className="w-[120px] text-xs font-medium text-gray-700 text-center pb-2">1</th>
|
||||||
|
<th className="w-[120px] text-xs font-medium text-gray-700 text-center pb-2">2</th>
|
||||||
|
<th className="w-[120px] text-xs font-medium text-gray-700 text-center pb-2">3</th>
|
||||||
|
<th className="w-[120px] text-xs font-medium text-gray-700 text-center pb-2">4</th>
|
||||||
|
<th className="w-[120px] text-xs font-medium text-gray-700 text-center pb-2 border-l border-gray-200">N</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{Object.entries(DIALECT_LABELS_WITHOUT_DE).map(([key, label], index) => {
|
||||||
|
const currentValue = formData.dialectQualities?.[key] ?? '';
|
||||||
return (
|
return (
|
||||||
<div key={key}>
|
<tr key={key} className={index % 2 === 0 ? 'bg-gray-50' : ''}>
|
||||||
<div className="flex justify-between items-center mb-2">
|
<td className="text-sm font-medium text-gray-900 py-3">{label}</td>
|
||||||
<label className="text-sm font-medium text-gray-900">
|
{['1', '2', '3', '4'].map((value) => (
|
||||||
{label}
|
<td key={value} className="w-[120px] text-center py-3">
|
||||||
</label>
|
|
||||||
<span className="text-sm font-semibold text-blue-600 bg-blue-50 px-3 py-1 rounded-full">
|
|
||||||
{qualityLabels[currentValue]}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
<input
|
<input
|
||||||
type="range"
|
type="radio"
|
||||||
min="1"
|
name={key}
|
||||||
max="5"
|
value={value}
|
||||||
step="1"
|
checked={currentValue === value}
|
||||||
value={currentValue}
|
onChange={() => handleDialectQualityChange(key, value)}
|
||||||
onChange={(e) =>
|
className="cursor-pointer accent-blue-600 w-5 h-5"
|
||||||
handleDialectQualityChange(key, e.target.value)
|
|
||||||
}
|
|
||||||
className="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer accent-blue-600"
|
|
||||||
/>
|
/>
|
||||||
</div>
|
</td>
|
||||||
<div className="flex justify-between text-xs text-gray-600 mt-2 font-medium">
|
))}
|
||||||
<span>Sehr schlecht</span>
|
<td className="w-[120px] text-center py-3 border-l border-gray-200">
|
||||||
<span>Sehr gut</span>
|
<input
|
||||||
</div>
|
type="radio"
|
||||||
</div>
|
name={key}
|
||||||
|
value="-1"
|
||||||
|
checked={currentValue === '-1'}
|
||||||
|
onChange={() => handleDialectQualityChange(key, '-1')}
|
||||||
|
className="cursor-pointer accent-blue-600 w-5 h-5"
|
||||||
|
/>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
</div>
|
</tbody>
|
||||||
</div>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
{/* Navigation Buttons */}
|
{/* Navigation Buttons */}
|
||||||
<div className="flex gap-4 justify-between pt-8">
|
<div className="flex gap-4 justify-between pt-8 mb-8">
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
onClick={handlePrevious}
|
onClick={handlePrevious}
|
||||||
disabled={currentStep === 1 || isPending}
|
disabled={isPending}
|
||||||
className="px-6 py-3 bg-gray-300 hover:bg-gray-400 disabled:bg-gray-200 text-gray-800 font-semibold rounded-lg transition-colors"
|
className="px-6 py-3 bg-gray-300 hover:bg-gray-400 disabled:bg-gray-200 text-gray-800 font-semibold rounded-lg transition-colors"
|
||||||
>
|
>
|
||||||
← Zurück
|
← Zurück
|
||||||
@@ -387,18 +413,13 @@ export default function OnboardingFormView({ experimentId }: OnboardingFormViewP
|
|||||||
disabled={isPending}
|
disabled={isPending}
|
||||||
className="px-8 py-3 bg-green-600 hover:bg-green-700 disabled:bg-green-400 text-white font-semibold rounded-lg transition-colors"
|
className="px-8 py-3 bg-green-600 hover:bg-green-700 disabled:bg-green-400 text-white font-semibold rounded-lg transition-colors"
|
||||||
>
|
>
|
||||||
{isPending ? 'Wird gespeichert...' : 'Abschließen'}
|
{isPending ? 'Wird gespeichert...' : 'Abschliessen'}
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
{/* Info */}
|
|
||||||
<div className="bg-blue-50 border border-blue-200 rounded-lg p-4 mt-8">
|
|
||||||
<p className="text-blue-800 text-sm">
|
|
||||||
Die mit <span className="text-red-600">*</span> gekennzeichneten Felder sind erforderlich.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -16,54 +16,59 @@ export default function OnboardingInfoView({
|
|||||||
return (
|
return (
|
||||||
<div className="flex flex-col h-full bg-white">
|
<div className="flex flex-col h-full bg-white">
|
||||||
<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-[700px] w-full">
|
||||||
{/* Header */}
|
{/* Header */}
|
||||||
<div className="text-center mb-4">
|
<div className="text-center mb-8 mt-4">
|
||||||
<h1 className="text-3xl font-bold text-gray-900 mb-2">Willkommen</h1>
|
<h1 className="text-3xl font-bold text-gray-900 mb-2">Willkommen und vielen Dank für deine Teilnahme!</h1>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Info Section */}
|
{/* Info Section */}
|
||||||
<div className="bg-blue-50 border border-blue-200 rounded-lg p-8 mb-8">
|
<div className="bg-blue-50 border border-blue-200 rounded-lg p-8 mb-8">
|
||||||
<h2 className="text-xl font-semibold text-gray-900 mb-4">Was ist das Ziel?</h2>
|
<h2 className="text-xl font-semibold text-gray-900 mb-4">Worum geht es?</h2>
|
||||||
<p className="text-gray-700 mb-4">
|
<p className="text-gray-700 mb-4">
|
||||||
Vielen Dank, dass Sie an diesem Experiment teilnehmen. Bevor Sie mit der Annotation beginnen, möchten wir gerne ein paar grundlegende Informationen über Sie und Ihre Erfahrung mit Dialekten sammeln.
|
Im Rahmen unserer Bachelorarbeit an der ZHAW untersuchen wir, wie authentisch ein KI-Modell schweizerdeutsche Dialekte synthetisieren kann. Dazu sind wir auf deine Einschätzung angewiesen.
|
||||||
</p>
|
</p>
|
||||||
<p className="text-gray-700 mb-4">
|
<p className="text-gray-700 mb-4">
|
||||||
Diese Informationen helfen uns, Ihre Annotationsergebnisse besser zu verstehen und zu bewerten. Alle Angaben werden vertraulich behandelt.
|
Deine Aufgabe ist es, kurze Audioaufnahmen anzuhören und zu bewerten, wie authentisch sie nach einem bestimmten Dialekt klingen. Es gibt dabei keine richtigen oder falschen Antworten – wichtig ist deine persönliche Einschätzung.
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Instructions Section */}
|
{/* Instructions Section */}
|
||||||
<div className="mb-8">
|
<div className="mb-8">
|
||||||
<h3 className="text-lg font-semibold text-gray-900 mb-4">Wie funktioniert es?</h3>
|
<h3 className="text-lg font-semibold text-gray-900 mb-4">So läuft die Teilnahme ab:</h3>
|
||||||
<ol className="space-y-3 text-gray-700">
|
<ol className="space-y-3 text-gray-700">
|
||||||
<li className="flex gap-3">
|
<li className="flex gap-3">
|
||||||
<span className="flex-shrink-0 w-6 h-6 bg-blue-600 text-white rounded-full flex items-center justify-center text-sm font-semibold">
|
<span className="flex-shrink-0 w-6 h-6 bg-blue-600 text-white rounded-full flex items-center justify-center text-sm font-semibold">
|
||||||
1
|
1
|
||||||
</span>
|
</span>
|
||||||
<span>Sie beantworten ein kurzes Umfrage-Formular</span>
|
<span>Du beantwortest zuerst ein paar kurze Fragen zu dir und deiner Erfahrung mit Schweizer Dialekten</span>
|
||||||
</li>
|
</li>
|
||||||
<li className="flex gap-3">
|
<li className="flex gap-3">
|
||||||
<span className="flex-shrink-0 w-6 h-6 bg-blue-600 text-white rounded-full flex items-center justify-center text-sm font-semibold">
|
<span className="flex-shrink-0 w-6 h-6 bg-blue-600 text-white rounded-full flex items-center justify-center text-sm font-semibold">
|
||||||
2
|
2
|
||||||
</span>
|
</span>
|
||||||
<span>Die Antworten werden gespeichert</span>
|
<span>Danach folgt eine kurze Kalibrierungsphase mit echten Dialektaufnahmen</span>
|
||||||
</li>
|
</li>
|
||||||
<li className="flex gap-3">
|
<li className="flex gap-3">
|
||||||
<span className="flex-shrink-0 w-6 h-6 bg-blue-600 text-white rounded-full flex items-center justify-center text-sm font-semibold">
|
<span className="flex-shrink-0 w-6 h-6 bg-blue-600 text-white rounded-full flex items-center justify-center text-sm font-semibold">
|
||||||
3
|
3
|
||||||
</span>
|
</span>
|
||||||
<span>Danach geht es zur Kalibrierungsphase</span>
|
<span>Anschliessend beginnt die eigentliche Bewertung der synthetischen Aufnahmen</span>
|
||||||
</li>
|
|
||||||
<li className="flex gap-3">
|
|
||||||
<span className="flex-shrink-0 w-6 h-6 bg-blue-600 text-white rounded-full flex items-center justify-center text-sm font-semibold">
|
|
||||||
4
|
|
||||||
</span>
|
|
||||||
<span>Anschließend können Sie mit der Annotation beginnen</span>
|
|
||||||
</li>
|
</li>
|
||||||
</ol>
|
</ol>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
{/* Participation Section */}
|
||||||
|
<div className="mb-8">
|
||||||
|
<p className="text-gray-700 mb-4">
|
||||||
|
Du entscheidest selbst, wie viele Aufnahmen du bewertest. Die gesamte Teilnahme inklusive Formular, Kalibrierung und 50 bewerteten Aufnahmen dauert etwa 30 Minuten. Unter allen Teilnehmenden, die 50 oder mehr Aufnahmen bewertet haben, <strong>verlosen wir 3x [Platzhalter]-Gutscheine im Wert von je [x].- CHF!</strong>
|
||||||
|
</p>
|
||||||
|
<p className="text-gray-700 mb-4">
|
||||||
|
Alle Angaben werden anonym ausgewertet und ausschliesslich zu Forschungszwecken verwendet.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
{/* Status */}
|
{/* Status */}
|
||||||
{hasExistingAnswers && (
|
{hasExistingAnswers && (
|
||||||
<div className="bg-green-50 border border-green-200 rounded-lg p-6 mb-8">
|
<div className="bg-green-50 border border-green-200 rounded-lg p-6 mb-8">
|
||||||
@@ -77,9 +82,9 @@ export default function OnboardingInfoView({
|
|||||||
<div className="flex justify-center">
|
<div className="flex justify-center">
|
||||||
<button
|
<button
|
||||||
onClick={handleContinue}
|
onClick={handleContinue}
|
||||||
className="px-8 py-3 bg-blue-600 hover:bg-blue-700 text-white font-semibold rounded-lg transition-colors"
|
className="px-8 py-3 bg-blue-600 hover:bg-blue-700 text-white font-semibold rounded-lg transition-colors text-lg"
|
||||||
>
|
>
|
||||||
Weiter zum Formular
|
Jetzt starten
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -69,5 +69,5 @@ export default function OnboardingPhase({ experimentId }: OnboardingPhaseProps)
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return <OnboardingFormView experimentId={experimentId} />;
|
return <OnboardingFormView experimentId={experimentId} onBack={() => { scrollToTop(); setShowInfoPage(true); }} />;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user