mirror of
https://github.com/OHTU-German-learning-website/OHTU-German-learning-website.git
synced 2026-10-12 19:13:51 +03:00
Merge pull request #529 from erzabytyci/user-tiers
improve Satzmix exercise
This commit is contained in:
@@ -9,9 +9,21 @@ import { withBasePath } from "@/shared/utils/basePath";
|
||||
import useQuery from "@/shared/hooks/useQuery";
|
||||
import styles from "./jumbled-sentence.module.css";
|
||||
|
||||
function toWordPerLine(value) {
|
||||
function toElementPerLine(value) {
|
||||
if (typeof value !== "string") return "";
|
||||
return value.trim().split(/\s+/).filter(Boolean).join("\n");
|
||||
|
||||
const trimmedValue = value.trim();
|
||||
if (!trimmedValue) return "";
|
||||
|
||||
if (trimmedValue.includes("\n")) {
|
||||
return trimmedValue
|
||||
.split(/\r?\n/)
|
||||
.map((line) => line.trim())
|
||||
.filter(Boolean)
|
||||
.join("\n");
|
||||
}
|
||||
|
||||
return trimmedValue.split(/\s+/).filter(Boolean).join("\n");
|
||||
}
|
||||
|
||||
export default function CreateJumbledSentenceExercise() {
|
||||
@@ -50,10 +62,12 @@ export default function CreateJumbledSentenceExercise() {
|
||||
setSentences(
|
||||
(loadedExercise.sentences || []).length
|
||||
? loadedExercise.sentences.map((item) => ({
|
||||
sentence: toWordPerLine(item.sentence || ""),
|
||||
sentence: toElementPerLine(item.sentence || ""),
|
||||
alternates:
|
||||
item.alternates && item.alternates.length > 0
|
||||
? item.alternates
|
||||
? item.alternates.map((alternate) =>
|
||||
toElementPerLine(alternate)
|
||||
)
|
||||
: [""],
|
||||
alternateFeedbacks:
|
||||
item.alternateFeedbacks &&
|
||||
@@ -67,7 +81,9 @@ export default function CreateJumbledSentenceExercise() {
|
||||
correctSentenceFeedback: item.correctSentenceFeedback || "",
|
||||
incorrectAlternates:
|
||||
item.incorrectAlternates && item.incorrectAlternates.length > 0
|
||||
? item.incorrectAlternates
|
||||
? item.incorrectAlternates.map((alternate) =>
|
||||
toElementPerLine(alternate)
|
||||
)
|
||||
: [""],
|
||||
incorrectFeedbacks:
|
||||
item.incorrectFeedbacks &&
|
||||
@@ -205,10 +221,10 @@ export default function CreateJumbledSentenceExercise() {
|
||||
|
||||
const normalizedSentences = sentences.map((item) => ({
|
||||
...item,
|
||||
sentence: toWordPerLine(item.sentence),
|
||||
sentence: toElementPerLine(item.sentence),
|
||||
alternates: item.alternates
|
||||
.map((alt, index) => ({
|
||||
alt: typeof alt === "string" ? alt.trim() : "",
|
||||
alt: toElementPerLine(alt),
|
||||
feedback:
|
||||
typeof item.alternateFeedbacks?.[index] === "string"
|
||||
? item.alternateFeedbacks[index].trim()
|
||||
@@ -232,7 +248,7 @@ export default function CreateJumbledSentenceExercise() {
|
||||
: "",
|
||||
incorrectAlternates: (item.incorrectAlternates || [])
|
||||
.map((alt, index) => ({
|
||||
alt: typeof alt === "string" ? alt.trim() : "",
|
||||
alt: toElementPerLine(alt),
|
||||
feedback:
|
||||
typeof item.incorrectFeedbacks?.[index] === "string"
|
||||
? item.incorrectFeedbacks[index].trim()
|
||||
@@ -346,15 +362,20 @@ export default function CreateJumbledSentenceExercise() {
|
||||
</label>
|
||||
<div>
|
||||
<div className={styles.sectionTitle}>Alternativ-Sätze:</div>
|
||||
<div className={styles.sentenceHint}>
|
||||
Schreiben Sie auch hier jedes Element in eine neue Zeile.
|
||||
</div>
|
||||
{s.alternates.map((alt, altIdx) => (
|
||||
<div key={altIdx} className={styles.alternateGroup}>
|
||||
<div className={styles.alternateRow}>
|
||||
<input
|
||||
<textarea
|
||||
value={alt}
|
||||
onChange={(e) =>
|
||||
handleAlternateChange(idx, altIdx, e.target.value)
|
||||
}
|
||||
className={`${styles.fieldInput} ${styles.alternateInput}`}
|
||||
className={`${styles.fieldInput} ${styles.sentenceTextarea}`}
|
||||
rows={4}
|
||||
placeholder="Jeden Tag\nlerne ich\nDeutsch"
|
||||
/>
|
||||
<Button
|
||||
type="button"
|
||||
@@ -410,10 +431,13 @@ export default function CreateJumbledSentenceExercise() {
|
||||
<div className={styles.sectionTitle}>
|
||||
Alternativ-Sätze (Falsche):
|
||||
</div>
|
||||
<div className={styles.sentenceHint}>
|
||||
Schreiben Sie auch hier jedes Element in eine neue Zeile.
|
||||
</div>
|
||||
{s.incorrectAlternates.map((alt, altIdx) => (
|
||||
<div key={altIdx} className={styles.alternateGroup}>
|
||||
<div className={styles.alternateRowNoGapTop}>
|
||||
<input
|
||||
<textarea
|
||||
value={alt}
|
||||
onChange={(e) =>
|
||||
handleIncorrectAlternateChange(
|
||||
@@ -422,8 +446,9 @@ export default function CreateJumbledSentenceExercise() {
|
||||
e.target.value
|
||||
)
|
||||
}
|
||||
className={`${styles.fieldInput} ${styles.alternateInput}`}
|
||||
placeholder="Falsche Alternative (optional)"
|
||||
className={`${styles.fieldInput} ${styles.sentenceTextarea}`}
|
||||
rows={4}
|
||||
placeholder="Deutsch\nlerne ich\njeden Tag"
|
||||
/>
|
||||
<Button
|
||||
type="button"
|
||||
|
||||
@@ -16,11 +16,11 @@ describe("admin jumbled sentence exercises API", () => {
|
||||
title: "Satzreihenfolge A1",
|
||||
sentences: [
|
||||
{
|
||||
sentence: "Ich lerne jeden Tag Deutsch",
|
||||
alternates: ["Jeden Tag lerne ich Deutsch"],
|
||||
sentence: "Ich\nlerne jeden Tag\nDeutsch",
|
||||
alternates: ["lerne jeden Tag\nIch\nDeutsch"],
|
||||
alternateFeedbacks: ["Auch richtig."],
|
||||
correctSentenceFeedback: "Perfekt!",
|
||||
incorrectAlternates: ["Deutsch ich lerne jeden Tag"],
|
||||
incorrectAlternates: ["Deutsch\nIch\nlerne jeden Tag"],
|
||||
incorrectFeedbacks: ["Subjekt und Verb bitte prüfen."],
|
||||
},
|
||||
],
|
||||
|
||||
@@ -33,8 +33,23 @@ export default function JumbledSentenceExercisePage() {
|
||||
const cleanTokenForComparison = (token) =>
|
||||
stripIgnoredPunctuation(token).toLowerCase();
|
||||
|
||||
const splitElements = (value) => {
|
||||
const trimmedValue = value.trim();
|
||||
|
||||
if (!trimmedValue) return [];
|
||||
|
||||
if (trimmedValue.includes("\n")) {
|
||||
return trimmedValue
|
||||
.split(/\r?\n/)
|
||||
.map((element) => element.trim())
|
||||
.filter(Boolean);
|
||||
}
|
||||
|
||||
return trimmedValue.split(/\s+/).filter(Boolean);
|
||||
};
|
||||
|
||||
const tokenizeSentenceForDisplay = (value) =>
|
||||
value.trim().split(/\s+/).map(stripIgnoredPunctuation).filter(Boolean);
|
||||
splitElements(value).map(stripIgnoredPunctuation).filter(Boolean);
|
||||
|
||||
const normalizeSentence = (value) =>
|
||||
tokenizeSentenceForDisplay(value)
|
||||
|
||||
@@ -11,8 +11,23 @@ function cleanToken(token) {
|
||||
return token.replace(/[\p{P}]/gu, "").toLowerCase();
|
||||
}
|
||||
|
||||
function normalizeTokens(sentence) {
|
||||
return sentence.trim().split(/\s+/).map(cleanToken).filter(Boolean).sort();
|
||||
function splitElements(sentence) {
|
||||
const trimmedSentence = sentence.trim();
|
||||
|
||||
if (!trimmedSentence) return [];
|
||||
|
||||
if (trimmedSentence.includes("\n")) {
|
||||
return trimmedSentence
|
||||
.split(/\r?\n/)
|
||||
.map((element) => element.trim())
|
||||
.filter(Boolean);
|
||||
}
|
||||
|
||||
return trimmedSentence.split(/\s+/).filter(Boolean);
|
||||
}
|
||||
|
||||
function normalizeElements(sentence) {
|
||||
return splitElements(sentence).map(cleanToken).filter(Boolean).sort();
|
||||
}
|
||||
|
||||
function sameWordSet(a, b) {
|
||||
@@ -52,7 +67,7 @@ export const jumbledSentenceSchema = z
|
||||
}, z.array(z.string())),
|
||||
})
|
||||
.superRefine((data, ctx) => {
|
||||
const baseTokens = normalizeTokens(data.sentence);
|
||||
const baseTokens = normalizeElements(data.sentence);
|
||||
|
||||
if (data.alternateFeedbacks.length !== data.alternates.length) {
|
||||
ctx.addIssue({
|
||||
@@ -64,7 +79,7 @@ export const jumbledSentenceSchema = z
|
||||
|
||||
for (let index = 0; index < data.alternates.length; index++) {
|
||||
const alternate = data.alternates[index];
|
||||
const alternateTokens = normalizeTokens(alternate);
|
||||
const alternateTokens = normalizeElements(alternate);
|
||||
|
||||
if (!sameWordSet(baseTokens, alternateTokens)) {
|
||||
ctx.addIssue({
|
||||
@@ -78,7 +93,7 @@ export const jumbledSentenceSchema = z
|
||||
|
||||
for (let index = 0; index < data.incorrectAlternates.length; index++) {
|
||||
const incorrectAlternate = data.incorrectAlternates[index];
|
||||
const incorrectTokens = normalizeTokens(incorrectAlternate);
|
||||
const incorrectTokens = normalizeElements(incorrectAlternate);
|
||||
|
||||
if (!sameWordSet(baseTokens, incorrectTokens)) {
|
||||
ctx.addIssue({
|
||||
|
||||
Reference in New Issue
Block a user