Easy-to-manage touch screens
Free, open-source touch screens. Guided USB setup, a drag-and-drop editor and local control. Made for Home Assistant.
ניהול ערב הפוקר של החברים: כניסות, ז׳יטונים, ספירה סופית והתחשבנות — הכול במקום אחד.
Translation pending
LifestyleTypeScript · PLpgSQLGitHub @ilanpasinkov10-bitOnlineOwner unconfirmed
Have you tried it? One tap is enough. No sign-in needed.
No updates detected yet. GitHub releases will appear here.
Owners of public repositories under a personal GitHub account can sign in to verify ownership and manage the introduction themselves. No administrator approval is needed.
Sign in, generate a verification file, and commit it to this repository’s default branch within 30 minutes. Files on other branches or forks do not count. Then check the file below to manage the project without administrator approval. Keep the file to retain your access.
If needed, you will go to sign-in. After signing in, return here and press your chosen verification button again.
Run /nomorevibe verify in your project folder to verify ownership and manage the introduction yourself.
Verify in one minuteAsk about other verification options
Loading the full README. The saved excerpt is shown below.
Poker With Friends · פוקר עם חברים A private poker-night manager for groups of friends. It does not deal cards — people play a real, physical game around a real table. The app handles the money: buy-ins, chips issued, the clock, the final chip count, profit and loss, and who owes whom at the end of the night. The interface is Hebrew-first and RTL, designed for a phone in one hand. Setup: see docs/SETUP.md for the Supabase and Vercel configuration. Nothing in this repository needs editing to get running.Read the full README ↗
A private poker-night manager for groups of friends. It does not deal cards — people play a real, physical game around a real table. The app handles the money: buy-ins, chips issued, the clock, the final chip count, profit and loss, and who owes whom at the end of the night.
The interface is Hebrew-first and RTL, designed for a phone in one hand.
Setup: see
docs/SETUP.mdfor the Supabase and Vercel configuration. Nothing in this repository needs editing to get running.
| Open a table | Buy-in amount, chips per buy-in, max entries, start and end time, join mode, visibility, counting mode. Defaults: 50₪ = 500 chips, 6 entries max. |
| Invite | A 5-character join code (A7K92) with a share link and native share sheet. The code never grants admin rights. |
| Join | Registered users, or guests with no account at all. |
| Play | Live dashboard: players, entries, pot, chips on the table, countdown. Players request another entry; the admin approves or rejects. |
| Count | Admin counting or self-counting, with chip-discrepancy detection before anything is finalised. |
| Settle | Profit/loss per player and the minimal set of transfers: "דניאל מעביר לאילן 80₪". |
| Remember | History, statistics, records, profit charts and per-group leaderboards. |
| Compare | An opt-in global leaderboard of registered players, ranked on realised profit from finished games only. |
| Tidy up | A table that never started can be deleted by its owner. |
| Leave early | A player can cash out mid-game: they declare their chips, their result is settled by the same accounting as everyone else, and the game carries on. |
src/
app/ routes (App Router, all screens Hebrew/RTL)
table/[id]/ live dashboard, counting, results, leaderboard
join/[code]/ guest and member joining
profile/ overview · history · stats · tables · settings
components/
ui/ design-system primitives (RTL-aware)
table/ join/ profile/ charts/ layout/
lib/
domain/ pure, unit-tested business logic
actions/ server actions — the only write path
data/ server-side read models
supabase/ browser · server · service-role · middleware clients
supabase/migrations/ schema, functions, RLS, realtime, storage
tests/ Vitest suites over the domain layer
Every mutation goes through a Server Action → SECURITY DEFINER Postgres
function. Those functions derive the actor from auth.uid() themselves and
take row locks before they read totals, so the rules hold under concurrency and
cannot be bypassed by a crafted request. The TypeScript in
src/lib/domain/permissions.ts mirrors the same rules purely so the UI can hide
controls that would fail — if the two ever disagree, the database wins.
The source of truth for seated-versus-left is table_players.left_at — null
while seated, a timestamp once the leave flow completed. Nothing else decides
it: not a chip submission, not an approved count, not a status value. The
predicates in src/lib/domain/participation.ts treat anything that is not a
real timestamp as seated, so a missing column can never mark a playing
participant as having left.
A player who cashes out keeps status = 'ACTIVE' and gains a left_at
timestamp. That is deliberate: their buy-ins are in the pot and their issued
chips came off the rack, so they are still a participant in that game.
compute_final_rows, the chip-conservation check and finalize_game all select
status = 'ACTIVE' and are therefore untouched — the leaver's profit and loss is
computed by exactly the same code as everyone else's. There is no second
accounting path. What left_at changes is only the right to keep playing: no
further buy-ins and no rebuy requests.
buyin_transactions is an append-only ledger. A mistaken entry is
corrected with a REVERSAL row, never by editing history.total_paid, chips_issued, buy_in_count) are derived by a view
over that ledger. There is no client-written counter anywhere.finalize_game recomputes the results itself and rejects any settlement
plan that does not exactly resolve every balance.request_id is covered by a unique index. A double-tap, or two admins tapping
at once, cannot produce two buy-ins.reverses_transaction_id, so a buy-in can be reversed
at most once.A visitor who joins with a code is given a Supabase anonymous session, minted
server-side, stored in httpOnly cookies. That means a guest holds a real
signed JWT and a real auth.uid(), so:
One channel per table subscribes to postgres_changes for that table's rows;
events trigger a debounced router.refresh(), so server components re-render
with authoritative data instead of the client duplicating query logic. Realtime
is delivered through RLS. A 30-second poll covers a dropped socket.
show_on_leaderboard controls whether a registered player appears in the
global ranking. It defaults to off — the leaderboard is opt-in. Ranking
someone's lifetime winnings in front of people they have never played with is
a real disclosure, so nobody is entered without asking. A missing settings row
resolves to off too, so it cannot become a way around the default. Guests
never appear at all.get_public_profile decides what one player may see about another: aggregate
stats need either the leaderboard opt-in or a shared table plus that player's
shared-stats setting, and per-game history additionally needs
share_detailed_history. Emails and auth identifiers are never read at all.OPEN (everyone sees everyone's entries and investment) or
PRIVATE (each player sees only their own). This is enforced in RLS, not just
hidden in the UI — a private table returns no ledger rows for other players.profile_privacy_settings
controls whether other players you have shared a table with may see your
aggregate statistics, and whether detailed history is shared at all. A table
admin gains no access to anyone's wider financial history.Once a game is COMPLETED its game_results are frozen and are the source of
truth for every statistic. Corrections go through correct_game_results, which
requires a stated reason, re-validates the chip balance, recomputes the
settlement, bumps a revision number, and snapshots the previous state into
game_corrections.
Next.js 15 (App Router) · React 19 · TypeScript (strict, noUncheckedIndexedAccess) ·
Tailwind CSS v4 · Supabase (Postgres, Auth, Realtime, Storage, RLS) · Zod ·
Vitest. Charts are hand-written SVG so the time axis can run right-to-left to
match the Hebrew reading direction.
/dev/preview is a development-only gallery of every major screen and state,
rendered from the real components with static fixture props (see
src/app/dev/preview/). It exists so the interface can be reviewed before a
Supabase project is configured.
cp .env.example .env.local # placeholder values are enough for the gallery
npm run dev # then open http://localhost:3000/dev/preview
Pick a screen from the selector at the top; append &bare=1 to hide the
gallery chrome. Buttons that would call a server action are inert without a
backend, but local component state — modals, dialogs, form inputs — works.
The route is blocked in production two ways over: middleware returns a plain
404 for anything under /dev when NODE_ENV=production, and the page itself
calls notFound(). No production module imports anything from that folder.
npm run dev # development server
npm run build # production build
npm run lint # ESLint
npm run typecheck # tsc --noEmit
npm test # Vitest — domain logic
npm run test:db # migrations + database guarantees (needs local PostgreSQL)
npm run test:db applies every migration to a throwaway PostgreSQL instance and
asserts the guarantees the app depends on, so a migration change cannot quietly
break them: that a player can never approve their own rebuy, that approving the
same request twice leaves the ledger untouched, that the entry cap holds, that
COMPLETED is unreachable without finalising, that a chip shortfall and a
settlement plan which does not resolve every balance are both rejected, that
profit and loss sum to exactly zero, that a player at a PRIVATE table sees
only their own ledger rows, and that clients cannot write to any table directly.
The domain layer is pure and directly tested: chip/cash conversion and the rounding partition, buy-in economics, maximum-entry enforcement, profit/loss, chip-count validation, the settlement algorithm and its rejection of tampered plans, permission rules (including "a player may never approve their own request" and "COMPLETED is unreachable by a plain status change"), lifetime statistics and records, Israeli date/time/currency formatting, and the guarantee that no raw database error reaches a user.
SELECT only. Every write is a privileged function.SUPABASE_SERVICE_ROLE_KEY is server-only, never prefixed NEXT_PUBLIC_, and
imported solely from a server-only module.