Pencatat durasi browsing, pemblokir distraksi, dan review fokus opsional berbasis Claude (membutuhkan ANTHROPIC_API_KEY). Ekstensi Chrome mencatat waktu di tab aktif dan memblokir domain saat kuota habis; dashboard Next.js menampilkan tren, anggaran fokus, insight deterministik, serta usulan perubahan yang selalu membutuhkan persetujuan pengguna.
Versi repo dan ekstensi: 1.4.0. Lihat changelog dan paket ekstensi.
Kode Claude Focus Review dan paket ekstensi tersedia di repo. Ini bukan bukti bahwa migrasi, konfigurasi Claude, deployment, atau publikasi Chrome Web Store sudah selesai. Sebelum mengaktifkan fitur di environment tujuan, ikuti production release gate dan checklist toko.
Situs aplikasi: https://focus-tracker-one-wheat.vercel.app
- Next.js 16 (App Router) + React 19 + Tailwind 4
- Supabase (Auth, Postgres, RLS)
- Recharts
- Chrome Extension Manifest V3
Service worker menandai tab aktif yang terlihat, bukan idle, dan bukan chrome://. Durasi dijumlah per domain per hari kalender perangkat, lalu dikirim ke Supabase lewat RPC increment_daily_time yang atomik.
Aturan di /dashboard/aturan membatasi satu domain beserta subdomainnya — youtube.com mencakup m.youtube.com. Setiap aturan punya kategori, status aktif/nonaktif, dan jendela jam opsional (active_start_hour–active_end_hour; keduanya kosong berarti sepanjang hari). Saat kuota habis, content script menempel overlay motivasi di halaman itu.
Satu batas lintas-domain untuk total waktu browsing per hari, terpisah dari kuota per domain. Diatur di /dashboard/aturan, disimpan di tabel focus_settings (15–1440 menit; mengosongkan input mematikan anggaran). Dashboard menampilkan kartu progres dengan nada warna berjenjang: aman di bawah 75%, waspada pada 75–100%, terlampaui di atas 100%. Anggaran hanya memberi sinyal — ia tidak memblokir situs, karena pemblokiran tetap milik kuota per domain.
Dashboard meringkas rentang aktif menjadi maksimal empat insight berprioritas: status anggaran, domain dominan beserta porsinya, tren total dibanding periode sebelumnya, dan kepatuhan kuota. Semuanya dihitung dari data yang sama dengan grafik, tanpa panggilan jaringan tambahan.
Review fokus hanya berjalan setelah pengguna menyetujui pengiriman satu ringkasan. Server mengirim data agregat terbatas — domain teratas, durasi, kuota, dan tren — ke Claude melalui ANTHROPIC_API_KEY; isi halaman, URL lengkap, dan teks yang diketik tidak dikirim.
Claude mengembalikan review terstruktur berisi pola dan maksimal tiga usulan. Usulan set_domain_limit atau set_daily_budget disimpan sebagai tindakan pending; perubahan baru diterapkan setelah pengguna menekan Terapkan. RPC Supabase menjalankan penerapan secara atomik dan mengaktifkan kembali aturan domain yang dipilih.
Rentang dipilih lewat query ?rentang=7|14|30 pada /dashboard; nilai lain jatuh ke 7 hari. Grafik, insight, dan pembanding periode sebelumnya semuanya mengikuti rentang aktif.
Ekstensi mengirim heartbeat ke extension_status: versi, waktu heartbeat terakhir, sinkron terakhir, jumlah antrean lokal, dan pesan galat yang sudah disanitasi. Dashboard menerjemahkannya menjadi empat keadaan — tersambung, tertunda, offline, perlu perhatian.
/api/export mengirim 30 hari terakhir sebagai CSV ber-BOM (aman dibuka di Excel) berisi tanggal, domain, detik, menit, kategori, dan batas kuota.
Prasyarat: Node.js 22 (sesuai CI), npm, dan proyek Supabase. Dashboard bisa dijalankan tanpa ekstensi; pencatatan durasi dan pemblokiran membutuhkan ekstensi yang terhubung.
- Untuk database baru, jalankan
database/schema.sqldi Supabase SQL Editor. Untuk database yang sudah dipakai, ikuti urutan migrasi di bawah. - Salin
.env.exampleke.env.local, lalu isi variabel pada tabel berikut. Jangan commit.env.localatau kredensial privat. - Di Supabase Auth → URL Configuration, gunakan URL aplikasi yang sesuai dan izinkan callback
http://localhost:3000/auth/callbackuntuk pengembangan. Deployment memakai callback pada origin deployment sendiri. - Install dependensi dari lockfile dan jalankan dashboard:
npm ci
npm run dev- Buka
http://localhost:3000/login, daftar atau masuk, lalu konfirmasi email jika diwajibkan oleh konfigurasi Supabase.
| Variabel | Kebutuhan | Kegunaan |
|---|---|---|
NEXT_PUBLIC_SUPABASE_URL |
Wajib untuk dashboard | URL proyek Supabase |
NEXT_PUBLIC_SUPABASE_ANON_KEY |
Wajib untuk dashboard | Anon/publishable key; bukan service-role key |
NEXT_PUBLIC_APP_URL |
Sesuai environment | Origin aplikasi untuk callback email; default http://localhost:3000 |
ANTHROPIC_API_KEY |
Hanya untuk Focus Review | Kredensial Claude di server, termasuk environment deployment bila fitur diaktifkan |
CLAUDE_MODEL |
Opsional | Model Claude; default claude-sonnet-5-5 |
DATABASE_URL |
Hanya untuk status/migrasi database | Connection string Session pooler; tidak dibutuhkan dashboard saat runtime |
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY juga didukung bila NEXT_PUBLIC_SUPABASE_ANON_KEY tidak didefinisikan. Jangan memberi prefix NEXT_PUBLIC_ pada ANTHROPIC_API_KEY atau DATABASE_URL. Tanpa API key Claude, fitur non-AI tetap dapat digunakan setelah schema dan autentikasi Supabase siap; pembuatan review AI belum tersedia.
- Ekstrak paket ZIP, atau gunakan folder
extension/dari repo. - Buka
chrome://extensions→ Developer mode → Load unpacked → pilih folder yang berisimanifest.json. - Masuk ke dashboard pada profil browser yang sama, lalu gunakan Masuk & Sinkronkan di popup. Setelah terhubung, periksa heartbeat dan antrean di dashboard.
Ekstensi bawaan menunjuk ke situs produksi, bukan localhost. Mengubah .env.local hanya mengubah dashboard, bukan konfigurasi ekstensi. Untuk menguji dashboard lokal atau deployment sendiri, gunakan salinan ekstensi di profil pengembangan:
- Ubah
DEFAULT_DASHBOARDdiextension/background.jske origin target, misalnyahttp://localhost:3000. - Sesuaikan
host_permissionsdiextension/manifest.jsondengan origin tersebut (http://localhost:3000/*) dan URL proyek Supabase yang dipakai (https://<project-ref>.supabase.co/*). - Muat atau reload salinan ekstensi setelah perubahan, lalu masuk dan sinkronkan lagi. Jangan publikasikan paket yang masih memakai konfigurasi pengembangan.
Sesi diambil dari /api/extension/session pada tab dashboard. Cookie dan penyimpanan ekstensi terpisah antarprofil browser; login di profil lain tidak memperbarui sesi ekstensi ini.
Paket siap-pasang dibangun ulang dari folder extension/:
npm run package:extensionScript menulis public/downloads/fokus-kerja-v<versi-manifest>.zip secara deterministik — entri diurutkan, timestamp tetap, tanpa dependency npm atau binary eksternal — lalu mencetak jumlah entri, ukuran, dan SHA-256 sehingga paket yang dipublikasikan bisa diverifikasi ulang kapan saja.
Untuk rilis baru, samakan versi di package.json, metadata root package-lock.json, extension/manifest.json, dan src/lib/release.ts, lalu bangun ulang ZIP. Paket versi lama tetap disimpan agar tautan unduhan yang sudah dibagikan tidak terputus.
Terapkan migrasi ke database tujuan sebelum men-deploy kode yang membacanya. Urutan yang dipakai: migrasi dulu, baru deploy.
Migrasi dijalankan dari terminal, tanpa membuka SQL Editor:
npm run db:status -- --list # daftar file lokal; tidak terhubung ke database
npm run db:status # status/pratinjau; tidak menerapkan file migrasi
npm run db:push # MENERAPKAN migrasi yang belum tercatatnpm run db:push bukan dry-run. Script npm sudah menyertakan --apply, sehingga tidak perlu menambahkannya lagi. Periksa database tujuan dan hasil db:status sebelum menjalankan perintah ini.
Script membaca DATABASE_URL dari environment terminal, kemudian .env.local dan .env tanpa menimpa nilai yang sudah ada. Ambil dari Supabase Dashboard → Project Settings → Database → Connection string → Session pooler (URI); pakai port 5432, bukan Transaction pooler 6543, karena DDL butuh sesi tetap.
db:status tidak menjalankan file migrasi, tetapi tetap mengambil advisory lock dan membuat tabel public.schema_migrations bila belum ada. Jadi perintah ini bukan pemeriksaan read-only. Gunakan db:status -- --list jika hanya ingin melihat daftar file tanpa akses database.
Runner mencatat setiap versi setelah SQL berhasil. File yang sudah tercatat dilewati; penerapan manual melalui SQL Editor tidak otomatis tercatat. Status BELUM berarti belum ada catatan runner, bukan bukti bahwa tabelnya tidak ada. Migrasi yang tersedia bersifat idempoten; bila SQL berhasil tetapi pencatatan versi gagal, jalankan npm run db:push kembali setelah memeriksa penyebabnya.
Cara lama: tempel manual di SQL Editor
Buka Supabase Dashboard → SQL Editor, tempel isi file dari tabel di bawah, jalankan berurutan.
| Migrasi | Dibutuhkan oleh |
|---|---|
database/migrations/20260828_release_readiness.sql |
rules.active, tabel extension_status |
database/migrations/20260913_focus_budget.sql |
tabel focus_settings (anggaran fokus harian) |
database/migrations/20261008_claude_focus_review.sql |
tabel review, tindakan, RLS, dan RPC penerapan atomik |
Verifikasi schema lewat query yang terautentikasi atau probe PostgREST tersanitasi: tabel/kolom harus tersedia tanpa PGRST205 atau 42703, kontrol akses yang sudah ada tetap bekerja, dan RPC apply_focus_review_action tersedia sebelum Focus Review digunakan. Jangan cetak token, connection string, atau data pengguna ke log verifikasi.
Sebelum menyatakan rilis siap digunakan:
- Jalankan lint, typecheck, test, audit dependency produksi, build, dan pembuatan ZIP seperti pada bagian perintah.
- Verifikasi alur terautentikasi: login, sinkronisasi ekstensi, heartbeat, pencatatan tab aktif, dan overlay saat kuota domain habis.
- Pastikan anggaran fokus tampil konsisten di dashboard/popup dan tidak memblokir situs; uji filter rentang dan unduhan CSV.
- Untuk Claude, konfigurasi key server-side, buat satu review nyata setelah persetujuan, lalu terapkan satu usulan dan verifikasi perubahan kuota/anggaran. Build atau unit test saja tidak membuktikan integrasi ini hidup.
- Periksa halaman
/privacy,/panduan, dan unduhan ZIP pada deployment tujuan, lalu selesaikan checklist toko sebelum pengajuan. Bukti lokal atau CI tidak berarti ekstensi sudah terbit di Chrome Web Store.
| Perintah | Fungsi |
|---|---|
npm run dev |
Server pengembangan |
npm run build |
Build dashboard produksi |
npm start |
Menjalankan hasil build produksi |
npm test |
Suite test yang terdaftar di package.json |
npm run typecheck |
Pemeriksaan TypeScript |
npm run lint |
Pemeriksaan ESLint |
npm audit --omit=dev |
Audit dependency produksi pada saat dijalankan |
npm run package:extension |
Membangun ZIP sesuai versi manifest |
npm run db:status -- --list |
Daftar migrasi lokal, tanpa koneksi database |
npm run db:status |
Status/pratinjau migrasi; dapat membuat tabel pelacakan |
npm run db:push |
Menerapkan migrasi yang belum tercatat |
CI di .github/workflows/ci.yml menjalankan npm ci, lint, typecheck, test, audit produksi, dan build pada push/PR ke main. CI memakai konfigurasi Supabase placeholder, bukan bukti akses database produksi atau keberhasilan panggilan Claude.
- Dashboard adalah Next.js; pencatatan dan pemblokiran hidup di ekstensi.
- Semua tabel per-pengguna dan dilindungi RLS; increment memakai RPC agar bebas race.
- Ekstensi menyinkronkan sesi dengan menjalankan fetch same-origin di tab dashboard, lalu memakai token itu untuk PostgREST.
- Next.js 16 memakai
src/proxy.ts(middleware berganti nama menjadi proxy) dan mengirimsearchParams/paramssebagai Promise — keduanya wajib di-await. - Parameter
nextpada halaman login disaring dengansafeNextPathagar hanya path same-origin yang diterima. - Claude dipanggil hanya dari Route Handler Node.js dengan API key server-side; client tidak pernah menerima kredensial.
focus_reviews.input_snapshotmenyimpan ringkasan agregat untuk audit;focus_review_actionsmenyimpan status pending/applied/dismissed.- Model menghasilkan JSON terstruktur melalui SDK resmi; domain tindakan divalidasi ulang terhadap domain yang memang ada di snapshot.
- RPC
apply_focus_review_actionmenerapkan satu usulan dalam transaksi database dan tidak memberi Claude akses langsung untuk mengubah data.
- Changelog: perubahan per versi.
- Chrome Web Store: metadata, permission, aset, dan checklist pengajuan.
- Materi Claude Startups: aliran data dan materi teknis; data bisnis harus diisi dari fakta pemilik produk, bukan ditebak dari kode.