Skip to content

About

Commercial web platform and digital fine arts portfolio for Anugruja Arts Studio (Anuradha Govarthanan)

Topics

Resources

Stars

0 stars

Watchers

1 watching

Forks

Latest commit

Β 

History

148 Commits

Folders and files

NameName
Last commit message
Last commit date
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

🎨 Anugruja Arts Studio β€” Official Website & Live Content Platform

Next.js React TypeScript Deployed on Vercel

The production website for Anugruja Arts Studio β€” the studio of Master Artist and Curator Anuradha Govarthanan (20+ years of practice across watercolours, acrylics, oils, Tanjore, Mysore, and Kerala mural styles).

This is a full Next.js App Router application: a public gallery site, a built-in password-protected admin console, a Git-backed CMS pipeline (content is committed to this repo through the GitHub API), and an AI studio assistant ("Chitra") that answers visitor questions from the same content file. No database, no server to babysit β€” the repo is the CMS.


✨ What's Inside

Area What it does
Public site (src/app/(site)/) Home, Art for Sale, Classes & exam coaching (NATA/NID/NIFT/BFA…), About the artist, events, commission & inquiry bridges (WhatsApp / email)
Artwork experience 3D drag carousels, a global lightbox (click any painting to enlarge β€” optimized variants, so it opens instantly), scroll reveals, reduced-motion support
Admin console (/admin) Password-gated studio console: photo-first upload wizard (pick photos β†’ describe one at a time β†’ crop/rotate/straighten β†’ publish one or the batch), gallery managers, home-shortcut & events editors, brand/SEO settings, live preview editor β€” every edit is staged and reviewed before one explicit Commit
Chat assistant "Chitra" Floating chat widget backed by OpenRouter (server-side key only). Its knowledge is generated from the same site.json the site renders β€” new paintings are in the bot's context automatically
Image pipeline next/image + a custom optimizer: on upload, originals are compressed client-side; on display, right-sized variants (640/1080/1920) are served and cached

Public UI layout invariants

  • The hero announcement stays in flow. Only Header owns the bounded brand/announcement swap after the hero scrolls past; focus, hover and hidden tabs pause it. Reduced motion uses a static announcement. Desktop navigation starts at 1280px; smaller widths use the drawer.
  • Event galleries reuse Carousel3D's deck presentation with no autoplay and all event photos, not a cropped three-image mosaic. The shared frame uses natural dimensions from imageSize() (or image load for new uploads), constrained to the measured stage. Stage measurements must not feed back into the stage's own height.
  • The collector showcase grows with its content rather than clipping to one viewport. Medium, dimensions and status come from each artwork; certification, shipping insurance and scarcity must not be promised without verified content. Price confirmation and WhatsApp inquiry helpers remain authoritative.
  • Keep the hero display title's 1.24 line height and descender padding in both responsive rules. Do not restore a filter on the gradient-clipped title.
  • Autoplay is disabled for reduced motion and lite devices. Keep idle carousels off the shared frame loop. Run npm run typecheck, npm run build and npm run audit:css after changing these surfaces.

πŸ›οΈ How the Content Pipeline Works

Everything the site shows β€” sections, artworks, prices, statuses, events, chatbot knowledge, quick-nav β€” lives in one file:

content/site.json        ← the single source of truth
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   1. Stage edits in the admin console
β”‚   Admin console /admin β”‚      (in-memory / localStorage, nothing live yet)
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
            β”‚ 2. One explicit "Commit Changes"
            β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   3. POST /api/content β†’
β”‚  GitHub Contents API   β”‚      commits content/site.json (and any uploaded
β”‚  (via /api/github)     β”‚      images in public/images) to this repository
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”¬β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜
            β”‚ 4. Push to main triggers
            β–Ό
β”Œβ”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”   5. Vercel rebuilds; site reads site.json
β”‚   Live website (Vercel)β”‚      at build/request time; the chatbot context
β””β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜      is regenerated from the same file

Consequences of this design:

  • Nothing goes live until you commit. Admin edits are staged and fully discardable.
  • Content is versioned. Every change is a Git commit β€” full history, easy revert.
  • The chatbot never drifts. src/lib/chatbot/context.ts builds its knowledge from the committed site.json, so uploaded/edited paintings update the bot automatically.

Galleries & artwork statuses

Artworks live in typed collections (GalleryKey in src/lib/types.ts, described by GALLERY_CATALOG). One of them β€” Art for Sale β€” is the commerce collection and the only place the public pages render a price; the rest are showcase galleries (Featured, Commissions, Classes, Watercolour, Workshops, Testimonials, Awards) that never show sales metadata.

Whether a painting is for sale is therefore an explicit per-artwork decision, asked as the first question of the upload flow:

  • Just show it (default) β€” the photo goes to a showcase gallery. No price is asked, none is stored, and no availability badge is published.
  • Sell it β€” it goes to Art for Sale and the form asks for Price (β‚Ή) and Availability (Available / Reserved / Sold).

Filenames still auto-suggest a collection (krishna-sale-2.jpg β†’ Art for Sale, student-boat.jpg β†’ Classes & Courses) and a photo with no hint lands in a showcase gallery β€” never in the sale catalogue by accident. Flipping the switch is non-destructive: it remembers the showcase gallery you had chosen and restores it.

Price privacy: nothing public until an admin saves it. Every artwork carries a priceConfirmedAt stamp that every admin price-edit path (gallery manager, section editor, upload wizard β€” both this console and the standalone admin portal) sets the moment a price is saved. Visitor-facing surfaces β€” the Art for Sale grid, the home carousel, the lightbox, the chatbot and the WhatsApp inquiry prefill β€” render an unconfirmed price as XXXX plus a "Contact the studio for the actual cost" note, while admin screens keep showing the raw figures. The shared rule lives in src/lib/price.ts; the seeded placeholder prices were stripped from content/site.json, and GET /api/content strips unconfirmed prices for anonymous callers, so the raw JSON can never leak what the pages don't show.

Uploading artwork, and framing it

The wizard is photo-first, exactly like a phone album:

  1. Pick one photo or many (file picker or drag & drop). Canvas pre-compression starts in the background immediately.
  2. Describe them one at a time β€” the painting fills one side while its own form sits beside it, with a filmstrip to jump between photos and a green tick on finished ones.
  3. Frame each photo β€” Crop, rotate & straighten opens a phone-style editor: pinch or scroll to zoom, drag to move, Turn 90Β° for sideways shots, a Straighten slider (Β±15Β°), and shape presets (Whole photo Β· Square Β· Portrait Β· Landscape). Straightening pulls the crop inward to real paint, so no grey wedges can reach the gallery. The geometry lives in src/lib/photoFraming.ts and is shared by the on-screen preview and the exported file, so what you frame is exactly what uploads.
  4. Publish β€” nothing needs ticking: the whole queue is included, Publish this photo to … sends just the one on screen, or Publish all N photos opens a review sheet listing every painting with its gallery, price and for-sale status before one commit.

Committing: everything staged is one commit

Editing anywhere in the console stages the change immediately β€” there is no per-item "mark for commit" step. getPendingChanges() in src/context/SiteContext.tsx diffs the working content against the last published baseline (studio details, SEO, every page section, social links, chatbot settings, every gallery and artwork, the events calendar) and the review sheet lists all of it; the console's bar reads All synced only when that diff is genuinely empty. It also runs a safety-net pass over every top-level content key, so a field added later cannot go missing from the list and leave the commit button disabled.

Destructive actions confirm through the console's own dialog (src/components/admin/ConfirmDialog.tsx) rather than window.confirm, which blocks the page's main thread, cannot be styled, and is silently suppressed by browsers after repeated use.


πŸ” Admin Access

  • The console at /admin (and /login) asks for the studio password before anything renders.
  • The password is never in this repository β€” it lives in the host's environment variables (ADMIN_PASSWORD on Vercel, .env.local locally).
  • Sessions are HMAC-SHA256-signed cookies (src/lib/adminAuth.ts), 1-hour expiry; sign-in is rate-limited with a lockout (src/lib/loginThrottle.ts).
  • Lost the password? Set a new value in the host's env vars and redeploy β€” no code change involved. Note that the signing secret is derived from the password, so changing it invalidates every existing session at once (visitors to /admin will see the "session expired" notice and simply sign in again).

πŸš€ Local Development

git clone https://github.com/abhay2008/AnugrujaArtsStudio.git
cd AnugrujaArtsStudio
npm install

# create .env.local from the template and fill in what you need
cp .env.example .env.local

npm run dev            # β†’ http://localhost:3000

Without any env vars the public site works fully from the committed site.json; the admin gate needs ADMIN_PASSWORD, and committing/uploading through the GitHub API needs a GITHUB_TOKEN.

Useful scripts:

