feat(cards): deck management UI + production auth portal wiring
Deck schema, API routes, and SvelteKit UI for creating and browsing decks (DeckStack component, inline creation, floating nav). Production compose updated with PUBLIC_AUTH_WEB_URL so cards-web redirects to auth.mana.how for login/register instead of the raw API. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
7116bd66b4
commit
5859e202c5
9 changed files with 271 additions and 28 deletions
|
|
@ -44,6 +44,7 @@ export const decks = cardsSchema.table(
|
|||
// Quelle nachzuladen.
|
||||
forkedFromMarketplaceDeckId: text('forked_from_marketplace_deck_id'),
|
||||
forkedFromMarketplaceVersionId: text('forked_from_marketplace_version_id'),
|
||||
archivedAt: timestamp('archived_at', { withTimezone: true, mode: 'date' }),
|
||||
createdAt: timestamp('created_at', { withTimezone: true, mode: 'date' })
|
||||
.notNull()
|
||||
.defaultNow(),
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ export function toDeckDto(row: typeof decks.$inferSelect) {
|
|||
content_hash: row.contentHash,
|
||||
forked_from_marketplace_deck_id: row.forkedFromMarketplaceDeckId,
|
||||
forked_from_marketplace_version_id: row.forkedFromMarketplaceVersionId,
|
||||
archived_at: row.archivedAt?.toISOString() ?? null,
|
||||
created_at: row.createdAt.toISOString(),
|
||||
updated_at: row.updatedAt.toISOString(),
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { and, eq, isNotNull, ne } from 'drizzle-orm';
|
||||
import { and, eq, isNotNull, isNull, ne } from 'drizzle-orm';
|
||||
import { sql } from 'drizzle-orm';
|
||||
import { Hono } from 'hono';
|
||||
|
||||
|
|
@ -52,10 +52,17 @@ export function decksRouter(deps: DecksDeps = {}): Hono<{ Variables: AuthVars }>
|
|||
r.get('/', async (c) => {
|
||||
const userId = c.get('userId');
|
||||
const forkedFromMarketplace = c.req.query('forked_from_marketplace');
|
||||
const archivedParam = c.req.query('archived');
|
||||
const conditions = [eq(decks.userId, userId)];
|
||||
if (forkedFromMarketplace === 'true') {
|
||||
conditions.push(isNotNull(decks.forkedFromMarketplaceDeckId));
|
||||
}
|
||||
// archived=true → nur archivierte; default → nur aktive
|
||||
if (archivedParam === 'true') {
|
||||
conditions.push(isNotNull(decks.archivedAt));
|
||||
} else {
|
||||
conditions.push(isNull(decks.archivedAt));
|
||||
}
|
||||
const rows = await dbOf()
|
||||
.select()
|
||||
.from(decks)
|
||||
|
|
@ -86,6 +93,7 @@ export function decksRouter(deps: DecksDeps = {}): Hono<{ Variables: AuthVars }>
|
|||
422
|
||||
);
|
||||
}
|
||||
const now = new Date();
|
||||
const [row] = await dbOf()
|
||||
.update(decks)
|
||||
.set({
|
||||
|
|
@ -97,7 +105,9 @@ export function decksRouter(deps: DecksDeps = {}): Hono<{ Variables: AuthVars }>
|
|||
...(parsed.data.fsrs_settings !== undefined && {
|
||||
fsrsSettings: parsed.data.fsrs_settings,
|
||||
}),
|
||||
updatedAt: new Date(),
|
||||
...(parsed.data.archived === true && { archivedAt: now }),
|
||||
...(parsed.data.archived === false && { archivedAt: null }),
|
||||
updatedAt: now,
|
||||
})
|
||||
.where(and(eq(decks.id, id), eq(decks.userId, userId)))
|
||||
.returning();
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue