Imported from strinsx/pact-next-app (
AGENTS.md). Install upstream withnpx skills add strinsx/pact-next-app. Copyright stays with the author.
This is NOT the Next.js you know
This version has breaking changes — APIs, conventions, and file structure may all differ from your training data. Read the relevant guide in node_modules/next/dist/docs/ before writing any code. Heed deprecation notices.
Continuing Context
Goal
Build out a Pact accountability Next.js app's auth flow (Supabase email + Google OAuth), username onboarding, protected dashboard with a sidebar, and a rich home page of dashboard cards with mock data.
Constraints & Preferences
- App is Next.js 16, React 19, Tailwind v4, TypeScript strict;
@/*path alias maps to repo root. - Styling tokens:
--color-primary #1f2a5a,--color-secondary #4a90f5,--color-teal #56d9c8,--color-purple #a37af7,--color-muted #5c6b8a,--color-border #e4e7f5,--color-surface #ffffff. - Gradients preferred:
from-sky-400 to-purple; stat values & full name use gradient text; white bg +text-primaryfor create/submit buttons; rounded corners 8px (rounded-lg) for buttons, 20px (rounded-[20px]) for the modal. - All icons must be lucide-react (FontAwesome fully removed).
UserGroupdoesn't exist in lucide — useUserRoundPlus. profilestable schema:id uuid,user_id uuid(links toauth.users),full_name text,username text. RLS usesauth.uid() = user_id.- Colors/hardcoded hex in charts use
#38bdf8(sky),#a37af7(purple),#56d9c8(teal),#4a90f5(blue). - ESLint gates: no FontAwesome, no unescaped apostrophes, no
setStatesynchronously in effects (must useuseSyncExternalStorewith cached snapshot).
Progress
Done
- Supabase client
app/lib/supabase/client.ts—createBrowserClientusingNEXT_PUBLIC_SUPABASE_URL+NEXT_PUBLIC_PUBLISHABLE_KEY(oldutils/folder deleted). - SignUp.tsx — email/password signup +
profilesinsert (crypto.randomUUID()for id,user_id: data.user.id,full_name), Google OAuth, redirect to/auth/onboarding. - Login.tsx —
signInWithPassword, Google OAuth, post-auth redirect viagetPostAuthDestination, fixed pre-existingDon't→Don'tlint error. - Auth callback
app/auth/callback/page.tsx— creates profile from Googleuser_metadata.full_name ?? nameif missing, redirects by username presence. app/lib/auth/redirect.ts—getPostAuthDestination(userId)returns/if username exists else/auth/onboarding.- Onboarding
app/components/Onboarding.tsx+app/auth/onboarding/page.tsx— "Hello {firstName}", username field saved toprofiles.username, redirects/. - SideNav
app/components/SideNav.tsx— now collapsible (icon-onlyw-20when collapsed), stickyh-screenso it never exceeds viewport; top content (profile header, Notifications, Dark Mode, + Commitment) scrolls internally, Logout + Collapse pinned at bottom. Main nav: Dashboard/home, Analysis/analysis, Profile/profile— all with right-alignedChevronRight. "Management" heading above Groups/groups. Dark Mode/Notifications/Commitment buttons are UI-only (no handlers yet). - Home —
app/home/page.tsx(SideNav + content),app/page.tsxredirects to/home; protected viagetUser(); username-exists guard;m-autowrapper fixed scroll clipping caused byjustify-center. - StatCards.tsx — mock Personal Stats (Completion Rate 86%, Commitments 12, Day Streak 7), gradient values purple→secondary.
- CommitmentCard.tsx — mock commitments with status badges (teal/blue/purple), Create button opens modal, subtitle "Be specific - your group will see this".
- CommitmentModal.tsx — 20px radius, Title + Description fields, Escape/backdrop/X close, white submit button.
- Analysis cards — MonthlyAnalysisCard = "Daily Commitments" area chart, WeeklyAnalysisCard = "Weekly Consistency" area chart with browser-generated week labels
[month 1, 8, 15, current](cached module-level snapshot foruseSyncExternalStore). - AreaChart.tsx — reusable SVG area chart with monotone cubic (d3
curveMonotoneXstyle) curve, gradient fill. - Group section — Groups heading → GroupRankingCard.tsx (mock horizontal gradient bars with "ranking for this month" / "who has been the most consistent" muted text).
- GroupFeedCard.tsx — full-width, Discord-style emoji reactions (chips that toggle on click + "+" button that adds next emoji from
emojiPool), working add-comment input (Enter/Send), posts have group leader crown. Feed list is ah-96scrollable container using.feed-scrollclass (thin--color-secondarywebkit scrollbar in globals.css). 4 mock posts (albert, sarah, mike, priya). Comment icon/count removed per request. - Lucide refactor — removed all FontAwesome from SignUp, Login, Onboarding, SideNav.
- globals.css — added
.feed-scrollcustom webkit scrollbar (8px,--color-secondarythumb,--color-purpleon hover;scrollbar-width: thinfor Firefox). - Home.tsx currently has pre-existing eslint warnings: unused
firstName/usernamestate (welcome heading was removed).
In Progress
- (none)
Blocked
- (none)
Key Decisions
- Profile insert uses
user_id: data.user.idinstead ofid: data.user.idbecause RLS policy checksauth.uid() = user_id; id getscrypto.randomUUID(). - OAuth redirects go through
/auth/callbackbecause redirecting straight to/lost the OAuth code in the URL (server redirect to/auth/login). - Post-auth destination is driven by whether
profiles.usernameexists rather than always onboarding. - Charts are hand-rolled SVG (monotone curve) instead of a chart library — none installed.
- Scroll fix:
m-autowrapper instead ofjustify-centeron flex column to avoid clipped unreachable overflow. - Weekly labels read browser date via
useSyncExternalStore+ module-levelcachedLabelsto satisfyreact-hooks/set-state-in-effectand cached-snapshot rules. - Sidebar:
sticky top-0 h-screen+ internalflex-1 overflow-y-autoso Logout/Collapse stay visible without scrolling the page.
Next Steps
- Build the pending routes referenced by SideNav:
/analysis,/groups/create,/groups/join,/groups(currently 404). - Wire up Dark Mode (globals.css has a commented-out dark mode block), Notifications, and + Commitment buttons in SideNav.
- Connect commitment modal and feed reactions/comments to Supabase (currently client-state mock).
- Decide whether the welcome heading + firstName/username should return to Home, or remove the unused state to clear lint warnings.
- Optionally add server-side route protection (
middleware.ts+createServerClient) — current protection is client-side. - Verify Google OAuth redirect URL
/auth/callbackis whitelisted in Supabase auth settings.
Critical Context
- Email confirmation enabled means
data.userisnullafter signup — profile insert is skipped; a DB trigger onauth.usersis the more robust pattern. - User must have run the RLS SQL (profiles, groups, group_members, commitments, comments, reactions policies) in Supabase.
getPostAuthDestinationfailure mode: if the profiles query errors, it falls back to/auth/onboarding.- Home page container:
m-auto flex w-full max-w-7xl flex-col items-center gap-2 mt-10inHome.tsx— components arew-fulland follow it. npx tsc --noEmiterror.next/types/validator.ts ... Cannot find module '../../app/home/page.js'is stale and resolves once/homeroute build regenerates types (route now exists).
Relevant Files
app/lib/supabase/client.ts: browser Supabase client singleton factory.app/lib/auth/redirect.ts:getPostAuthDestinationusername-based redirect helper.app/components/SignUp.tsx,app/components/Login.tsx: email + Google auth flows.app/auth/callback/page.tsx: OAuth callback, profile creation fallback, destination redirect.app/auth/onboarding/page.tsx,app/components/Onboarding.tsx: username onboarding.app/components/SideNav.tsx: collapsible sticky sidebar, profile header, Notifications/Dark Mode/Commitment buttons, nav, logout, collapse toggle.app/home/page.tsx,app/page.tsx: protected dashboard layout;/redirects to/home.app/components/Home.tsx: dashboard composition (StatCards, CommitmentCard, analysis cards, Group section).app/components/StatCards.tsx,CommitmentCard.tsx,CommitmentModal.tsx,MonthlyAnalysisCard.tsx(Daily Commitments),WeeklyAnalysisCard.tsx,AreaChart.tsx,GroupFeedCard.tsx,GroupRankingCard.tsx: dashboard card components.app/globals.css: theme tokens incl.--color-teal/--color-purple+.feed-scrollwebkit scrollbar.app/components/Navbar.tsx: marketing navbar (links to/home,/about,/features,/auth/signup), still references/about//featuresroutes that may not exist.