feat(decks): Deck-Kategorien über den ganzen Stack
Some checks are pending
CI / validate (push) Waiting to run
Some checks are pending
CI / validate (push) Waiting to run
- cards-domain: DECK_CATEGORY_IDS, Labels, DeckCategorySchema, category-Feld im DeckSchema - DB-Schema (decks + marketplace/decks): category-Spalte - API-Routen: category in create/update/list/explore - Web: DeckCategoryIcon-Komponente, Kategorie-Picker auf Deck-Detail, Kategorie-Icon in DeckListGrid (Marketplace) - Layout: Bottom-Padding für floating Nav-Bar Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
5876f95d85
commit
7bf61315b5
13 changed files with 251 additions and 11 deletions
47
apps/web/src/lib/components/DeckCategoryIcon.svelte
Normal file
47
apps/web/src/lib/components/DeckCategoryIcon.svelte
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
<script lang="ts">
|
||||
import type { DeckCategoryId } from '@cards/domain';
|
||||
import {
|
||||
Globe,
|
||||
Heartbeat,
|
||||
Leaf,
|
||||
Brain,
|
||||
Flag,
|
||||
Medal,
|
||||
Lightning,
|
||||
Palette,
|
||||
MusicNote,
|
||||
Barbell,
|
||||
Sparkle,
|
||||
} from '@mana/shared-icons';
|
||||
|
||||
type Weight = 'thin' | 'light' | 'regular' | 'bold' | 'fill' | 'duotone';
|
||||
|
||||
interface Props {
|
||||
category: DeckCategoryId;
|
||||
size?: number;
|
||||
color?: string | null;
|
||||
weight?: Weight;
|
||||
}
|
||||
|
||||
let { category, size = 32, color = null, weight = 'duotone' }: Props = $props();
|
||||
|
||||
const ICON_MAP: Record<DeckCategoryId, typeof Globe> = {
|
||||
language: Globe,
|
||||
medicine: Heartbeat,
|
||||
science: Leaf,
|
||||
math: Brain,
|
||||
history: Flag,
|
||||
law: Medal,
|
||||
technology: Lightning,
|
||||
arts: Palette,
|
||||
music: MusicNote,
|
||||
sport: Barbell,
|
||||
other: Sparkle,
|
||||
};
|
||||
|
||||
const IconComponent = $derived(ICON_MAP[category]);
|
||||
</script>
|
||||
|
||||
{#if IconComponent}
|
||||
<IconComponent {size} {weight} color={color ?? undefined} />
|
||||
{/if}
|
||||
|
|
@ -1,6 +1,9 @@
|
|||
<script lang="ts">
|
||||
import type { DeckListEntry } from '$lib/api/marketplace.ts';
|
||||
import type { DeckCategoryId } from '@cards/domain';
|
||||
import { DECK_CATEGORY_IDS } from '@cards/domain';
|
||||
import AuthorBadge from './AuthorBadge.svelte';
|
||||
import DeckCategoryIcon from '$lib/components/DeckCategoryIcon.svelte';
|
||||
|
||||
interface Props {
|
||||
items: DeckListEntry[];
|
||||
|
|
@ -9,6 +12,10 @@
|
|||
|
||||
const { items, emptyMessage = 'Noch keine Decks gefunden.' }: Props = $props();
|
||||
|
||||
function isValidCategory(c: string | null): c is DeckCategoryId {
|
||||
return c !== null && (DECK_CATEGORY_IDS as readonly string[]).includes(c);
|
||||
}
|
||||
|
||||
function languageLabel(code: string | null): string {
|
||||
if (!code) return '';
|
||||
const map: Record<string, string> = { de: 'Deutsch', en: 'English', es: 'Español', fr: 'Français' };
|
||||
|
|
@ -29,14 +36,21 @@
|
|||
<a href="/d/{deck.slug}" class="block">
|
||||
<div class="flex items-start justify-between gap-2">
|
||||
<h3 class="truncate font-medium">{deck.title}</h3>
|
||||
{#if deck.is_featured}
|
||||
<span
|
||||
class="shrink-0 rounded-full bg-[hsl(var(--color-primary))]/15 px-2 py-0.5 text-[10px] font-medium text-[hsl(var(--color-primary))]"
|
||||
title="Editorial Pick"
|
||||
>
|
||||
★ Featured
|
||||
</span>
|
||||
{/if}
|
||||
<div class="flex shrink-0 items-center gap-1.5">
|
||||
{#if isValidCategory(deck.category)}
|
||||
<span class="text-[hsl(var(--color-muted-foreground))]" aria-hidden="true">
|
||||
<DeckCategoryIcon category={deck.category} size={16} weight="duotone" />
|
||||
</span>
|
||||
{/if}
|
||||
{#if deck.is_featured}
|
||||
<span
|
||||
class="rounded-full bg-[hsl(var(--color-primary))]/15 px-2 py-0.5 text-[10px] font-medium text-[hsl(var(--color-primary))]"
|
||||
title="Editorial Pick"
|
||||
>
|
||||
★ Featured
|
||||
</span>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
{#if deck.description}
|
||||
<p
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue