diff --git a/scripts/merge-youtube-data.mjs b/scripts/merge-youtube-data.mjs
index c9a46cd..da58b10 100644
--- a/scripts/merge-youtube-data.mjs
+++ b/scripts/merge-youtube-data.mjs
@@ -10,7 +10,7 @@ for (const exam of Object.values(tables.exams)) {
metadata.youtube = findVideo(technique);
}
}
- result[exam.labelKey] = exam;
+ result[exam.id] = exam;
}
console.log(JSON.stringify(result, 0, 2));
diff --git a/src/components/solid/organisms/TechniqueChooser/TechniqueChooser.test.tsx b/src/components/solid/organisms/TechniqueChooser/TechniqueChooser.test.tsx
index b551172..c13d8d4 100644
--- a/src/components/solid/organisms/TechniqueChooser/TechniqueChooser.test.tsx
+++ b/src/components/solid/organisms/TechniqueChooser/TechniqueChooser.test.tsx
@@ -15,9 +15,24 @@ describe("Chooser.test.tsx", async () => {
const dojo = createResolvedDojo({
details: createDojoDetails({
exams: [
- createExam({ labelKey: "chooser.button.kyu5" }),
- createExam({ labelKey: "chooser.button.kyu4" }),
- createExam({ labelKey: "chooser.button.kyu3" }),
+ createExam({
+ label: {
+ type: "wellknown",
+ key: "kyu5",
+ },
+ }),
+ createExam({
+ label: {
+ type: "wellknown",
+ key: "kyu4",
+ },
+ }),
+ createExam({
+ label: {
+ type: "wellknown",
+ key: "kyu3",
+ },
+ }),
],
}),
});
@@ -30,7 +45,14 @@ describe("Chooser.test.tsx", async () => {
it("clicking an exam selects it", async () => {
const dojo = createResolvedDojo({
details: createDojoDetails({
- exams: [createExam({ labelKey: "chooser.button.kyu5" })],
+ exams: [
+ createExam({
+ label: {
+ type: "wellknown",
+ key: "kyu5",
+ },
+ }),
+ ],
}),
});
render(() => );
@@ -44,7 +66,14 @@ describe("Chooser.test.tsx", async () => {
it("clicking an selected exam deselects it", async () => {
const dojo = createResolvedDojo({
details: createDojoDetails({
- exams: [createExam({ labelKey: "chooser.button.kyu5" })],
+ exams: [
+ createExam({
+ label: {
+ type: "wellknown",
+ key: "kyu5",
+ },
+ }),
+ ],
}),
});
render(() => );
@@ -60,7 +89,10 @@ describe("Chooser.test.tsx", async () => {
details: createDojoDetails({
exams: [
createExam({
- labelKey: "chooser.button.kyu5",
+ label: {
+ type: "wellknown",
+ key: "kyu5",
+ },
techniques: {
"suwari waza": {
"ai hanmi katate dori": {
@@ -86,7 +118,10 @@ describe("Chooser.test.tsx", async () => {
details: createDojoDetails({
exams: [
createExam({
- labelKey: "chooser.button.kyu5",
+ label: {
+ type: "wellknown",
+ key: "kyu5",
+ },
techniques: {
"suwari waza": {
"ai hanmi katate dori": {
@@ -108,7 +143,10 @@ describe("Chooser.test.tsx", async () => {
details: createDojoDetails({
exams: [
createExam({
- labelKey: "chooser.button.kyu5",
+ label: {
+ type: "wellknown",
+ key: "kyu5",
+ },
techniques: {
"hanmi handachi waza": {
"ai hanmi katate dori": {
@@ -138,7 +176,10 @@ describe("Chooser.test.tsx", async () => {
details: createDojoDetails({
exams: [
createExam({
- labelKey: "chooser.button.kyu5",
+ label: {
+ type: "wellknown",
+ key: "kyu5",
+ },
techniques: {
"hanmi handachi waza": {
"ai hanmi katate dori": {
@@ -163,8 +204,20 @@ describe("Chooser.test.tsx", async () => {
const dojo = createResolvedDojo({
details: createDojoDetails({
exams: [
- createExam({ id: "kyu5", labelKey: "chooser.button.kyu5" }),
- createExam({ id: "kyu4", labelKey: "chooser.button.kyu4" }),
+ createExam({
+ id: "kyu5",
+ label: {
+ type: "wellknown",
+ key: "kyu5",
+ },
+ }),
+ createExam({
+ id: "kyu4",
+ label: {
+ type: "wellknown",
+ key: "kyu4",
+ },
+ }),
],
}),
});
@@ -186,8 +239,20 @@ describe("Chooser.test.tsx", async () => {
info: createDojoInfo({ id }),
details: createDojoDetails({
exams: [
- createExam({ id: "kyu5", labelKey: "chooser.button.kyu5" }),
- createExam({ id: "kyu4", labelKey: "chooser.button.kyu4" }),
+ createExam({
+ id: "kyu5",
+ label: {
+ type: "wellknown",
+ key: "kyu5",
+ },
+ }),
+ createExam({
+ id: "kyu4",
+ label: {
+ type: "wellknown",
+ key: "kyu4",
+ },
+ }),
],
}),
});
@@ -211,7 +276,15 @@ describe("Chooser.test.tsx", async () => {
it("renders filters", () => {
const dojo = createResolvedDojo({
details: createDojoDetails({
- exams: [createExam({ id: "kyu5", labelKey: "chooser.button.kyu5" })],
+ exams: [
+ createExam({
+ id: "kyu5",
+ label: {
+ type: "wellknown",
+ key: "kyu5",
+ },
+ }),
+ ],
}),
});
renderSolid(() => );
@@ -225,7 +298,10 @@ describe("Chooser.test.tsx", async () => {
exams: [
createExam({
id: "kyu5",
- labelKey: "chooser.button.kyu5",
+ label: {
+ type: "wellknown",
+ key: "kyu5",
+ },
techniques: {
"suwari waza": { "ai hanmi katate dori": { ikkyo: { omote: {} } } },
"hanmi handachi waza": { "ai hanmi katate dori": { ikkyo: { omote: {} } } },
diff --git a/src/components/solid/organisms/TechniqueChooser/TechniqueChooser.tsx b/src/components/solid/organisms/TechniqueChooser/TechniqueChooser.tsx
index 73581a8..6ded3cf 100644
--- a/src/components/solid/organisms/TechniqueChooser/TechniqueChooser.tsx
+++ b/src/components/solid/organisms/TechniqueChooser/TechniqueChooser.tsx
@@ -1,13 +1,13 @@
import { type Component, createDeferred, createSignal } from "solid-js";
import type { ResolvedDojo } from "$core/model/Dojo.ts";
-import { t } from "@/i18n";
+import { t, tx } from "@/i18n";
import { l } from "astro-i18n";
import { ExamSheet } from "@/components/solid/organisms/TechniqueChooser/ExamSheet.tsx";
import {
ChooserControlButtons,
type Option,
} from "@/components/solid/organisms/TechniqueChooser/ChooserControlButtons.tsx";
-import type { Exam } from "$core/model";
+import type { Exam, ExamLabel, WellKnownExam } from "$core/model";
import { syncToStorage } from "@/components/solid/hooks/syncToStorage.ts";
import { isServer } from "solid-js/web";
import { ChooserControlContainer } from "@/components/solid/organisms/TechniqueChooser/ChooserControlContainer.tsx";
@@ -21,6 +21,7 @@ import { SimpleButton } from "@/components/solid/atoms/SimpleButton.tsx";
import { IconPrint, IconSpeak } from "@/icons";
import { createTechniqueStore } from "$core/store";
import { LinkButton } from "@/components/solid/atoms/LinkButton.tsx";
+import type { TranslationSchema } from "@/i18n/TranslationSchema.ts";
export const TechniqueChooser: Component<{ dojo: ResolvedDojo }> = (props) => {
const [examSelection, setExamSelection] = syncToStorage(createSignal(new Set()), {
@@ -115,5 +116,26 @@ export const TechniqueChooser: Component<{ dojo: ResolvedDojo }> = (props) => {
};
function examsToOptions(exams: Exam[]): Option[] {
- return exams.map((exam) => ({ id: exam.id, label: t(exam.labelKey) }));
+ return exams.map((exam) => ({ id: exam.id, label: resoveExamLabel(exam.label) }));
+}
+
+const wellknownLabels: Record = {
+ kyu5: "chooser.button.kyu5",
+ kyu4: "chooser.button.kyu4",
+ kyu3: "chooser.button.kyu3",
+ kyu2: "chooser.button.kyu2",
+ kyu1: "chooser.button.kyu1",
+ dan1: "chooser.button.dan1",
+ dan2: "chooser.button.dan2",
+ dan3: "chooser.button.dan3",
+ dan4: "chooser.button.dan4",
+};
+
+function resoveExamLabel(label: ExamLabel): string {
+ switch (label.type) {
+ case "wellknown":
+ return t(wellknownLabels[label.key]);
+ case "free":
+ return tx(label.text);
+ }
}
diff --git a/src/core/model/Dojo.test-helper.ts b/src/core/model/Dojo.test-helper.ts
index ba5847b..6395029 100644
--- a/src/core/model/Dojo.test-helper.ts
+++ b/src/core/model/Dojo.test-helper.ts
@@ -12,9 +12,24 @@ export function createDojoInfo(partialInfo: Partial = {}): DojoInfo {
export function createExams() {
return [
- createExam({ labelKey: "chooser.button.kyu5" }),
- createExam({ labelKey: "chooser.button.kyu4" }),
- createExam({ labelKey: "chooser.button.kyu3" }),
+ createExam({
+ label: {
+ type: "wellknown",
+ key: "kyu5",
+ },
+ }),
+ createExam({
+ label: {
+ type: "wellknown",
+ key: "kyu4",
+ },
+ }),
+ createExam({
+ label: {
+ type: "wellknown",
+ key: "kyu3",
+ },
+ }),
];
}
diff --git a/src/core/model/Exam.test-helper.ts b/src/core/model/Exam.test-helper.ts
index 32ba228..714eb40 100644
--- a/src/core/model/Exam.test-helper.ts
+++ b/src/core/model/Exam.test-helper.ts
@@ -4,7 +4,10 @@ import { nanoid } from "nanoid";
export function createExam(exam: Partial): Exam {
return {
id: nanoid(),
- labelKey: "chooser.button.kyu5",
+ label: {
+ type: "wellknown",
+ key: "kyu5",
+ },
techniques: {},
...exam,
};
diff --git a/src/core/model/Exam.ts b/src/core/model/Exam.ts
index fd30da6..54c9d6d 100644
--- a/src/core/model/Exam.ts
+++ b/src/core/model/Exam.ts
@@ -3,15 +3,25 @@ import type { TechniqueMetadata } from "./TechniqueMetadata";
import type { Defence } from "./Defence";
import type { Attack } from "./Attack";
import type { Execution } from "./Execution";
-import type { TranslationSchema } from "./TranslationSchema";
+import type { TranslatedText } from "$core/model/Dojo.ts";
export type Directions = Partial>;
export type Defences = Partial>;
export type Attacks = Partial>;
export type Table = Partial>;
+export type WellKnownExam = "kyu5" | "kyu4" | "kyu3" | "kyu2" | "kyu1" | "dan1" | "dan2" | "dan3" | "dan4";
+export type ExamLabel =
+ | {
+ type: "wellknown";
+ key: WellKnownExam;
+ }
+ | {
+ type: "free";
+ text: TranslatedText;
+ };
export interface Exam {
id: string;
- labelKey: keyof TranslationSchema;
+ label: ExamLabel;
techniques: Table;
}
diff --git a/src/core/resolveExamTables/resolveExamTables.test.ts b/src/core/resolveExamTables/resolveExamTables.test.ts
index 867161a..7291a99 100644
--- a/src/core/resolveExamTables/resolveExamTables.test.ts
+++ b/src/core/resolveExamTables/resolveExamTables.test.ts
@@ -6,7 +6,10 @@ describe("resolve-exam-tables", () => {
const actual = resolveExamTables([
{
id: "dan2",
- labelKey: "chooser.button.dan2",
+ label: {
+ type: "wellknown",
+ key: "dan2",
+ },
techniques: {
"suwari waza": {
"ryote dori": {
@@ -23,7 +26,10 @@ describe("resolve-exam-tables", () => {
},
{
id: "dan3",
- labelKey: "chooser.button.dan3",
+ label: {
+ type: "wellknown",
+ key: "dan3",
+ },
techniques: {
"tachi waza": {
"ai hanmi katate dori": {
diff --git a/src/data/dojos/aikido-dojo-darmstadt/exams/additional.ts b/src/data/dojos/aikido-dojo-darmstadt/exams/additional.ts
index 3775869..9421a4b 100644
--- a/src/data/dojos/aikido-dojo-darmstadt/exams/additional.ts
+++ b/src/data/dojos/aikido-dojo-darmstadt/exams/additional.ts
@@ -2,7 +2,12 @@ import type { Exam } from "$core/model";
export const additional: Exam = {
id: "additional",
- labelKey: "chooser.button.additional",
+ label: {
+ type: "free",
+ text: {
+ en: "+X",
+ },
+ },
techniques: {
"tachi waza": {
"chudan tsuki": {
diff --git a/src/data/dojos/aikido-dojo-darmstadt/exams/dan1.ts b/src/data/dojos/aikido-dojo-darmstadt/exams/dan1.ts
index 585d567..2038d88 100644
--- a/src/data/dojos/aikido-dojo-darmstadt/exams/dan1.ts
+++ b/src/data/dojos/aikido-dojo-darmstadt/exams/dan1.ts
@@ -2,7 +2,10 @@ import type { Exam } from "$core/model";
export const dan1: Exam = {
id: "dan1",
- labelKey: "chooser.button.dan1",
+ label: {
+ type: "wellknown",
+ key: "dan1",
+ },
techniques: {
"hanmi handachi waza": {
"gyuako hanmi katate dori": {
diff --git a/src/data/dojos/aikido-dojo-darmstadt/exams/kyu1.ts b/src/data/dojos/aikido-dojo-darmstadt/exams/kyu1.ts
index 6f787cc..00f13ef 100644
--- a/src/data/dojos/aikido-dojo-darmstadt/exams/kyu1.ts
+++ b/src/data/dojos/aikido-dojo-darmstadt/exams/kyu1.ts
@@ -2,7 +2,10 @@ import type { Exam } from "$core/model";
export const kyu1: Exam = {
id: "kyu1",
- labelKey: "chooser.button.kyu1",
+ label: {
+ type: "wellknown",
+ key: "kyu1",
+ },
techniques: {
"suwari waza": {
"yokomen uchi": {
diff --git a/src/data/dojos/aikido-dojo-darmstadt/exams/kyu2.ts b/src/data/dojos/aikido-dojo-darmstadt/exams/kyu2.ts
index b4abc64..c3a61e8 100644
--- a/src/data/dojos/aikido-dojo-darmstadt/exams/kyu2.ts
+++ b/src/data/dojos/aikido-dojo-darmstadt/exams/kyu2.ts
@@ -2,7 +2,10 @@ import type { Exam } from "$core/model/Exam";
export const kyu2: Exam = {
id: "kyu2",
- labelKey: "chooser.button.kyu2",
+ label: {
+ type: "wellknown",
+ key: "kyu2",
+ },
techniques: {
"suwari waza": {
"yokomen uchi": {
diff --git a/src/data/dojos/aikido-dojo-darmstadt/exams/kyu3.ts b/src/data/dojos/aikido-dojo-darmstadt/exams/kyu3.ts
index c7e62f1..0cb59f7 100644
--- a/src/data/dojos/aikido-dojo-darmstadt/exams/kyu3.ts
+++ b/src/data/dojos/aikido-dojo-darmstadt/exams/kyu3.ts
@@ -2,7 +2,10 @@ import type { Exam } from "$core/model/Exam";
export const kyu3: Exam = {
id: "kyu3",
- labelKey: "chooser.button.kyu3",
+ label: {
+ type: "wellknown",
+ key: "kyu3",
+ },
techniques: {
"suwari waza": {
"gyuako hanmi katate dori": {
diff --git a/src/data/dojos/aikido-dojo-darmstadt/exams/kyu4.ts b/src/data/dojos/aikido-dojo-darmstadt/exams/kyu4.ts
index 26dcb82..9f39cf1 100644
--- a/src/data/dojos/aikido-dojo-darmstadt/exams/kyu4.ts
+++ b/src/data/dojos/aikido-dojo-darmstadt/exams/kyu4.ts
@@ -2,7 +2,10 @@ import type { Exam } from "$core/model/Exam";
export const kyu4: Exam = {
id: "kyu4",
- labelKey: "chooser.button.kyu4",
+ label: {
+ type: "wellknown",
+ key: "kyu4",
+ },
techniques: {
"suwari waza": {
"ai hanmi katate dori": {
diff --git a/src/data/dojos/aikido-dojo-darmstadt/exams/kyu5.ts b/src/data/dojos/aikido-dojo-darmstadt/exams/kyu5.ts
index 984692a..72d2445 100644
--- a/src/data/dojos/aikido-dojo-darmstadt/exams/kyu5.ts
+++ b/src/data/dojos/aikido-dojo-darmstadt/exams/kyu5.ts
@@ -2,7 +2,10 @@ import type { Exam } from "$core/model/Exam";
export const kyu5: Exam = {
id: "kyu5",
- labelKey: "chooser.button.kyu5",
+ label: {
+ type: "wellknown",
+ key: "kyu5",
+ },
techniques: {
"suwari waza": {
"ryote dori": {
diff --git a/src/data/dojos/aikido-foederation/exams/additional.ts b/src/data/dojos/aikido-foederation/exams/additional.ts
index 6a01b25..cf27fa6 100644
--- a/src/data/dojos/aikido-foederation/exams/additional.ts
+++ b/src/data/dojos/aikido-foederation/exams/additional.ts
@@ -2,7 +2,12 @@ import type { Exam } from "$core/model";
export const additional: Exam = {
id: "additional",
- labelKey: "chooser.button.additional",
+ label: {
+ type: "free",
+ text: {
+ en: "+X",
+ },
+ },
techniques: {
"tachi waza": {
"chudan tsuki": {
diff --git a/src/data/dojos/aikido-foederation/exams/dan1.ts b/src/data/dojos/aikido-foederation/exams/dan1.ts
index 6db04e0..2b4068f 100644
--- a/src/data/dojos/aikido-foederation/exams/dan1.ts
+++ b/src/data/dojos/aikido-foederation/exams/dan1.ts
@@ -2,7 +2,10 @@ import type { Exam } from "$core/model";
export const dan1: Exam = {
id: "dan1",
- labelKey: "chooser.button.dan1",
+ label: {
+ type: "wellknown",
+ key: "dan1",
+ },
techniques: {
"tanto dori": {
"chudan tsuki": {
diff --git a/src/data/dojos/aikido-foederation/exams/dan2.ts b/src/data/dojos/aikido-foederation/exams/dan2.ts
index 781243e..dd7a8b2 100644
--- a/src/data/dojos/aikido-foederation/exams/dan2.ts
+++ b/src/data/dojos/aikido-foederation/exams/dan2.ts
@@ -2,7 +2,10 @@ import type { Exam } from "$core/model";
export const dan2: Exam = {
id: "dan2",
- labelKey: "chooser.button.dan2",
+ label: {
+ type: "wellknown",
+ key: "dan2",
+ },
techniques: {
"jo dori": {
"chudan tsuki": {
diff --git a/src/data/dojos/aikido-foederation/exams/dan3.ts b/src/data/dojos/aikido-foederation/exams/dan3.ts
index 978ebd4..d9b9151 100644
--- a/src/data/dojos/aikido-foederation/exams/dan3.ts
+++ b/src/data/dojos/aikido-foederation/exams/dan3.ts
@@ -2,7 +2,10 @@ import type { Exam } from "$core/model";
export const dan3: Exam = {
id: "dan3",
- labelKey: "chooser.button.dan3",
+ label: {
+ type: "wellknown",
+ key: "dan3",
+ },
techniques: {
"tachi dori": {
"shomen uchi": {
diff --git a/src/data/dojos/aikido-foederation/exams/kyu1.ts b/src/data/dojos/aikido-foederation/exams/kyu1.ts
index 8d1a53a..f56dcce 100644
--- a/src/data/dojos/aikido-foederation/exams/kyu1.ts
+++ b/src/data/dojos/aikido-foederation/exams/kyu1.ts
@@ -2,7 +2,11 @@ import type { Exam } from "$core/model";
export const kyu1: Exam = {
id: "kyu1",
- labelKey: "chooser.button.kyu1",
+ label: {
+ type: "wellknown",
+ key: "kyu1",
+ },
+
techniques: {
"suwari waza": {
"shomen uchi": {
diff --git a/src/data/dojos/aikido-foederation/exams/kyu2.ts b/src/data/dojos/aikido-foederation/exams/kyu2.ts
index 5a9181d..bae83db 100644
--- a/src/data/dojos/aikido-foederation/exams/kyu2.ts
+++ b/src/data/dojos/aikido-foederation/exams/kyu2.ts
@@ -2,7 +2,11 @@ import type { Exam } from "$core/model";
export const kyu2: Exam = {
id: "kyu2",
- labelKey: "chooser.button.kyu2",
+ label: {
+ type: "wellknown",
+ key: "kyu2",
+ },
+
techniques: {
"suwari waza": {
"gyuako hanmi katate dori": {
diff --git a/src/data/dojos/aikido-foederation/exams/kyu3.ts b/src/data/dojos/aikido-foederation/exams/kyu3.ts
index 0ebebdb..5f6c068 100644
--- a/src/data/dojos/aikido-foederation/exams/kyu3.ts
+++ b/src/data/dojos/aikido-foederation/exams/kyu3.ts
@@ -2,7 +2,10 @@ import type { Exam } from "$core/model";
export const kyu3: Exam = {
id: "kyu3",
- labelKey: "chooser.button.kyu3",
+ label: {
+ type: "wellknown",
+ key: "kyu3",
+ },
techniques: {
"suwari waza": {
"gyuako hanmi katate dori": {
diff --git a/src/data/dojos/aikido-foederation/exams/kyu4.ts b/src/data/dojos/aikido-foederation/exams/kyu4.ts
index 8c0267c..86ca732 100644
--- a/src/data/dojos/aikido-foederation/exams/kyu4.ts
+++ b/src/data/dojos/aikido-foederation/exams/kyu4.ts
@@ -2,7 +2,11 @@ import type { Exam } from "$core/model";
export const kyu4: Exam = {
id: "kyu4",
- labelKey: "chooser.button.kyu4",
+ label: {
+ type: "wellknown",
+ key: "kyu4",
+ },
+
techniques: {
"suwari waza": {
"ai hanmi katate dori": {
diff --git a/src/data/dojos/aikido-foederation/exams/kyu5.ts b/src/data/dojos/aikido-foederation/exams/kyu5.ts
index 0e4a218..86f723c 100644
--- a/src/data/dojos/aikido-foederation/exams/kyu5.ts
+++ b/src/data/dojos/aikido-foederation/exams/kyu5.ts
@@ -2,7 +2,10 @@ import type { Exam } from "$core/model";
export const kyu5: Exam = {
id: "kyu5",
- labelKey: "chooser.button.kyu5",
+ label: {
+ type: "wellknown",
+ key: "kyu5",
+ },
techniques: {
"suwari waza": {
"ryote dori": {
diff --git a/src/i18n/common/de.json b/src/i18n/common/de.json
index b5aa92b..40b4570 100644
--- a/src/i18n/common/de.json
+++ b/src/i18n/common/de.json
@@ -3,10 +3,10 @@
"app.title": "Aikido-Prüfung",
"button.play-video.label": "Video zeigen",
"button.settings.youtube.label": "YouTube Videos anzeigen",
- "chooser.button.additional": "+X",
"chooser.button.dan1": "1.DAN",
"chooser.button.dan2": "2.DAN",
"chooser.button.dan3": "3.DAN",
+ "chooser.button.dan4": "4.DAN",
"chooser.button.kyu1": "1.Kyu",
"chooser.button.kyu2": "2.Kyu",
"chooser.button.kyu3": "3.Kyu",
diff --git a/src/i18n/common/en.json b/src/i18n/common/en.json
index 22eee60..ce1a1be 100644
--- a/src/i18n/common/en.json
+++ b/src/i18n/common/en.json
@@ -3,10 +3,10 @@
"app.title": "Aikido Exam",
"button.play-video.label": "Play video",
"button.settings.youtube.label": "Enable YouTube videos",
- "chooser.button.additional": "+X",
"chooser.button.dan1": "1st DAN",
"chooser.button.dan2": "2nd DAN",
"chooser.button.dan3": "3rd DAN",
+ "chooser.button.dan4": "4th DAN",
"chooser.button.kyu1": "1st Kyu",
"chooser.button.kyu2": "2nd Kyu",
"chooser.button.kyu3": "3rd Kyu",