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.
Manage shared house cleaning rotations and compete in a monthly league
Translation pending
AI summary · README
LifestylePLpgSQL · TypeScriptGitHub @thomas0035OnlineOwner 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.
Shared house-cleaning rotations, volunteering and a friendly monthly league (with a weekly champion on the side) for a six-person house. Built from spec.md (V3).
Stack: React 19 + TypeScript + Vite + Tailwind v4 · Supabase (Postgres, Auth, Realtime) · PWA · Vercel.
All rules that matter (points, rotation advancement, volunteer claiming, streaks, achievements, cooldowns) live in Postgres functions in supabase/migrations. The browser never sends a points value; it only calls RPCs like complete_cleaning(area_id).Read the full README ↗Shared house-cleaning rotations, volunteering and a friendly monthly league (with a weekly champion on the side) for a six-person house. Built from spec.md (V3).
Stack: React 19 + TypeScript + Vite + Tailwind v4 · Supabase (Postgres, Auth, Realtime) · PWA · Vercel.
All rules that matter (points, rotation advancement, volunteer claiming, streaks, achievements, cooldowns) live in Postgres functions in supabase/migrations. The browser never sends a points value; it only calls RPCs like complete_cleaning(area_id).
https://supabase.com/dashboard/project/<ref>).cp .env.example .env # then paste the URL + anon key into .env (not into .env.example)
npm install
VITE_SUPABASE_URL must be the API URL shown under Project Settings → API (https://<ref>.supabase.co), not the dashboard link.
npx supabase login # opens the browser once
npx supabase link --project-ref <ref>
npx supabase db push --include-seed
The seed creates:
Asia/Kolkata, week starts Monday, 4-hour re-flag cooldown)Logins: every seeded member signs in as <firstname>@house.local with password chorewars. Everyone should change it from Profile → Change password on first login.
If
db push --include-seedcomplains aboutauth.users, run the contents ofsupabase/seed.sqlin the dashboard SQL Editor instead — it is idempotent.
The migration adds the tables to the supabase_realtime publication. If live updates don't arrive, check Database → Replication and make sure the publication includes cleaning_area_state etc.
npm run dev # http://localhost:5173
npm run typecheck
npm run build # production build in dist/
VITE_SUPABASE_URL and VITE_SUPABASE_ANON_KEY.vercel.json already rewrites all routes to index.html for client-side routing.Open the Vercel URL on a phone and "Add to Home Screen" — it installs as a PWA.
Migration 0009 queues notifications in Postgres; the send-push Edge Function delivers them. One-time setup:
npx web-push generate-vapid-keysnpx supabase secrets set VAPID_PUBLIC_KEY=<public> VAPID_PRIVATE_KEY=<private> VAPID_SUBJECT=mailto:<[email protected]> PUSH_WEBHOOK_SECRET=<long random string>
npx supabase functions deploy send-push
select vault.create_secret('https://<ref>.supabase.co', 'chorewars_project_url');
select vault.create_secret('<long random string>', 'chorewars_push_secret');
VITE_VAPID_PUBLIC_KEY=<public> to .env and to Vercel's environment variables, then redeploy.Push needs the built service worker, so test with npm run build && npm run preview, not npm run dev. Delivery problems show in notifications.last_error and the function's logs.
| Task | Where |
|---|---|
| Add a new housemate | Supabase → Authentication → Users → Add user (email + password), then app → Admin → Members → Add member (same email) |
| Change rotation order / who's up | Admin → Areas → tap area → drag order with arrows, tap a number to set current turn |
| Change intervals, points, grace, cooldown | Admin → Areas → tap area |
| Timezone / week start / bonuses | Admin → Settings |
| Turns per round / max round length / forfeit | Admin → Settings → League rounds |
| Give or take points (dirty dishes, unchecked area, extra effort) | Admin → Members → tap member → Adjust points (reason required; undo from History until the round settles; cap under Settings → "Biggest admin adjustment") |
| Remove an area | Admin → Areas → tap area → Remove (history is kept; it can be restored) |
| Spec | Implementation |
|---|---|
| Fresh / Due Soon / Ready / Attention computed on read | src/lib/status.ts (computeStatus) — pure function of last_cleaned_at + thresholds; nothing stored |
| Needs Cleaning + 4h reactivation cooldown | mark_needs_cleaning() |
| Atomic volunteer claim after grace period | claim_volunteer() — single conditional UPDATE … WHERE activation_state='active' AND volunteer_user_id IS NULL AND now() >= activated_at + grace RETURNING |
| Scheduled person can always complete (even after a claim) | complete_cleaning() — scheduled user → normal; volunteer → volunteer; claim simply cleared |
| Points ledger, round/weekly/all-time derived | user_points_ledger + get_leaderboard('round'|'week'|'all'), get_user_stats(); the round score comes from league_round_scores() |
| Week boundary in house timezone | house_week_start() reads house_settings.timezone and week_start_day |
| Weekly champion snapshot | award_weekly_champions() — idempotent; called lazily by the leaderboard RPC and hourly by pg_cron when available |
| League rounds: a round ends when every eligible member has had N own turns (default 3) or after the max length (default 45 days). Only your first N turns count for you; rescues always count. Champion → Skip Pass + achievement; lowest scorer among those who had all N turns → forfeit (only if at least two did) | settle_league_rounds() — idempotent/lazy like the weekly champion; league_rounds, skip_passes, get_league_round(), get_league_rounds(), mark_forfeit_done() |
Rescue penalty (scheduled person loses rescue_penalty_points, default 5) | negative row in user_points_ledger written by complete_cleaning(); undone with the completion |
| Ties: champion → more rescues, fewer times rescued, reached the score first; forfeit → rescued more, fewer turns, reached the score last, else no forfeit; weekly champion → reached the score first | settle_league_rounds(), award_weekly_champions(), (0011) |
Run these by hand with two browsers signed in as different people:
grace_period); after grace it works. (Temporarily set grace to 0 hours on an area to test quickly.)not_active.supabase.from('user_points_ledger').insert(...) from the console → RLS rejects.league_round_scores().last_scored_atAdmin point adjustments with a required reason, capped by max_point_adjustment (default 50), visible to the whole house | admin_adjust_points(), admin_undo_adjustment(); ledger rows with reason admin_adjustment + note + created_by |
| Skip Pass: turn goes to last round's lowest scorer (next in rotation if they're away), counts as their turn, can't be skipped again; rotation then resumes after the skipper | use_skip_pass(), league_skip_target(), cleaning_area_state.skip_from_user_id |
| "Looks fine" needs half the rotation to agree | dismiss_votes + vote_dismiss(); admins override with dismiss_activation() |
| Away members skipped everywhere, no penalty, round doesn't wait for them, no forfeit | house_members.away, admin_set_member_away(), next_rotation_user() skips them |
| Streak: per-user, global, only broken by a rescue | inside complete_cleaning() (user_streaks) |
| Achievements evaluated in the same transaction | evaluate_achievements() called from complete_cleaning() |
| RLS: members read, nobody writes directly | 20260913000003_rls_realtime.sql |