feat: grid layout, global claim/qty toggles, admin access link, swaddle image

- Public wishlist now renders as responsive 3-col grid instead of list
- Subtitle supports line breaks (whitespace-pre-line)
- claimingEnabled and showQuantity moved to global site settings (not per-item); toggled in admin Configurações panel; claim API enforces server-side
- Admin dashboard shows admin access link with copy button
- Settings API exposes and persists the two new boolean settings

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Adriano Belisario
2026-05-03 22:52:32 +00:00
parent 5548f5000f
commit b19a3fdf48
8 changed files with 326 additions and 166 deletions

View File

@@ -1,6 +1,6 @@
import { NextRequest, NextResponse } from 'next/server';
import { and, eq, ne, sql } from 'drizzle-orm';
import { db, wishlistItems, wishlists, itemClaims, guests } from '@/lib/db';
import { db, wishlistItems, wishlists, itemClaims, guests, settings } from '@/lib/db';
import { getGuestFromRequest } from '@/lib/auth/tokens';
export async function POST(
@@ -26,6 +26,11 @@ export async function POST(
if (wl.length === 0) return NextResponse.json({ error: 'Wishlist not found' }, { status: 404 });
if (!wl[0].isPublic) return NextResponse.json({ error: 'This wishlist is private' }, { status: 403 });
const claimingSetting = await db.select().from(settings).where(eq(settings.key, 'claimingEnabled')).limit(1);
if (claimingSetting[0]?.value === 'false') {
return NextResponse.json({ error: 'Reservas desativadas' }, { status: 403 });
}
// Atomically check remaining quantity and upsert the claim inside a synchronous
// transaction to prevent race conditions (better-sqlite3 is synchronous).
type TxResult = { ok: true } | { ok: false; remaining: number };