# Baby Art Class

A complete, zero-build children's art-class website: a warm public gallery and portfolio site, plus a private teacher studio for adding artists, uploading artwork and managing categories.

The site works immediately in **Demo Mode** with the included sample class. Add a Firebase project when you want shared, secure, real-time data.

---

## Quick start — Demo Mode

Open a terminal in this folder and run:

```sh
node server.js
```

Then visit **http://localhost:4173**.

The demo studio is pre-filled with 6 young artists, 4 categories and 12 original sample artworks.

### Demo teacher sign-in

| Field | Value |
| --- | --- |
| Email | `teacher@babyartclass.example` |
| Password | `sunshine123` |

The login form fills these in automatically in Demo Mode.

> **Demo Mode is a public preview, not real security.** The credentials live in `js/config.js`, changes are stored only in the current browser, and nothing is shared with visitors or other devices. Never enter real or private student information in Demo Mode.

### What Demo Mode does

- Seeded class content comes from `js/sample-data.js`.
- Metadata is saved in `localStorage`; uploaded images are saved in `IndexedDB`.
- Open tabs sync through `BroadcastChannel` and the `storage` event.
- Uploads are compressed in the browser before storage.
- The teacher studio can add, edit and delete artists, artwork and categories, reset the sample class, and sign out.
- Public pages read the same demo data, so changes appear on the gallery and portfolio pages automatically.

To wipe demo edits, sign in and choose **Reset the sample studio** in the Overview quick actions.

---

## Public pages

| Page | What it does |
| --- | --- |
| `index.html` | Hero, live class stats, featured artwork, artist previews, gallery preview and studio story. |
| `artists.html` | Searchable directory of every young artist. |
| `portfolio.html?student=<id>` | One artist's illustrated portfolio, with category filters and related artists. |
| `gallery.html` | Masonry gallery with search, medium and artist filters, sorting, shareable URLs and a lightbox. |
| `admin.html` | Teacher authentication and the complete studio dashboard. |
| `404.html` | Friendly not-found page served by the local server and Firebase Hosting. |

Every public page is responsive, includes reduced-motion support, and uses the shared cream-and-pastel design system in `css/styles.css`.

---

## Teacher studio

Sign in at **`admin.html`**.

- **Overview** — artist/artwork/category counts, published vs hidden pieces, recent uploads, quick actions and artist spotlights.
- **Our artists** — search the class, add a new artist, edit public profile details, preview a portfolio, or remove an artist and their artwork.
- **Artwork** — search, filter by artist/category/status, preview in the lightbox, edit details, replace images, publish/hide, feature, or delete.
- **Categories** — add categories with custom colors, rename/recolor them, or delete unused ones.
- **Sample content** — in an empty studio, load the ready-made sample class once.
- **Demo reset** — restore the original demo class at any time (Demo Mode only).

Adding or editing artwork updates one record. That same record powers the class gallery and the artist's portfolio, with live subscriptions keeping every open page in sync.

### Upload rules

- Accepted upload types: JPG, PNG, WebP and GIF.
- Maximum source file: **10 MB**.
- Images are compressed client-side to WebP, maximum dimension **1600 px**, quality **0.86**.
- Invalid files show an inline error; uploads show progress and success/error toasts.
- Demo images are stored in IndexedDB. Live images go to Firebase Storage at `artworks/<artworkId>/<timestamp>.webp`.

---

## Connect Firebase

Demo Mode stays active while the Firebase config is blank. To use real authentication, Firestore and Storage:

### 1. Create the Firebase project

