UR$&kNREM=2|}!Z1gJ+GnE
Q4FCWD07*qoM6N<$f?+e=8UO$Q
delta 471
zcmV;|0Vw{*1jGc8QwSFpAt|V%AYYMND1WR;L_t(Y$IVsEYQjJe{xT*+=mhFMWU>b1HoWl_lU+Dy2P$MGz^a&~6X9>t?h0p*^$%IXFA>
zeY4-rjKE(Tk$()V7M$7%!rczdW-}HTMG*$4r=Txy2p6*?fShbn%3OtM3gFXmh<^%F
zl7o1106+)eQ_4ceAr~-&-;jeiv#Yr)_nVY5*>9+UUWQ=&Ox8dETw3RXDa~|e0^okV
zF8p+k<7m;@1Yj)Zn*huOSgHm;wt>}xTc84&JyyY0{~9%`McNr8?R`IxcJC$$@DM%X
z>Fi4c>XK6h{FvUwgycebhfnp(Ie(?t(@-kjI>_jOL>0qM51et5^Mt&jVW)T0VD5wA;wycVVT_;mz$@=`
z!KmNOO&;~Tu+5ErC*H2!a{<(t#8wV6l7k{!`B#5!e57T?(|rHOz5%@i(FtM(xoH3Z
N002ovPDHLkV1l~3-If3V
diff --git a/public/character/torso/torso-1.png b/public/character/torso/torso-1.png
index 5a25f7f725a5329b4443a3bcc7d9bdeceb30b996..d3832932f58daf8301fc89a6513dc2178b1e0c97 100644
GIT binary patch
delta 300
zcmV+{0n`440+|DlQwSIWCk=tNc=C~3Cw~C}Nkl6Om_T9VL1ne
zJMi7PbMH3{I2?}Sqx=8~`cf+5N|}Bq+-UtyxY61{i1Gsfgjqm>9s*6^H;P0Qet)uB
z76JHp`_eIR1!$?n$#hP#7~2Knbl=T&*GOYCEpYxlVdfRA+jS$2I=4(M8%6m6Ufze%
z9*?$Zo;mw}83JOI=%2F^g>4C4V`g@Ek|DAVq|v69N(@?6E4fyGvGV@B!t=!~0FMvX
ydq=o?dIum`jBOJS9R}#V-g9$Wb{Rj8xq?S{Hj7^{gGUL}VE_OC07*qoL-!
zgHxK`AYB5d{sFP2EzrOv938_yLI*b&|AdAJnhJ7~3=D)$a1gzr!8qi68@l)I6l!W{
zxDPx$UtXT)dB5)u2nh-K6Er0<**vMe1FTL7
z8@~^EdAtW80#K~hF+>il(~?bK+GtDNF#(9jYnM%lQzma18}Zgt0GG8+2lC{$_)kk24XAf>B?t5uW&>xD|qcVPGKykT883pb7f
z7uF5vU=QMlUDq8bmr8pM)+xKW8B}mk{_PpX_e9TT>hwfAN*}Hi_WEy(AD&j~x}a&n
zqD(dqKyrHrBbFc?OR`wlyv{B9pEEvY69DfIgoOMbz5q30j?7H&2weaG002ovPDHLk
FV1hA!o-68xq?S{Hj7^{gGUL}VE_OC07*qoL)^mS!_#VWz1E;&1AO+Qdbvcxr_#5q4VH#M(>!MP|ku_QG`
zp**uBL&4qCHz2%`PaLS|i>HfYh{y4_lkB?>8A!Cx4|8k@khyYptM~D~?~1=9V~XUT
zs!f+Z%K!LL*m1q>ySDNvu@+hsIZvK&Y)-*pUMG1D-+T7^zl%#rNJ#vUFAAtXd2}I@
zq=t!M{1GmZqqjN~N}U7O{JimI!h$8|c5dbW=Cyaqzf%rtSf1|cs6Dz?x#~*rede9>
zy6?=KU@bhg{M&bi#|J{+e9;Z4%Bxw^u(5K6zOsvxnQ%eo&pDh7tGrJy+NTP2
literal 0
HcmV?d00001
diff --git a/public/character/torso/torso-4.png b/public/character/torso/torso-4.png
new file mode 100644
index 0000000000000000000000000000000000000000..9ebdc7b2289c147a2050d4a789ad941fa057e09b
GIT binary patch
literal 419
zcmeAS@N?(olHy`uVBq!ia0vp^0zj<5!3HFyJAa%3Qfx`y?k)@<7Q@wL`<4PJ&H|6f
zVg?31We{epSZZGe6l5>)^mS!_#VWz1E?>mYxeF*HS>hT|;+&tGo0?a`;9QiNSdyBe
zP@Y+mq2TW68xY>eC(gjY$nELk7~*mK?Ic4#W^pSTfzQaJ9Os$4as5H
z8oPUacQIS6_^gyPz6v1$R;`O!#S6YZi^%?bk>&AOu4k-|n=0kMm*0pvZtGCDSn%9(
z`y`=>KQk`rP7rHn(!BJtanAN%XQ!Tj+~m2%%uvv3=If95e=w%4Y!LkZA>|}6>=-;<
L{an^LB{Ts5&gP|!
literal 0
HcmV?d00001
From 61f98866e825047f46bcef4564f6ff5e99ee1758 Mon Sep 17 00:00:00 2001
From: pricewowen <231476659+pricewowen@users.noreply.github.com>
Date: Sat, 23 May 2026 23:24:36 -0600
Subject: [PATCH 3/5] avatar to main merge
I have completed the character creation screen on the avatar branch and am preparing to merge it into main.
---
README.md | 130 ++++---
app/(app)/avatar/page.tsx | 16 +-
app/character-creator/page.tsx | 15 +-
app/layout.tsx | 7 +-
app/onboarding/customize/page.tsx | 14 +
app/onboarding/quiz/page.tsx | 143 +-------
components/avatar/avatar-page-content.tsx | 191 +++++++++++
components/avatar/avatar-status-card.tsx | 30 +-
components/character/character-creator.tsx | 117 ++++++-
.../character/character-layer-preview.tsx | 17 +-
components/layout/global-bottom-nav.tsx | 19 +-
components/layout/logged-in-redirect.tsx | 10 +-
components/layout/onboarding-auth-gate.tsx | 11 +-
.../layout/onboarding-customize-auth-gate.tsx | 30 ++
.../layout/onboarding-quiz-auth-gate.tsx | 26 ++
.../onboarding/onboarding-customize-form.tsx | 94 +++++
.../onboarding/onboarding-quiz-form.tsx | 119 +++++++
components/shop/shop-content.tsx | 225 +++++++-----
components/ui/toast-provider.tsx | 79 +++++
lib/auth-helpers.ts | 42 ++-
lib/avatar-customization-storage.ts | 270 +++++++++++++++
lib/avatar-display.ts | 37 ++
lib/character/color-utils.ts | 10 +
lib/character/variant-validation.ts | 79 +++++
lib/onboarding-status.ts | 93 +++++
lib/profile-preferences-storage.ts | 24 +-
lib/routes.ts | 5 +
lib/shop-catalog.ts | 87 +++++
lib/shop-storage.ts | 212 ++++++++----
package-lock.json | 321 +++++++++++++++++-
package.json | 4 +-
public/character/eyes/.gitkeep | 0
public/character/head/.gitkeep | 0
public/character/pants/.gitkeep | 0
public/character/torso/.gitkeep | 0
scripts/generate-character-sprites.mjs | 159 +++++++++
...4180000_avatar_layer_colors_and_styles.sql | 67 ++++
...nboarding_flags_and_variant_validation.sql | 62 ++++
supabase/seed.sql | 32 +-
39 files changed, 2361 insertions(+), 436 deletions(-)
create mode 100644 app/onboarding/customize/page.tsx
create mode 100644 components/avatar/avatar-page-content.tsx
create mode 100644 components/layout/onboarding-customize-auth-gate.tsx
create mode 100644 components/layout/onboarding-quiz-auth-gate.tsx
create mode 100644 components/onboarding/onboarding-customize-form.tsx
create mode 100644 components/onboarding/onboarding-quiz-form.tsx
create mode 100644 components/ui/toast-provider.tsx
create mode 100644 lib/avatar-customization-storage.ts
create mode 100644 lib/avatar-display.ts
create mode 100644 lib/character/variant-validation.ts
create mode 100644 lib/onboarding-status.ts
create mode 100644 lib/shop-catalog.ts
delete mode 100644 public/character/eyes/.gitkeep
delete mode 100644 public/character/head/.gitkeep
delete mode 100644 public/character/pants/.gitkeep
delete mode 100644 public/character/torso/.gitkeep
create mode 100644 scripts/generate-character-sprites.mjs
create mode 100644 supabase/migrations/20260524180000_avatar_layer_colors_and_styles.sql
create mode 100644 supabase/migrations/20260524190000_onboarding_flags_and_variant_validation.sql
diff --git a/README.md b/README.md
index df5513c..4e8ab70 100644
--- a/README.md
+++ b/README.md
@@ -1,24 +1,34 @@
# Habit Pet
-Bootstrap repo for the Calgary hackathon Habit Pet project.
+Calgary hackathon app for building habits while caring for a customizable pixel pet.
## Stack
-- Next.js
-- Supabase
-- Vercel
-- GitHub
+- Next.js 16 (App Router)
+- Supabase (auth + Postgres)
+- Tailwind CSS + shadcn/ui
+- Vercel deployment via GitHub Actions
-## Scope of this bootstrap
+## Features
-This repo currently stops at:
+- **Auth** — email/password sign up and login
+- **Onboarding** — focus-topic quiz, then required avatar customization (DB-gated)
+- **Avatar** — layered sprite pet with per-layer styles and hex colors, naming, equipped shop items
+- **Habits** — catalog + custom habits, streaks, coin rewards
+- **Daily quiz** — wellness sliders and journal that affect pet mood
+- **Shop** — buy and equip items from `shop_items` / `user_items` (hats, glasses, backgrounds)
+- **Profile** — focus topic, vibe, and reminder preferences
-- Next.js app scaffold
-- Supabase auth wiring from the starter
-- environment variable template
-- deployment-ready project structure
+## User flow
-This repo does not yet include MVP feature pages, avatar logic, habits, streaks, or shop logic.
+1. Sign up / sign in
+2. **Step 1:** onboarding quiz (focus topic only)
+3. **Step 2:** avatar customization (required confirm + pet name)
+4. Main app via bottom nav: Pet, Habits, Quiz, Shop, Profile
+
+Onboarding progress is stored in the database (`profiles.onboarding_quiz_complete`, `profiles.onboarding_complete`, `avatar_state.avatar_customized`) and mirrored in auth metadata for fast client checks.
+
+On the **Pet** tab, users rename their pet and switch between **Overview** and **Customize** sub-tabs.
## Local setup
@@ -28,68 +38,80 @@ This repo does not yet include MVP feature pages, avatar logic, habits, streaks,
npm install
```
-2. Create local environment file:
+2. Generate placeholder character/shop sprites (if missing):
+
+ ```bash
+ npm run generate:sprites
+ ```
+
+3. Create local environment file:
```bash
cp .env.example .env.local
```
-3. Add Supabase values to `.env.local`:
+4. Add Supabase values to `.env.local`:
```env
NEXT_PUBLIC_SUPABASE_URL=
NEXT_PUBLIC_SUPABASE_ANON_KEY=
```
-4. Start the app:
+5. Apply migrations locally (Supabase CLI):
+
+ ```bash
+ supabase db reset
+ ```
+
+6. Start the app:
```bash
npm run dev
```
-5. Create a production build when needed:
+## Avatar customization data
- ```bash
- npm run build
- ```
+Stored in `avatar_state`:
+
+| Column | Purpose |
+|--------|---------|
+| `avatar_name` | Pet display name |
+| `skin_color` | Skin layer hex color |
+| `pants_style` / `pants_color` | Pants variant id + hex |
+| `shoe_style` / `shoe_color` | Shoes variant id + hex |
+| `torso_style` / `torso_color` | Torso variant id + hex |
+| `eye_type` / `eye_color` | Eyes variant id + hex |
+| `head_style` / `head_color` | Head variant id + hex |
+| `avatar_customized` | True after onboarding confirm |
+| `equipped_items` | Shop item ids currently equipped |
+
+Style columns are validated against allowed variant ids (`none`, `pants-1`, …). Shop accessories overlay equipped variants at render time without overwriting saved customization.
+
+Sprite assets: `public/character/{layer}/` (regenerate via `npm run generate:sprites`).
+
+## Shop
+
+- Sells character layer styles from `public/character/` (`head-1`, `pants-2`, etc.)
+- Shop item `id` = variant id, `type` = layer id (`head`, `pants`, `shoes`, `torso`, `eyes`)
+- Purchases unlock styles in `user_items`; owned styles appear in Customize and can be equipped from the shop
+- Catalog seeded in `supabase/seed.sql`
## Supabase
-Create a Supabase project, then copy the project URL and anon key from **Project Settings > API** into `.env.local` and Vercel project environment variables.
+- Migrations: `supabase/migrations/`
+- Seed: `supabase/seed.sql`
+- Config: `supabase/config.toml`
-The starter also supports Supabase's publishable key name, but this repo documents the anon key convention from the project plan.
+Production deploys on push to `main` via `.github/workflows/deploy.yml` (lint, build, `supabase db push --include-seed`, Vercel).
-### Database workflow
+## Routes
-- Schema migrations live under `supabase/migrations/`.
-- Baseline catalog data lives in `supabase/seed.sql` and is safe to rerun during deploys because it uses idempotent upserts.
-- Local CLI config lives in `supabase/config.toml` for `supabase start`, `supabase db reset`, and related commands.
-- Production database sync runs from GitHub Actions on `main` using `supabase db push --linked --include-seed` before the app deploy relies on those changes.
-
-## Deploy
-
-This repo includes `.github/workflows/deploy.yml`, which runs on pushes to `main`.
-
-The workflow:
-
-- checks out the repo
-- runs `npm ci`, `npm run lint`, and `npm run build`
-- links the Supabase CLI to the production project
-- applies pending database migrations and reruns `supabase/seed.sql` via `supabase db push --linked --include-seed`
-- deploys the app to Vercel production only after the database sync succeeds
-
-### Required GitHub secrets
-
-- `SUPABASE_ACCESS_TOKEN`
-- `SUPABASE_PROJECT_REF`
-- `SUPABASE_DB_PASSWORD`
-- `VERCEL_TOKEN`
-- `VERCEL_ORG_ID`
-- `VERCEL_PROJECT_ID`
-
-### Required Vercel environment variables
-
-- `NEXT_PUBLIC_SUPABASE_URL`
-- `NEXT_PUBLIC_SUPABASE_ANON_KEY`
-
-Frontend and backend are both served from this Next.js app. Supabase provides auth and database services.
+| Route | Access |
+|-------|--------|
+| `/` | Public |
+| `/auth/*` | Public |
+| `/onboarding/quiz` | Authenticated, quiz incomplete |
+| `/onboarding/customize` | Authenticated, quiz complete, avatar not confirmed |
+| `/avatar` | Authenticated, onboarding complete |
+| `/habits`, `/daily/quiz`, `/shop`, `/profile` | Authenticated app |
+| `/character-creator` | Redirects to `/avatar?tab=customize` |
diff --git a/app/(app)/avatar/page.tsx b/app/(app)/avatar/page.tsx
index da2310a..d56b728 100644
--- a/app/(app)/avatar/page.tsx
+++ b/app/(app)/avatar/page.tsx
@@ -1,19 +1,17 @@
-import { AvatarStatusCard } from "@/components/avatar/avatar-status-card";
-import { DailyQuizLinkButton } from "@/components/daily-quiz/daily-quiz-link-button";
-import { HabitTracker } from "@/components/habits/habit-tracker";
+import { Suspense } from "react";
+
+import { AvatarPageContent } from "@/components/avatar/avatar-page-content";
import { AppShell } from "@/components/layout/app-shell";
export default function AvatarPage() {
return (
-
+ Loading...
}>
+
+
);
}
diff --git a/app/character-creator/page.tsx b/app/character-creator/page.tsx
index cfe3e30..9d6407e 100644
--- a/app/character-creator/page.tsx
+++ b/app/character-creator/page.tsx
@@ -1,14 +1,7 @@
-import { CharacterCreator } from "@/components/character/character-creator";
+import { redirect } from "next/navigation";
+
+import { routes } from "@/lib/routes";
export default function CharacterCreatorPage() {
- return (
-
-
-
- Character Creation
-
-
-
-
- );
+ redirect(`${routes.avatar}?tab=customize`);
}
diff --git a/app/layout.tsx b/app/layout.tsx
index 916f99b..0326579 100644
--- a/app/layout.tsx
+++ b/app/layout.tsx
@@ -3,6 +3,7 @@ import { Geist } from "next/font/google";
import { ThemeProvider } from "next-themes";
import { GlobalBottomNav } from "@/components/layout/global-bottom-nav";
+import { ToastProvider } from "@/components/ui/toast-provider";
import "./globals.css";
const defaultUrl = process.env.VERCEL_URL
@@ -35,8 +36,10 @@ export default function RootLayout({
enableSystem
disableTransitionOnChange
>
- {children}
-
+
+ {children}
+
+