Script What it does
npm run dev Dev server (default http://localhost:3000)
npm run build / npm start Production build / serve it
npm run typecheck tsc --noEmit β€” the check to run before every commit
npm run lint Next.js ESLint
npm run test:framing 8 400+ assertions over the crop/rotate/straighten geometry (scripts/test-photo-framing.ts)
npm run test:chat Chat pipeline unit checks (fuzzy lookups, guardrails, pricing replies)
npm run test:e2e End-to-end chat conversation against the local pipeline
npm run images:optimize Rebuild the public/images/opt/* derivatives
npm run audit:css Cross-engine CSS compatibility audit

πŸ”‘ Environment Variables

Create .env.local (gitignored β€” never commit real values). Templates: .env.example.

Variable Required Purpose
ADMIN_PASSWORD for /admin Studio password checked server-side at sign-in
GITHUB_TOKEN for commits/uploads GitHub fine-grained PAT (Contents: read/write on this repo) β€” held server-side only
GITHUB_OWNER / GITHUB_REPO / GITHUB_BRANCH optional Override commit target (defaults to this repo's main)
OPENROUTER_API_KEY for chatbot Server-side key for the Chitra assistant
OPENROUTER_MODEL / OPENROUTER_FALLBACK_MODELS optional Chat model + fallback chain (free-tier models rotate β€” check that the configured ones are still available)
NEXT_PUBLIC_SITE_URL optional Public URL used for chatbot attribution headers
CHATBOT_RAG_TOP_K / CHATBOT_RAG_TOKEN_BUDGET optional How much of the catalogue the assistant retrieves per question
CHATBOT_CACHE_TTL_SECONDS / CHATBOT_CACHE_MAX_ENTRIES optional Response cache β€” repeat questions are answered with zero API calls, and entries expire when content changes
NEXT_DIST_DIR optional Point a dev server at its own build directory, so two servers can run from one checkout without sharing a .next cache

Security rules this project follows:

  • Secrets live only in .env.local (gitignored) and the host's (Vercel) environment settings.
  • Never paste real tokens or passwords into code, docs, issues, or commit messages.
  • If a secret ever touches Git history, treat it as compromised: rotate it and scrub history β€” do not just delete the file.

☁️ Deployment

The site deploys on Vercel: push to main β†’ automatic build β†’ live. The same environment variables must be configured in the Vercel project settings (they are not read from the repo). Uploaded images commit to public/images/ in this repo, so they deploy with the next build and are served optimized via next/image.


πŸ“‚ Project Structure

AnugrujaArtsStudio/
β”œβ”€β”€ content/
β”‚   └── site.json              # ← Single source of truth for ALL site content
β”œβ”€β”€ public/
β”‚   └── images/                # 140+ artwork photos, portraits, certificates
β”œβ”€β”€ src/
β”‚   β”œβ”€β”€ app/
β”‚   β”‚   β”œβ”€β”€ (site)/            # Public pages: home, sale, classes, about, …
β”‚   β”‚   β”œβ”€β”€ admin/             # Admin console (password-gated) + preview editor
β”‚   β”‚   β”œβ”€β”€ login/             # Admin sign-in gate
β”‚   β”‚   └── api/
β”‚   β”‚       β”œβ”€β”€ auth/          # Password check β†’ signed session cookie
β”‚   β”‚       β”œβ”€β”€ content/       # GET/POST site content (commit pipeline)
β”‚   β”‚       β”œβ”€β”€ upload/        # Image upload β†’ public/images + GitHub commit
β”‚   β”‚       β”œβ”€β”€ github/        # GitHub Contents API helpers (commit status, etc.)
β”‚   β”‚       └── chat/          # Chitra: OpenRouter proxy, guardrails, rate limit
β”‚   β”œβ”€β”€ components/            # Carousels, lightbox, chat widget
β”‚   β”‚   └── admin/             # Console: MassUploadStudio (wizard), ArtworkFramer
β”‚   β”‚                          # (crop/rotate/straighten), GalleryManager,
β”‚   β”‚                          # EventsManager, QuickNavManager, SectionEditor,
β”‚   β”‚                          # AdminShell (commit flow), ConfirmDialog
β”‚   β”œβ”€β”€ context/
β”‚   β”‚   └── SiteContext.tsx    # Live content store shared by site + admin;
β”‚   β”‚                          # getPendingChanges() drives the review sheet
β”‚   └── lib/
β”‚       β”œβ”€β”€ serverContent.ts   # Reads site.json (+ GitHub fallback)
β”‚       β”œβ”€β”€ github.ts          # GitHub Contents API client
β”‚       β”œβ”€β”€ adminAuth.ts       # HMAC-SHA256 session cookies (1 h)
β”‚       β”œβ”€β”€ loginThrottle.ts   # Brute-force lockout for the admin gate
β”‚       β”œβ”€β”€ imageOptimize.ts   # Client-side compression + canvas encoding
β”‚       β”œβ”€β”€ photoFraming.ts    # Crop / rotate / straighten geometry (pure math)
β”‚       β”œβ”€β”€ types.ts           # Data model + GALLERY_CATALOG
β”‚       └── chatbot/           # context.ts, lookup.ts, guardrails.ts, rateLimit.ts
β”œβ”€β”€ scripts/
β”‚   β”œβ”€β”€ test-photo-framing.ts  # Geometry assertions (npm run test:framing)
β”‚   β”œβ”€β”€ test-chatbot.ts        # Chat pipeline checks
β”‚   β”œβ”€β”€ e2e-chat.ts            # End-to-end chat run
β”‚   β”œβ”€β”€ optimize-images.mjs    # Image derivative builder
β”‚   └── audit-css-compat.mjs   # Cross-engine CSS audit
└── .env.example               # Template only β€” real values live in .env.local / Vercel

πŸ‘¨β€πŸ’» Developer

Abhay Kashyap Undergraduate, Computer Science & Business Systems @ BMSCE Bangalore GitHub Profile

Historical note: the studio's first website (2024, static HTML/jQuery) was handcrafted before modern AI tooling; this repo is its full production successor.

About

Commercial web platform and digital fine arts portfolio for Anugruja Arts Studio (Anuradha Govarthanan)

Topics

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages