# Family Hub

A centralized family organization system with tabs for chores, groceries, meal planning, family currency, and settings.

## Features
- Tabbed interface for chores, groceries, meals, combined Calendar (Family Hub agenda plus optional Google embed), currency, and family settings (profiles, stores, preferences)
- Optional banking mode in the Currency tab: per-person checking, savings, and charity accounts with transfer rules and HoH controls
- Banking safeguards and reporting: irreversible transfers into charity, immutable transaction reversals, and monthly statements (JSON/CSV)
- JSON-based data storage under `data/`
- Daily automated exports (`scripts/daily_export.php` and `export.php?type=…`)
- Mobile-friendly interface

## Setup
1. Clone this repository to your web server.
2. **Configure the environment**
   - With a browser, open the app URL (for example `http://your-server/familyHub/`). If `.env` is missing, you are redirected to **`install.php`**, which builds `.env` from **`env.example`**.
   - Alternatively, copy `env.example` to `.env` and edit `.env` by hand.
   - See [Environment variables](#environment-variables) below for where to get each value.
3. Ensure proper permissions on the `data` directory (and that the web server can create or write JSON files there).
4. Set up the daily cron job for exports (see `scripts/daily_export.php`). It writes one JSON file per type into `exports/` (for example `chores`, `groceries`, `meals`, **`meal_plans`**, `expenses`, `stores`, `grocery_lists`, `grocery_catalog`, `bank_transactions`). From the command line, the script exits with an error if `.env` is missing; complete browser setup first or copy `env.example` manually.
5. Access the hub at `http://your-local-ip/familyHub/` (adjust path and host to match your deployment).

### Google Cloud prerequisites (OAuth + APIs)

Most Google-related values come from **[Google Cloud Console](https://console.cloud.google.com/)**.

1. Create or select a **project**.
2. Open **APIs & Services → Library**, then enable any APIs you rely on (for example **Google Calendar API** and **Google Drive API** if you use those integrations).
3. Open **APIs & Services → OAuth consent screen** and complete it for your use case (Internal for Workspace-only, External for broader testing; test users may be required until published).
4. Open **APIs & Services → Credentials → Create credentials → OAuth client ID**. Choose **Web application** if your app uses a browser redirect.
5. Under **Authorized redirect URIs**, add the exact URL you will put in **`GOOGLE_REDIRECT_URI`** in `.env` (scheme, host, path, no trailing slash unless your app uses one).

Use the official [Google Cloud OAuth documentation](https://developers.google.com/identity/protocols/oauth2) for details if anything in the console changes.

## Environment variables

Values are defined in **`env.example`**. The install wizard reads that file only; use this section to learn **where to obtain** each value.

### Google API credentials

| Variable | What it is | Where to find it |
| -------- | ----------- | ---------------- |
| **`GOOGLE_CLIENT_ID`** | Public OAuth 2.0 client ID for your app | **Google Cloud Console → APIs & Services → Credentials**. Open your **OAuth 2.0 Client ID** (Web application). Copy **Client ID**. |
| **`GOOGLE_CLIENT_SECRET`** | Secret for the same OAuth client | Same credential row. Copy **Client secret** (treat it like a password; never commit it). |
| **`GOOGLE_REDIRECT_URI`** | URL Google redirects to after OAuth | Must match **exactly** one of **Authorized redirect URIs** on that OAuth client. Set it to your real callback URL (e.g. `https://yourdomain.com/family-hub/auth/google/callback`). Use the same scheme and path your server exposes. |

### Google Calendar

The **Calendar** tab embeds your family calendar in the hub using these values (HTTPS Google Calendar URLs only; optional iframe `src` is extracted from pasted HTML).

| Variable | What it is | Where to find it |
| -------- | ----------- | ---------------- |
| **`GOOGLE_CALENDAR_ID`** | ID of the calendar to target (APIs, sharing, embeds) | **[Google Calendar](https://calendar.google.com/)** → hover the calendar in the sidebar → **⋮** → **Settings and sharing** → scroll to **Integrate calendar** → **Calendar ID** (often an email or `...@group.calendar.google.com`). If **`GOOGLE_CALENDAR_EMBED_CODE`** is unset, the tab builds a standard embed URL from this ID. |
| **`GOOGLE_CALENDAR_EMBED_CODE`** | Embed URL or full iframe HTML from Google | Same **Settings and sharing** page → **Integrate calendar** → copy the **embed code** (iframe) or the `https://calendar.google.com/...` URL from the `src` attribute. Prefer this when you want Google’s chosen view options. Leave the placeholder `your_embed_code_here` unchanged only if you use **`GOOGLE_CALENDAR_ID`** instead. If a long iframe line misbehaves in `.env`, wrap the whole value in **double quotes**. |

### Google Drive

| Variable | What it is | Where to find it |
| -------- | ----------- | ---------------- |
| **`GOOGLE_DRIVE_FOLDER_ID`** | Folder used as export or attachment root | **[Google Drive](https://drive.google.com/)** → open the folder in the browser. The URL looks like `https://drive.google.com/drive/folders/THIS_PART_IS_THE_ID` — copy **`THIS_PART_IS_THE_ID`**. |

### Application settings

| Variable | What it is | Where to find it |
| -------- | ----------- | ---------------- |
| **`APP_ENV`** | Logical environment name | Your choice (e.g. `development`, `staging`, `production`). Used for app behavior toggles if the code checks it. |
| **`APP_DEBUG`** | Verbose errors / debug mode | `true` or `false` (strings as in `env.example`). Only use **`true`** on trusted local or staging servers. |
| **`APP_URL`** | Base URL of this app | Same origin users use in the browser (e.g. `https://example.com/family-hub`), no trailing path beyond the app root unless your deployment requires it. Should align with **`GOOGLE_REDIRECT_URI`** host/path where applicable. |

### Export settings

| Variable | What it is | Where to find it |
| -------- | ----------- | ---------------- |
| **`EXPORT_FREQUENCY`** | How often exports are expected to run | Your choice (e.g. `daily`). Match how you schedule **`scripts/daily_export.php`** in cron. |
| **`EXPORT_RETENTION_DAYS`** | How long to keep export files (interpreted by app logic) | Integer number of days (e.g. `30`). |

### MCP / AI automation (optional)

These enable **machine-to-machine** access to the same JSON APIs the browser uses. When `MCP_API_TOKEN` is non-empty, API requests may send `Authorization: Bearer <MCP_API_TOKEN>`; the app then treats the request as the Head of household identified by `MCP_ACTOR_PERSON_ID` (PIN-verified semantics), without a browser session.

| Variable | What it is | Where to find it |
| -------- | ----------- | ---------------- |
| **`MCP_API_TOKEN`** | Shared secret for automation (Cursor MCP, scripts, etc.) | Generate locally (treat like a password). Example: `openssl rand -hex 32`. Paste into `.env`. Never commit the real value. If left **empty**, Bearer impersonation is disabled and only normal browser sessions work. |
| **`MCP_ACTOR_PERSON_ID`** | Which person the token impersonates | Open **`data/people.json`** (after setup) and copy the **`id`** field for your **Head of household** profile. Must match a person whose **`role`** is `head_of_household`. Wrong or non-head IDs are ignored and the token will not authenticate. |

**Security:** The token grants the same power as a logged-in, HoH-verified session (create chores, manage people, grocery data, etc.). Use **HTTPS** in production. Rotate the token by changing `.env` and restarting anything that caches env.

**Cursor MCP:** The MCP server is plain PHP (no Node): **`mcp/familyhub/server.php`**. Register it in Cursor **Settings → MCP** with the absolute path to PHP and the script, and set environment variables `FAMILYHUB_BASE_URL` (your hub’s **`…/api`** URL, e.g. `http://localhost/family-hub/api`) and `FAMILYHUB_MCP_TOKEN` (same value as **`MCP_API_TOKEN`**). Tools exposed: `familyhub_health`, `familyhub_get_snapshot`, `familyhub_calendar_events`, `familyhub_post_api` (allowlisted POST endpoints only).

Example `mcp.json` server entry (adjust paths and env):

```json
{
  "mcpServers": {
    "familyhub": {
      "command": "php",
      "args": ["/full/path/to/familyHub/mcp/familyhub/server.php"],
      "env": {
        "FAMILYHUB_BASE_URL": "http://localhost/family-hub/api",
        "FAMILYHUB_MCP_TOKEN": "paste-the-same-secret-as-MCP_API_TOKEN"
      }
    }
  }
}
```

## Calendar preferences (family settings)

These are **not** in `.env`; they live in `data/family_settings.json` and are edited under **Family settings → Calendar and dates**.

| Key / UI | Purpose | Notes |
| -------- | -------- | ----- |
| **Family timezone** | US IANA zone (e.g. `America/Denver`) | Dropdown of US zones only. Drives **today** and date ranges on the **Calendar** tab agenda. Options are the standard PHP US timezone identifiers (e.g. **America/***, **Pacific/Honolulu**). |
| **Notify when two-way Google Calendar sync is available** (`calendar_two_way_google`) | Preference for a future OAuth feature | Default off. **Does not enable sync today.** The Calendar tab still uses only the Google **iframe embed** from `.env` (if set). The Family Hub agenda is built from chores, meals, groceries, expenses, bills, and birthdays in this app—not from Google’s API. |
| **Monthly bill reminders** | `calendar_bill_days` | JSON array of `{ "dayOfMonth": 1–31, "title": "Rent" }`. Shown on the agenda on that calendar day each month. |

**API:** `GET api/calendar_events.php?from=YYYY-MM-DD&to=YYYY-MM-DD` returns `{ success, events, rangeStart, rangeEnd, today }` for the signed-in profile (same session rules as other APIs). Omit `from`/`to` to use the default two-week window from **today** in the family timezone.

## Banking mode (optional)

Enable this in **Family settings → Chore economy → Banking system**.

- **Accounts per person**
  - `checking_balance`: primary spendable chore income balance.
  - `savings_balance`: earns the configured monthly rate, compounded daily via lazy apply when balances are read/written.
  - `charity_pending_balance`: funds committed to charity and not transferable back out.
  - `charity_donated_total`: historical amount already logged as donated by HoH.
- **Income routing**
  - Approved chore payouts and HoH manual credits flow through checking.
  - Optional auto split sends a percent of each incoming credit to savings and charity.
  - Optional round-up rule moves each income credit’s fractional remainder to either savings or charity.
- **Permissions**
  - Any active person can move their own funds between checking/savings/charity, but cannot transfer out of charity.
  - Verified HoH can add checking credits/debits, log charity outflows, and reverse transactions with immutable compensating entries.
- **Donation goals**
  - Per-person monthly donation goals with progress based on current-month charity outflow totals.
- **Statements**
  - `GET api/bank_statement.php?month=YYYY-MM&format=json|csv[&person_id=...]`
  - Non-HoH users can fetch only their own statement; verified HoH can request any person.

## NFC chore submission (HoH)

NFC support uses per-chore tokens plus per-person submitter tokens so a scan can submit as a specific person and credit that person after HoH approval.

### Family settings for NFC

In **Family settings → Chore economy**:

- **NFC base URL override** (`nfc_base_url`): optional app base URL used when generating NFC links. Leave blank to auto-detect from the current request.
- **NFC scan cooldown seconds** (`nfc_scan_cooldown_seconds`): optional soft replay dampener (0-600 seconds).
- **Show NFC confirmation page after scan** (`nfc_show_confirmation`): when off, NFC scans redirect directly to chores.
- **Emergency NFC actions**:
  - Disable all chore NFC links
  - Rotate all person NFC submitter tokens

### HoH setup checklist (per chore)

1. Open **Chores** as a verified Head of household.
2. For a chore, optionally enable **Allow anyone to complete this chore**.
3. Click **Generate NFC links**.
4. Copy each person-specific URL and write it to that person’s NFC card/tag (NDEF URI).
5. Label tags physically (name + chore).
6. Test scan once per person.

### NFC URL format

Generated links use:

`/api/chore_submit_nfc.php?id=<chore_id>&token=<chore_token>&person_token=<person_token>`

- `token` identifies the chore NFC token.
- `person_token` identifies who is submitting (credit recipient on approval).

### Completion and credit rules

- **Assignee-only chore** (`anyone_can_complete = false`): only assignees may submit, even with valid person tokens.
- **Anyone-can-complete chore** (`anyone_can_complete = true`): any valid person token holder may submit.
- Credit is always applied only on HoH approval (not at scan time), to the `submitted_by` person on the pending submission.

### Troubleshooting

- **Invalid chore token**: regenerate chore NFC links and rewrite tags.
- **Invalid submitter token**: rotate that person token and regenerate links.
- **Already pending review**: wait for HoH approve/reject.
- **Chore inactive**: reactivate/update chore before scanning again.
- **Lost/shared card**: rotate token(s) or disable chore NFC immediately.

### Security notes

- NFC links are bearer-style secrets. Anyone with a valid URL can submit as that URL’s person token.
- Use HTTPS in production and rotate tokens on loss/suspected leakage.
- Keep HoH approval enabled as the payout gate.

## Directory Structure
```
familyHub/
├── api/                  # JSON endpoints (chores, groceries, meals, people, settings, expenses, banking, calendar_events, NFC helpers, …)
├── assets/               # Static assets
│   ├── css/              # CSS files
│   │   └── style.css
│   ├── js/               # JavaScript files
│   │   └── main.js
│   └── img/              # Images
├── config/               # Configuration files
│   ├── config.php
│   └── env.php           # Loads .env
├── data/                 # JSON data storage (not tracked in git)
│   ├── chores.json       # Chores, assignments, submissions, reviews
│   ├── people.json       # Profiles, roles, PIN hashes, currency balances
│   ├── family_settings.json   # Currency labels, US timezone, week start, calendar merge flag, bill reminders, etc.
│   ├── expenses.json     # Head-of-household expense log
│   ├── bank_transactions.json # Banking ledger (credits, debits, transfers, outflows, reversals)
│   ├── stores.json       # Grocery store definitions
│   ├── grocery_lists.json     # Per-store shopping lines
│   ├── grocery_catalog.json   # Deduped catalog / recurring metadata
│   ├── groceries.json    # Legacy single-file groceries; migrated when detected
│   ├── meals.json        # Meal library (titles, tags, ingredients, shopping items)
│   └── meal_plans.json   # Active weekly plan: `weekStart` (Monday YYYY-MM-DD) and `slots` 0–6 × breakfast/lunch/dinner → meal id or empty
├── includes/             # PHP includes (db, persona, tab helpers, `api_bootstrap.php`, …)
│   ├── header.php
│   ├── footer.php
│   ├── db.php            # JSON file I/O (flock)
│   └── utils.php         # Shared helpers
├── exports/              # Export output (not tracked in git)
├── scripts/              # Cron / CLI helpers
│   └── daily_export.php
├── mcp/familyhub/      # Optional MCP stdio server (PHP only): `server.php`
├── tabs/                 # Tab views included by `index.php`
│   ├── chores.php
│   ├── groceries.php
│   ├── meals.php
│   ├── calendar.php
│   ├── currency.php
│   └── settings.php
├── env.example           # Environment template (parsed by install.php)
├── install.php           # Web wizard to create .env when missing
├── .gitignore
├── .cursor/              # Cursor editor configuration and rules
├── readme.md
├── index.php             # Main entry point
├── AGENTS.md             # Agent / contributor notes
└── export.php            # `?type=` JSON export (same types as daily script)
```