尚未检测到更新。GitHub 发布版本后会显示在这里。
个人 GitHub 账号下公开仓库的所有者可以登录并验证所有权,自行管理介绍,无需管理员批准。
登录并生成验证文件,在 30 分钟内将其提交到此仓库的默认分支。其他分支或分叉仓库中的文件无效。然后在下方检查文件,即可无需管理员批准直接管理项目。请保留文件以维持管理权限。
如需登录,将前往登录页面。登录后请回到此处,再次点击所选的验证按钮。
在项目文件夹中运行 /nomorevibe verify,即可验证所有权并自行管理介绍。
一分钟完成验证制作者的介绍仅有上面的一句话。 更多详情请阅读仓库 README。 仓库 README ↗
正在加载完整 README。
查看完整 README ↗Vánoční losování dárků. Každý si na jedné adrese klikne na své jméno, jednou zalosuje a dozví se jednoho člověka, kterého obdaruje — a nic víc. Dárek musí začínat na stejné písmeno jako jméno obdarovaného.
Stack: Next.js 16 (App Router) · React 19 · TypeScript · Tailwind v4 · Drizzle ORM + Postgres (Supabase) · Vercel
/admin seznam lidí — jméno i příjmení, protože
v losu se zobrazí přesně to, co zadá.
Pár nebo domácnost napíše na jeden řádek a jména oddělí „+“
(Jan Novák + Jana Nováková) — navzájem se nevylosují.Název události, datum a cena dárku jsou pohromadě v konstantách v
lib/game/countdown.ts — na další rok se mění
jen tam.
Kdo chce, může místo toho použít osobní odkaz („Jan Novák“ → /jannovak).
Je odvozený ze jména, takže ho administrace nevypisuje — kdo ho potřebuje,
složí si ho sám (seznam i s odkazy ukáže npm run stav). Na přesný tvar si vzpomínat nemusí —
/Jan-Novák, /JanNovak i /jan_novak skončí přesměrováním na /jannovak.
Odkaz zůstává platný, takže se k losu dá vrátit kdykoli a odkudkoli.
Každý daruje i dostane právě jednou, nikdo nedaruje sám sobě ani partnerovi či nikomu z vlastní domácnosti a losování nikdy neuvázne — ani když všichni zmáčknou Losovat! ve stejný okamžik. Losuje se v transakci pod advisory lockem, takže dva lidé nemohou dostat téhož obdarovaného, a kdyby logika selhala, odmítne to databáze.
Nikdo se nedozví, z kolika lidí losoval ani jak dopadli ostatní — páry se nezobrazují ani v administraci. Animace je pro všechny stejně dlouhá, i pro posledního, kterému už zbývá jediná možnost.
Přeověřit to jde kdykoli: npm run kontrola -- --force. Podrobnosti a pravidla
pro úpravy jsou v AGENTS.md.
| Příkaz | Co dělá |
|---|---|
npm run dev | vývojový server na http://localhost:3000 |
npm run build / npm start | produkční build / jeho spuštění |
npm test | testy losovací logiky (Vitest) |
npm run typecheck / npm run lint | tsc --noEmit / ESLint |
npm run db:push | vytvoří tabulky podle lib/db/schema.ts |
npm run seed | naplní testovací jména (scripts/seed.ts) |
npm run stav | vypíše jména, odkazy a kolik lidí už losovalo |
npm run kontrola -- --force | prověří koncovku a souběh proti databázi (destruktivní) |
# 1. Databáze — buď Supabase, nebo Postgres v Dockeru:
docker run -d --name pipi-tree-pg \
-e POSTGRES_PASSWORD=pipi -e POSTGRES_DB=pipitree \
-p 55432:5432 postgres:16-alpine
# 2. Konfigurace — doplň DATABASE_URL a ADMIN_PASSWORD
cp .env.local.example .env.local
# 3. Tabulky, testovací data, start
npm install
npm run db:push
npm run seed
npm run dev
Administrace je na http://localhost:3000/admin (uživatel libovolný, heslo
ADMIN_PASSWORD). Až databázi nebudeš potřebovat: docker rm -f pipi-tree-pg.
Všechno v Postgresu, v prohlížeči nic — žádné localStorage ani cookies. Los tak nezmizí smazáním dat prohlížeče; kdo má odkaz, dostane ho ze serveru.
participants — seznam lidí a jejich odkazy (name, slug, …)draws — vylosované páry. Pravidla hry vynucuje i databáze, ne jen kód:
giver_id je primární klíč (daruje se jednou), receiver_id má unique index
(dostane se jednou), omezení draws_not_self zakáže darovat sám sobě
a draws_not_same_group partnerovi či nikomu z vlastní domácnosti.Schéma je v lib/db/schema.ts. Lokálně běží Postgres
v Dockeru na portu 55432, v produkci Supabase přes DATABASE_URL.
DATABASE_URL — Supabase session pooler, port 5432
(Supabase → Connect → Session pooler)ADMIN_PASSWORD — heslo do /adminmain = produkční deploy. npm run db:push se pouští z lokálu
(DIRECT_URL je stejná adresa a Vercel ji nepotřebuje).Po deployi musí GET /api/health vrátit {"ok":true}.
Ne transaction pooler (port
6543) — postgres.js na něm při souběžných dotazech zamrzá. Aplikace ho proto odmítne a health check vrátí"kod":"TRANSACTION_POOLER". Ani přímá adresadb.…supabase.conejde: je jen přes IPv6 a Vercel se k ní nepřipojí. Podrobnosti vlib/db/client.ts.
Aplikace nemá přihlašování: kdo klikne na cizí jméno (nebo uhádne cizí odkaz —
je odvozený ze jména, takže /petrsvoboda je snadné), dostane se k cizímu
losu. Vědomá volba ve prospěch jednoduchosti; hra je pro jednu domácnost, kde
se lidi znají. Proti překliknutí je aspoň potvrzovací krok.
Kdyby to nestačilo, přidej v lib/game/slug.ts ke slugu
náhodnou příponu a rozesílej jen osobní odkazy — zbytek aplikace se měnit
nemusí.
Co hra nedovolí ani tak: zjistit, kolik lidí ještě zbývá, přečíst si los někoho, kdo už losoval, ani losovat dvakrát.
app/
page.tsx odpočet do oslavy + výběr jména ze seznamu
[slug]/page.tsx osobní stránka: zalosovat / připomenout si los
[slug]/loading.tsx okamžitá odezva, než se stránka načte
actions.ts server actions: losování, admin
admin/page.tsx seznam lidí, stav losování, kontrola losů, reset
api/health/route.ts health check
not-found.tsx česká 404
globals.css designové tokeny (@theme), tlačítka, animace
components/
VyberJmena.tsx výběr jména ze seznamu + potvrzení proti překliknutí
PersonalDraw.tsx hra na klientovi — losovat, válec, odhalení
DrawReel.tsx výherní válec se jmény
Backdrop.tsx fotka na pozadí, ztmavení, sněžení
CandyButton.tsx / Confetti.tsx / Snow.tsx
lib/
game/draw.ts čistá logika losování (derangement + dokončitelnost)
game/letters.ts první písmeno jména česky (háčky, „Ch“, Á → A)
game/groups.ts řádky z administrace na skupiny (páry, domácnosti)
game/audit.ts zpětná kontrola losů — jen ano/ne, žádná jména
game/slug.ts osobní odkaz ze jména + tolerance k zápisu
game/countdown.ts fakta o události (název, datum, cena) + odpočet
game/random.ts kryptografická náhoda
db/schema.ts participants, draws
db/queries/game.ts serverová pravidla hry (transakce + advisory lock)
admin.ts kontrola admin hesla
proxy.ts brána pro /admin
scripts/ seed.ts, stav.ts, kontrola.ts
tests/game/ testy logiky, písmen, odkazů a odpočtu