TreeMap
A free, open-source disk-space visualizer. Squarified treemap, duplicate hunting, disk-full forecasting, and cleanup that only ever moves files to the Trash.
翻譯準備中
透過慶祝每項成就,改變您的生產力。追蹤、整理並反思每日的勝利。
AI 翻譯
Transform your productivity by celebrating every achievement. Track, organize, and reflect on your daily wins.
取自儲存庫 README
正在載入完整 README。 以下為已儲存的摘錄。
DailyWins — Record What You Actually Got Done 🌅
A calm, celebratory Progressive Web App for logging your daily wins — part achievement tracker, part journal, part solo stand-up. Built offline-first with real-time sync, a warm hand-crafted design system, and full PWA installability.檢視完整 README ↗A calm, celebratory Progressive Web App for logging your daily wins — part achievement tracker, part journal, part solo stand-up. Built offline-first with real-time sync, a warm hand-crafted design system, and full PWA installability.
/ on the Timeline to open it.Ctrl/⌘ + Enter to save); a full sheet (with category picker and back-dating) for editing or logging a missed win.?tour=1 (the param is removed once the tour opens). Navigate with Next/Back, the step dots, ←/→ keys or a swipe; Esc closes it.src/styles/dailywins.css) scoped under .dw-app, themed via data attributes. Brand mark is a rising sun + checkmark in a sunrise gradient.Auto mode that follows your device. Auto is the default until you pick Light or Dark.prefers-reduced-motion).ui-ux project skill (.claude/skills/ui-ux/SKILL.md) and is checked with npm run ui:check.src/lib/links.ts with utm_source=dailywins, utm_medium=referral, utm_campaign=support|credit and utm_content set to where the link sits (auth-signin, auth-signup, profile, timeline, page-footer), and keeps the referrer (rel="noopener", no noreferrer) so those sites' analytics can attribute the visit./pricing page (the Pro plan is still marked "Coming Soon"), a Paddle checkout flow, and a Netlify webhook function that records subscription state in a profiles table. See WEBHOOK_SETUP.md.dailywins.css) for the app UI; Tailwind CSS for marketing/policy pages.nvmrc)git clone https://github.com/mjawaids/daily-accomplishments.git
cd daily-accomplishments
npm install
Copy the example env and fill in your values:
cp .env.example .env
| Variable | Required | Purpose |
|---|---|---|
VITE_SUPABASE_URL | ✅ | Supabase project URL |
VITE_SUPABASE_ANON_KEY | ✅ | Supabase anon/public key |
VITE_GA_TRACKING_ID | optional | Google Analytics 4 measurement ID |
VITE_PADDLE_CLIENT_TOKEN | optional | Paddle.js client token (Pro plan) |
VITE_PADDLE_PRICE_ID | optional | Paddle price ID for the Pro plan |
VITE_PADDLE_SANDBOX | optional | true to use Paddle sandbox |
VITE_ONESIGNAL_APP_ID | optional | OneSignal App ID — public by design (push reminders) |
PADDLE_WEBHOOK_SECRET | optional | Server-side Paddle webhook secret (Netlify) |
SUPABASE_SERVICE_ROLE_KEY | ✅ for functions | Server-side key for Netlify functions |
ONESIGNAL_REST_API_KEY | optional | Secret. Server-side OneSignal key (Netlify) |
Node 22 is pinned in .nvmrc — @supabase/supabase-js declares engines: node >=22.
Server-side secrets (no
VITE_prefix) must be set in your host's dashboard, never in client code.
Apply the SQL in supabase/migrations/ to your project (via the Supabase SQL editor or CLI). This creates the accomplishments, categories, profiles, user_settings and notification_log tables, plus the RLS policies, triggers and RPC functions around them. In production you never apply these by hand: the deploy workflow does it (see DEPLOYMENT.md).
https://<project-ref>.supabase.co/auth/v1/callback.http://localhost:5173 and production URL) under Authentication → URL Configuration.Daily Wins can send a push notification at each user's chosen local time
(default 8:00 PM) reminding them to log a win. Delivery is handled by OneSignal;
the schedule is owned by netlify/functions/evening-reminder.ts, which runs
every 15 minutes and works out who is due in their own timezone.
In the OneSignal dashboard:
src/lib/onesignal.ts;
do not also fill in the dashboard's service-worker path fields — setting
both conflicts.)Daily Wins. This doubles as the default notification title.https://dailywins.app.
It must match the origin the SDK initialises on, www included or excluded
exactly as you serve it. A mismatch makes subscription fail silently.https://<site>/icon-512.png. OneSignal asks for 256×256; supply one at
that size if it rejects the 512.http://localhost:5173, and enable "Treat HTTP localhost as HTTPS for
testing". The production app's Site URL check rejects localhost.VITE_ONESIGNAL_APP_ID and the REST API Key into ONESIGNAL_REST_API_KEY.Keys: the App ID is public by design and ships in the client bundle. The
REST API Key is a secret — set it only in the Netlify dashboard, never with a
VITE_ prefix, and never commit it.
Service worker: public/onesignal/OneSignalSDKWorker.js is served from this
origin alongside the app's own public/sw.js. After the first deploy, confirm
it is served as JavaScript rather than swallowed by the SPA redirect:
curl -I https://<site>/onesignal/OneSignalSDKWorker.js # expect application/javascript
Platform limits worth knowing:
cdn.onesignal.com. When it fails to load
(or does not load within 10s), the toggle reports it as blocked and asks the
user to allow the site and reload. It does not report "not supported".
Reminders cannot reach that browser until the user does this.src/lib/onesignal.ts for the reasoning.Netlify environment variables. Server-side values must be set in the Netlify dashboard
(Site settings → Environment variables), not in .env:
SUPABASE_SERVICE_ROLE_KEY — required by both evening-reminder and paddle-webhook. Neither
function works without it.ONESIGNAL_REST_API_KEY — mark as secret, functions scope.VITE_ONESIGNAL_APP_ID — needs the builds scope so it is inlined at build time.Scheduled functions run only on published production deploys, never on deploy previews. After
deploying, check that evening-reminder is listed under Functions with its schedule, and that the
OneSignal worker is served as JavaScript rather than swallowed by the SPA redirect:
curl -I https://<site>/onesignal/OneSignalSDKWorker.js # expect application/javascript
Supabase. Apply every file in supabase/migrations/ — check which have actually been applied
rather than assuming. Nothing else is needed on the Supabase side: the reminder scheduler lives on
Netlify, so there is no pg_cron, no pg_net and no Edge Functions to configure.
The service worker is generated by vite-plugin-pwa (Workbox) — see the VitePWA block in
vite.config.ts. This matters for correctness, not just offline support:
dist/sw.js changes
whenever any asset changes. The browser detects the update, and the superseded precache (including
the previous index.html) is dropped.public/sw-legacy-cleanup.js is imported by the generated worker purely to delete the pre-Workbox
daily-wins-* caches left on existing installations. It can be removed once no installs remain on
a pre-Workbox build.registerType: 'prompt' means a new worker installs and then waits, so nobody loses a half-typed
win to a surprise reload. src/components/UpdateBanner.tsx offers a Reload button; if it is
ignored, the waiting worker activates by itself once every tab is closed, so the update applies on
next open.netlify.toml caches /assets/* (content-hashed by Vite) as immutable for a year, and forces
revalidation of /index.html. Netlify additionally invalidates its own CDN cache on every deploy.Never make the entry document cache-first in a service worker. That is what pinned every returning user to the build they first loaded, and it is why the previous hand-rolled
public/sw.jswas replaced.
npm run dev # start the dev server (http://localhost:5173)
npm run build # production build
npm run preview # preview the production build
npm run lint # run ESLint
npm test # run unit tests (Vitest)
npm run typecheck:functions # typecheck the Netlify functions
npm run ui:check # screenshot screens (phone/desktop × light/dark) to ui-shots/ + axe WCAG 2.2 AA scan
npm run typecheck:ui-check # typecheck the ui:check fixtures against the database types
ui:check starts its own Vite dev server and uses Playwright's Chromium (run npx playwright install chromium once locally). It covers the signed-out screens (auth, with and without the install button, pricing, policies) and the signed-in ones (timeline, insights, profile, the add-win sheet, search and filters, the category editor, the reminder and install prompts, the Add to Home Screen steps, the update banner, the intro tour, and logging a win). Screens that scroll also get a -full.png covering the whole scroll area. Signed-in screens run against a fake Supabase inside the browser, fed by fixture scenarios in scripts/ui-check/scenarios.mjs (default, empty, busy, error, offline), so no account or secret is needed and the check never contacts a real Supabase project. It exits non-zero on serious or critical accessibility violations or on a Supabase call the fake can't answer. Options: --only=timeline,add limits screens, --scenario=empty,busy (or all) picks data, --accent=forest (or all) renders another brand accent, --no-axe skips the scan.
daily-accomplishments/
├── .claude/skills/ui-ux/SKILL.md # UI/UX playbook for Claude: design rules, checklist, Claude Design handoff
├── .github/workflows/
│ ├── ci.yml # PR checks: lint, tests, typecheck (functions + ui:check fixtures), build, migration dry-run
│ └── deploy.yml # main: migrate, deploy to Netlify, tag + release
├── public/
│ ├── manifest.json # PWA manifest
│ ├── sw-legacy-cleanup.js # Deletes pre-Workbox caches (imported by the generated SW)
│ ├── onesignal/ # OneSignal service worker
│ ├── favicon.svg / .ico # Brand favicons (all devices)
│ ├── favicon-16x16/32x32.png
│ ├── apple-touch-icon.png # iOS home-screen icon
│ └── icon-192/512.png # PWA install icons
├── src/
│ ├── components/
│ │ ├── dw/ # DailyWins app UI
│ │ │ ├── AppShell.tsx # Responsive shell (sidebar / tab bar / FAB)
│ │ │ ├── WinsProvider.tsx # State, Supabase + offline wiring, toast/confetti
│ │ │ ├── useDW.ts # Hook for the WinsProvider context
│ │ │ ├── components.tsx # Entry card, composer, form, chips, avatar
│ │ │ ├── screens.tsx # Timeline (search/filter) + Insights
│ │ │ ├── screens2.tsx # Profile + Empty
│ │ │ ├── CategorySheet.tsx # Add / edit / delete categories
│ │ │ ├── PushPrompt.tsx # One-time push reminder opt-in
│ │ │ ├── InstallSteps.tsx # Add to Home Screen / Add to Dock steps sheet
│ │ │ ├── useInstallAction.tsx # Shared "Install app" button behavior
│ │ │ ├── Auth.tsx # Email/password + Google auth
│ │ │ ├── Onboarding.tsx # 3-step intro: first run after signup, replayable overlay
│ │ │ ├── icons.tsx # Icon set + brand mark + category glyphs
│ │ │ ├── keys.ts # Keyboard-shortcut helpers
│ │ │ └── useDevice.ts # Responsive + theme hooks
│ │ ├── InstallPrompt.tsx # One-time PWA install nudge (Chromium)
│ │ ├── UpdateBanner.tsx # "A new version is ready" reload banner
│ │ ├── OfflineIndicator.tsx # Offline / pending-sync status pill (mounted in AppShell)
│ │ └── PageHeader / PageFooter / ThemeToggle.tsx # Marketing/policy pages
│ ├── hooks/useTheme.ts
│ ├── lib/
│ │ ├── supabase.ts # Supabase client + types
│ │ ├── offline.ts # IndexedDB offline manager + sync
│ │ ├── winsData.ts # Date/stat/filter helpers (streak, charts, grouping, search)
│ │ ├── categories.ts # User categories: colors, icons, CRUD, defaults
│ │ ├── userSettings.ts # Notification preferences + timezone
│ │ ├── onesignal.ts # OneSignal web push wrapper
│ │ ├── install.ts # PWA install mode per browser + beforeinstallprompt capture
│ │ ├── onboarding.ts # Intro "seen" flag, first-sign-in detection, ?tour=1
│ │ ├── analytics.ts # Google Analytics helpers
│ │ ├── links.ts # ibexoft.com / jawaid.dev links with UTM tags
│ │ └── paddle.ts # Paddle checkout (optional)
│ ├── pages/ # Pricing, checkout success, policy routes
│ ├── styles/dailywins.css # Design tokens + component styles
│ ├── App.tsx # Routing + auth/onboarding flow
│ └── main.tsx # Entry point
├── supabase/migrations/ # Database schema (applied by the deploy workflow)
├── netlify/functions/
│ ├── evening-reminder.ts # Scheduled (every 15 min) push reminder sender
│ └── paddle-webhook.ts # Paddle webhook (optional)
├── scripts/
│ ├── check-build-env.mjs # Fails the Netlify build if required VITE_* vars are missing
│ ├── ui-check.mjs # npm run ui:check: screenshots + axe accessibility scan
│ └── ui-check/ # Fake Supabase, fixture scenarios and a PWA-update stub for ui:check
└── vite.config.ts # Vite + PWA (Workbox) config; injects __APP_VERSION__
(Unit tests sit beside the code as *.test.ts.)
Simplified; the migrations in supabase/migrations/ are the source of truth.
-- Your wins
accomplishments (
id uuid PRIMARY KEY,
user_id uuid REFERENCES auth.users(id),
text text NOT NULL,
category_id uuid NOT NULL REFERENCES categories(id),
category text, -- legacy text key (work|personal|learning|health), kept in sync by a trigger
created_at timestamptz DEFAULT now(),
updated_at timestamptz DEFAULT now()
)
-- User-managed categories (four defaults seeded per user; max 20 per user)
categories (
id uuid PRIMARY KEY,
user_id uuid REFERENCES auth.users(id),
name text, color text, icon text, position int,
legacy_key text -- maps defaults to the old text values
)
-- Notification preferences (created lazily on first load)
user_settings (
user_id uuid PRIMARY KEY REFERENCES auth.users(id),
push_alias uuid, -- opaque OneSignal external ID, never client-writable
push_enabled boolean,
evening_reminder_enabled boolean,
reminder_local_time time, -- quarter hours only
timezone text,
weekly_digest_enabled boolean,
push_unreachable_since timestamptz
)
-- Reminder send ledger (server-only; RLS with no policies)
notification_log (...)
-- Subscription state (auto-created on signup)
profiles (
id uuid PRIMARY KEY REFERENCES auth.users(id),
subscription_status text, -- free | active | cancelled | past_due
subscription_plan text, -- free | pro
paddle_customer_id text,
paddle_transaction_id text,
...
)
accomplishments, categories, user_settings and profiles enforce Row Level Security so users only ever see their own rows. notification_log is reachable only through the service role. Reminder scheduling and category deletion go through Postgres functions (claim_due_evening_reminders, delete_category, and others).
src/styles/dailywins.css. Accent palettes and category colors are defined as CSS custom properties (category accents use OKLCH).src/lib/categories.ts; the color/icon lists must match the CHECK constraints in supabase/migrations/20260929120000_user_categories.sql, and the glyphs are in src/components/dw/icons.tsx.Production is deployed only by GitHub Actions from main: migrations are applied with the Supabase CLI, then the site and functions are deployed to Netlify. Pull requests run lint, tests, a function typecheck, a build and a read-only migration dry-run. Versions are calendar-based (YYYY.MM.DD.N), generated on deploy and never edited by hand. Full details, required secrets and rules for schema changes are in DEPLOYMENT.md.
git checkout -b feature/your-featureCLAUDE.md)main; CI must passMIT — see LICENSE.
Developed with ❤️ by Jawaid · Powered 🚀 by Ibexoft Backend by Supabase · Hosted on Netlify
Record what you actually got done — and watch your momentum build.