mirror of
https://github.com/OHTU-German-learning-website/OHTU-German-learning-website.git
synced 2026-10-12 19:13:51 +03:00
@@ -9,13 +9,51 @@
|
||||
margin: 0 0 var(--u-sm);
|
||||
}
|
||||
|
||||
.glossary-index-item {
|
||||
display: inline-block;
|
||||
padding: 0.35rem 0.75rem;
|
||||
.glossary-index-letter {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0.45rem 0.75rem;
|
||||
border-radius: var(--radius-sm);
|
||||
background-color: var(--bg3);
|
||||
color: var(--fg1);
|
||||
font-size: 0.95rem;
|
||||
font-weight: 600;
|
||||
text-decoration: none;
|
||||
border: 1px solid transparent;
|
||||
transition: border-color 0.2s, background-color 0.2s;
|
||||
}
|
||||
|
||||
.glossary-index-letter:hover,
|
||||
.glossary-index-letter:focus {
|
||||
background-color: var(--bg4);
|
||||
border-color: var(--bg5);
|
||||
}
|
||||
|
||||
.glossary-index-group {
|
||||
padding: var(--u-sm);
|
||||
border-radius: var(--radius-sm);
|
||||
background-color: var(--bg3);
|
||||
display: grid;
|
||||
gap: var(--u-2xs);
|
||||
}
|
||||
|
||||
.glossary-index-group-title {
|
||||
margin: 0 0 var(--u-xs);
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
.glossary-index-entry {
|
||||
display: inline-block;
|
||||
color: var(--fg1);
|
||||
text-decoration: none;
|
||||
padding: 0.35rem 0.5rem;
|
||||
border-radius: var(--radius-sm);
|
||||
transition: background-color 0.2s;
|
||||
}
|
||||
|
||||
.glossary-index-entry:hover,
|
||||
.glossary-index-entry:focus {
|
||||
background-color: var(--bg4);
|
||||
}
|
||||
|
||||
.glossary-card {
|
||||
@@ -25,12 +63,23 @@
|
||||
border: 1px solid var(--bg3);
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
transition: box-shadow 0.2s;
|
||||
scroll-margin-top: 100px;
|
||||
}
|
||||
|
||||
.glossary-card:hover {
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
|
||||
}
|
||||
|
||||
.glossary-letter-group {
|
||||
border-top: 1px solid var(--bg3);
|
||||
padding-top: var(--u-md);
|
||||
}
|
||||
|
||||
.glossary-letter-title {
|
||||
margin: 0 0 var(--u-sm);
|
||||
font-size: 1.35rem;
|
||||
}
|
||||
|
||||
.glossary-word {
|
||||
color: var(--fg1);
|
||||
margin: 0;
|
||||
|
||||
@@ -19,24 +19,63 @@ export default function GlossaryPage() {
|
||||
a.word.localeCompare(b.word)
|
||||
);
|
||||
|
||||
const entriesByLetter = sortedEntries.reduce((map, entry) => {
|
||||
const letter = (entry.word?.[0] || "").toUpperCase();
|
||||
if (!map[letter]) {
|
||||
map[letter] = [];
|
||||
}
|
||||
map[letter].push(entry);
|
||||
return map;
|
||||
}, {});
|
||||
|
||||
const letters = Object.keys(entriesByLetter).sort();
|
||||
|
||||
return (
|
||||
<Column gap="md" width="100%">
|
||||
<Row justify="space-between" gap="xl" width="100%" align="center">
|
||||
<h2>Glossar</h2>
|
||||
</Row>
|
||||
|
||||
{sortedEntries.length > 0 && (
|
||||
{letters.length > 0 && (
|
||||
<Container className="glossary-index" mb="md">
|
||||
<h3>Register</h3>
|
||||
<Container display="flex" flexWrap="wrap" gap="xs" mb="sm">
|
||||
{letters.map((letter) => (
|
||||
<a
|
||||
key={letter}
|
||||
className="glossary-index-letter"
|
||||
href={`#letter-${letter}-section`}
|
||||
>
|
||||
{letter}
|
||||
</a>
|
||||
))}
|
||||
</Container>
|
||||
|
||||
<Container
|
||||
display="grid"
|
||||
templateColumns="repeat(auto-fit, minmax(160px, 1fr))"
|
||||
gap="sm"
|
||||
>
|
||||
{sortedEntries.map((entry) => (
|
||||
<span key={entry.id} className="glossary-index-item">
|
||||
{entry.word}
|
||||
</span>
|
||||
{letters.map((letter) => (
|
||||
<Container key={letter} className="glossary-index-group">
|
||||
<h4
|
||||
id={`letter-${letter}`}
|
||||
className="glossary-index-group-title"
|
||||
>
|
||||
{letter}
|
||||
</h4>
|
||||
<Column gap="xs">
|
||||
{entriesByLetter[letter].map((entry) => (
|
||||
<a
|
||||
key={entry.id}
|
||||
href={`#entry-${entry.id}`}
|
||||
className="glossary-index-entry"
|
||||
>
|
||||
{entry.word}
|
||||
</a>
|
||||
))}
|
||||
</Column>
|
||||
</Container>
|
||||
))}
|
||||
</Container>
|
||||
</Container>
|
||||
@@ -45,17 +84,36 @@ export default function GlossaryPage() {
|
||||
{sortedEntries.length === 0 ? (
|
||||
<p>Keine Einträge gefunden.</p>
|
||||
) : (
|
||||
<Container
|
||||
display="grid"
|
||||
templateColumns="repeat(auto-fill, minmax(300px, 1fr))"
|
||||
gap="md"
|
||||
>
|
||||
{sortedEntries.map((entry) => (
|
||||
<Container key={entry.id} className="glossary-card">
|
||||
<Column gap="sm">
|
||||
<h4 className="glossary-word">{entry.word}</h4>
|
||||
<RenderHTML data={entry.word_definition} disableGlossary />
|
||||
</Column>
|
||||
<Container display="grid" gap="md">
|
||||
{letters.map((letter) => (
|
||||
<Container key={letter} className="glossary-letter-group">
|
||||
<h3
|
||||
id={`letter-${letter}-section`}
|
||||
className="glossary-letter-title"
|
||||
>
|
||||
{letter}
|
||||
</h3>
|
||||
<Container
|
||||
display="grid"
|
||||
templateColumns="repeat(auto-fill, minmax(300px, 1fr))"
|
||||
gap="md"
|
||||
>
|
||||
{entriesByLetter[letter].map((entry) => (
|
||||
<Container
|
||||
key={entry.id}
|
||||
id={`entry-${entry.id}`}
|
||||
className="glossary-card"
|
||||
>
|
||||
<Column gap="sm">
|
||||
<h4 className="glossary-word">{entry.word}</h4>
|
||||
<RenderHTML
|
||||
data={entry.word_definition}
|
||||
disableGlossary
|
||||
/>
|
||||
</Column>
|
||||
</Container>
|
||||
))}
|
||||
</Container>
|
||||
</Container>
|
||||
))}
|
||||
</Container>
|
||||
|
||||
@@ -5,14 +5,37 @@ import { Container, Column, Row } from "@/components/ui/layout/container";
|
||||
import useQuery from "@/shared/hooks/useQuery";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { ExerciseLinkButton } from "@/components/ui/button/exercise-link-button";
|
||||
import { withBasePath } from "@/shared/utils/basePath";
|
||||
|
||||
export default function MemoryGameExercisesPage() {
|
||||
const {
|
||||
data: exercises,
|
||||
isLoading,
|
||||
error,
|
||||
refetch,
|
||||
} = useQuery("/exercises/memory-game");
|
||||
|
||||
const handleDelete = async (exerciseId) => {
|
||||
const confirmed = confirm("Möchten Sie diese Übung wirklich löschen?");
|
||||
if (!confirmed) return;
|
||||
|
||||
try {
|
||||
const response = await fetch(
|
||||
withBasePath(`/api/admin/exercises/memory-game/${exerciseId}`),
|
||||
{ method: "DELETE" }
|
||||
);
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error("Fehler beim Löschen der Übung.");
|
||||
}
|
||||
|
||||
refetch();
|
||||
} catch (deleteError) {
|
||||
console.error("Error deleting exercise:", deleteError);
|
||||
alert("Fehler beim Löschen der Übung.");
|
||||
}
|
||||
};
|
||||
|
||||
if (isLoading) {
|
||||
return (
|
||||
<Container display="flex" justify="center" align="center" h="200px">
|
||||
@@ -97,13 +120,6 @@ export default function MemoryGameExercisesPage() {
|
||||
>
|
||||
Link kopieren
|
||||
</ExerciseLinkButton>
|
||||
<Link
|
||||
href={`/grammar/exercises/memory-game/${exercise.memory_game_exercise_id}`}
|
||||
>
|
||||
<Button type="button" variant="secondary" size="sm">
|
||||
Spielen
|
||||
</Button>
|
||||
</Link>
|
||||
<Link
|
||||
href={`/admin/create-exercise/memory-game/${exercise.memory_game_exercise_id}/edit`}
|
||||
>
|
||||
@@ -111,6 +127,19 @@ export default function MemoryGameExercisesPage() {
|
||||
Bearbeiten
|
||||
</Button>
|
||||
</Link>
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
style={{
|
||||
color: "#fff5f5",
|
||||
borderColor: "#d85a5a",
|
||||
backgroundColor: "#d85a5a",
|
||||
}}
|
||||
onClick={() => handleDelete(exercise.memory_game_exercise_id)}
|
||||
>
|
||||
Löschen
|
||||
</Button>
|
||||
</Column>
|
||||
</Row>
|
||||
))}
|
||||
|
||||
Reference in New Issue
Block a user