1. Open the [Firebase Console](https://console.firebase.google.com/) and create a project.
2. Add a **Web app** to the project.
3. Copy the web-app configuration values.

### 2. Enable the services

1. **Authentication → Sign-in method:** enable **Email/Password**.
2. **Authentication → Users:** create exactly one teacher account. Copy its **UID**.
3. **Firestore Database:** create a database.
4. **Storage:** create a storage bucket.

### 3. Fill in `js/config.js`

Paste the web config into the `firebase` object and add the teacher UID:

```js
firebase: {
  apiKey: "...",
  authDomain: "your-project.firebaseapp.com",
  projectId: "your-project",
  storageBucket: "your-project.firebasestorage.app",
  messagingSenderId: "...",
  appId: "...",
  teacherUid: "THE_TEACHER_AUTH_UID"
}
```

`apiKey` is a public Firebase web-app identifier, not a server secret. Access is controlled by Authentication and the security rules, so never put service-account credentials in this file.

If only some Firebase values are filled in, the site intentionally stops and shows a configuration error instead of falling back to Demo Mode.

### 4. Publish the security rules

Replace `REPLACE_WITH_TEACHER_UID` with the same teacher UID in **both**:

- `firestore.rules`
- `storage.rules`

Publish them in the Firebase Console (Firestore → Rules, Storage → Rules), or with the Firebase CLI:

```sh
npm install -g firebase-tools
firebase login
firebase use --add
firebase deploy --only firestore:rules,storage
```

### 5. Deploy the site (optional)

`firebase.json` is configured to host this folder as a static site:

```sh
firebase deploy --only hosting
```

You can also host the files on any static host. There is no build step.

### 6. Sign in and seed the class

1. Open `admin.html` on the deployed site.
2. Sign in with the teacher account created in Firebase Authentication.
3. Starter categories are created automatically when the categories collection is empty.
4. To load the sample class into a brand-new empty studio, use **Load sample class** on the Overview page.

---

## Data model

### `students/{studentId}`

| Field | Type | Notes |
| --- | --- | --- |
| `id` | string | Must match the document ID. |
| `name` | string | 2–40 characters, public display name only. |
| `bio` | string | Up to 320 characters. |
| `funFact` | string | Up to 160 characters. |
| `favoriteMedium` | string | Optional category ID. |
| `avatar` | string | One emoji, up to 8 characters. |
| `accent` | string | `pink`, `sky`, `sunflower`, `mint`, `lavender` or `peach`. |
| `createdAt` | ISO string or timestamp | Set by the studio. |

### `categories/{categoryId}`

| Field | Type | Notes |
| --- | --- | --- |
| `id` | string | Must match the document ID. |
| `name` | string | 2–30 characters. |
| `color` | string | Six-digit hex color, e.g. `#E18CAB`. |
| `order` | number | Controls filter order. |

### `artworks/{artworkId}`

| Field | Type | Notes |
| --- | --- | --- |
| `id` | string | Must match the document ID. |
| `title` | string | 2–80 characters. |
| `description` | string | 1–700 characters. |
| `studentId` | string | Artist document ID. |
| `categoryId` | string | Category document ID. |
| `imageUrl` | string | Storage download URL or local sample path. |
| `storagePath` | string | Firebase Storage object path (empty for sample images). |
| `featured` | boolean | Featured on the homepage. |
| `published` | boolean | Public visibility. Only `published == true` is queried publicly. |
| `createdAt` | ISO string or timestamp | Set by the studio. |

---

## Security model

- **Visitors read only published artwork.** Firestore rules allow public reads of artists and categories, but an artwork document is readable only when `published == true`. Public pages also query with `where("published", "==", true)`.
- **Only the teacher can write.** Firestore and Storage writes require the Auth UID configured in both rule files.
- **No student accounts.** Children and visitors never sign in, upload, or manage data.
- **One teacher account.** If `teacherUid` is set in `js/config.js`, any other signed-in account is rejected by the client as well as by the rules.
- **Public-safe profile fields only.** Do not add addresses, phone numbers, school names, medical details or other private information to an artist profile or artwork description.
- **Demo Mode is not protected.** Its credentials are visible in the page source and its data is local to one browser.

Firebase Storage download URLs are bearer URLs: once a published image URL is shared, it may remain accessible even if the artwork is later unpublished. Treat every published artwork image as public. Unpublished records are excluded from all site queries and views.

---

## Project structure

```
.
├─ index.html                 Public home
├─ artists.html               Artist directory
├─ portfolio.html             Individual portfolio shell
├─ gallery.html               Class gallery
├─ admin.html                 Teacher studio
├─ 404.html                   Not-found page
├─ css/
│  ├─ styles.css              Public design system and page styles
│  └─ admin.css               Teacher studio styles
├─ js/
│  ├─ config.js               Demo credentials, upload settings, Firebase config
│  ├─ sample-data.js          Sample artists, artwork and categories
│  ├─ store.js                ArtStore: auth + Demo/Firebase data adapter
│  ├─ ui.js                   Shared cards, icons, lightbox, dialogs, toasts
│  └─ pages/                  One page controller per page
├─ assets/
│  ├─ art/                    Hand-authored sample artwork SVGs
│  ├─ studio-still-life.svg   Login artwork
│  └─ favicon.svg
├─ firestore.rules
├─ storage.rules
├─ firebase.json
├─ server.js                  Zero-dependency local preview server
└─ package.json
```

`ArtStore` chooses Demo Mode or Firebase at startup. Both modes expose the same API to the pages, so the public site and teacher studio share one data path.

---

## Troubleshooting

**“The studio couldn't open” or a Firebase configuration error**  
Check that `apiKey`, `projectId`, `storageBucket` and `appId` are all filled in, and that Auth, Firestore and Storage are enabled.

**“Missing or insufficient permissions”**  
The teacher UID in `firestore.rules` / `storage.rules` does not match the signed-in account, or the rules were not published. Replace both placeholders and redeploy.

**Wrong account keeps getting signed out**  
`firebase.teacherUid` in `js/config.js` must match the teacher's Authentication UID.

**Images are broken after unpublishing**  
Storage rules only allow public reads while the Firestore artwork document is published. Republish the artwork or keep it hidden from the public site.

**Port 4173 is busy**  
Run with a different port: `PORT=5000 node server.js` (macOS/Linux) or `$env:PORT=5000; node server.js` (PowerShell).

**Demo changes disappeared**  
Demo data is stored per browser. Private windows, blocked storage, clearing site data, or a different browser starts a fresh sample class.

**I changed the sample files but the site still shows old demo data**  
The browser keeps the seeded demo database. Sign in and use **Reset the sample studio**, or clear the site's local storage for this origin.

---

## Browser support

Modern Chrome, Edge, Firefox and Safari. The site uses CSS custom properties, `color-mix()`, `:has()` in the studio form, `dialog`-free custom overlays, `IntersectionObserver`, IndexedDB and `BroadcastChannel` where available. Demo Mode degrades gracefully when storage is blocked, though changes then last only for the current page.
