# Vision Electronics — Full Website Documentation **Version:** 0.1.4 **Stack:** Next.js 16 (App Router) · React 19 · TypeScript · Tailwind CSS v4 · Neon Postgres · Vercel Blob · Upstash Redis **Last updated:** May 2026 --- ## Table of Contents 1. [Overview](#1-overview) 2. [Tech Stack & Dependencies](#2-tech-stack--dependencies) 3. [Project Structure](#3-project-structure) 4. [Environment Variables](#4-environment-variables) 5. [Database Schema](#5-database-schema) 6. [Public Website](#6-public-website) 7. [Admin Panel](#7-admin-panel) 8. [API Reference](#8-api-reference) 9. [Authentication & Security](#9-authentication--security) 10. [Storage & Media](#10-storage--media) 11. [Search](#11-search) 12. [Deployment](#12-deployment) 13. [Local Development](#13-local-development) 14. [Maintenance & Operations](#14-maintenance--operations) --- ## 1. Overview Vision Electronics is a product-showcase website with a full content-management admin panel. Visitors browse products, categories, gallery images, and submit contact / career applications. Administrators manage all content through a protected `/admin` panel. ### Core capabilities - Product catalog with categories, specifications, images, features, and SEO metadata - Contact form with status workflow (new → in progress → resolved) - Career listings and applications - Image gallery with display ordering - Section content management (homepage hero, etc.) - Social media link management - Site-wide settings - Full-text search across all content - Admin user management with sessions - Real-time admin notifications --- ## 2. Tech Stack & Dependencies ### Framework & runtime - **Next.js 16.2.0** — App Router, Turbopack, React Server Components - **React 19** with React DOM 19 - **TypeScript 5** ### UI & styling - **Tailwind CSS v4** with `@tailwindcss/postcss` - **shadcn/ui** (new-york style) — Radix UI primitives - **lucide-react** — icon library - **next-themes** — light/dark theme support - **sonner** — toast notifications - **embla-carousel-react** — carousels - **recharts** — charts and analytics - **vaul** — drawers - **cmdk** — command palette ### Backend & data - **@neondatabase/serverless** — Postgres driver - **@vercel/blob** — file storage - **@upstash/redis** — caching / rate limiting - **bcryptjs** — password hashing - **sharp** — image processing - **zod** — schema validation - **react-hook-form** — form state --- ## 3. Project Structure ``` my-v0-project/ ├── app/ │ ├── (public pages) │ │ ├── page.tsx # Home │ │ ├── about/ │ │ ├── products/ # Catalog + [slug] detail │ │ ├── contact/ │ │ ├── careers/ # Listings + [slug] detail │ │ ├── education-program/ │ │ ├── find-a-reseller/ │ │ ├── privacy-policy/ │ │ ├── terms-of-service/ │ │ ├── cookie-policy/ │ │ ├── warranty-policy/ │ │ └── gdpr/ │ ├── admin/ # Protected admin panel │ │ ├── layout.tsx # Sidebar + TopBar wrapper │ │ ├── page.tsx # Dashboard │ │ ├── login/ │ │ ├── products/ # List, create, edit, specs, images │ │ ├── categories/ │ │ ├── contacts/ │ │ ├── gallery/ │ │ ├── sections/ │ │ ├── social-media/ │ │ └── settings/ │ ├── api/ # API routes (see Section 8) │ ├── layout.tsx # Root layout │ ├── layout-client.tsx # Client wrapper (navbar/footer) │ ├── globals.css # Tailwind + design tokens │ ├── sitemap.ts │ └── robots.ts ├── components/ │ ├── ui/ # shadcn primitives │ ├── admin/ # Admin-only components │ │ ├── sidebar.tsx │ │ ├── top-bar.tsx │ │ ├── breadcrumbs.tsx │ │ ├── data-table.tsx │ │ ├── product-form.tsx │ │ ├── category-form.tsx │ │ ├── confirm-dialog.tsx │ │ ├── duplicate-dialog.tsx │ │ ├── status-badge.tsx │ │ ├── empty-state.tsx │ │ └── toast-container.tsx │ ├── navbar.tsx │ ├── simple-footer.tsx │ ├── global-search.tsx │ └── admin-realtime-notifications.tsx ├── hooks/ │ ├── use-toast.ts │ └── use-mobile.ts ├── lib/ │ ├── utils.ts # cn() helper │ ├── db.ts # Neon SQL client │ ├── auth.ts # Session helpers │ └── notification-system.ts ├── scripts/ # SQL migrations └── public/ # Static assets ``` --- ## 4. Environment Variables ### Required (auto-provisioned by integrations) | Variable | Source | Purpose | |---|---|---| | `DATABASE_URL` | Neon | Pooled Postgres connection | | `POSTGRES_URL` | Neon | Same as DATABASE_URL | | `POSTGRES_URL_NON_POOLING` | Neon | Direct connection for migrations | | `BLOB_READ_WRITE_TOKEN` | Vercel Blob | File upload auth | | `KV_REST_API_URL` | Upstash | Redis HTTP endpoint | | `KV_REST_API_TOKEN` | Upstash | Redis auth | | `REDIS_URL` | Upstash | Redis connection string | ### Required (manual) | Variable | Purpose | |---|---| | `ADMIN_SALT` | Extra salt mixed into bcrypt password hashing for admin users | --- ## 5. Database Schema Database: **Neon Postgres**. 11 tables under the `public` schema. ### `admin_users` Admin accounts with role-based access. | Column | Type | Notes | |---|---|---| | `id` | integer | PK | | `username` | varchar | Unique login name | | `email` | varchar | Email | | `name` | varchar | Display name | | `password_hash` | varchar | bcrypt hash | | `role` | enum | `admin`, `editor`, etc. | | `avatar_url` | varchar | Profile picture | | `is_active` | boolean | Soft-disable flag | | `last_login` | timestamp | Latest sign-in | | `created_at` / `updated_at` | timestamp | Audit | ### `admin_sessions` Active login sessions for admin users. | Column | Type | Notes | |---|---|---| | `id` | varchar | Session ID | | `user_id` | integer | FK → admin_users | | `token` | varchar | Cookie value | | `expires_at` | timestamp | Expiry | | `ip_address` | varchar | Client IP | | `user_agent` | text | Client UA | | `created_at` | timestamp | Issued at | ### `products` Product catalog. | Column | Type | Notes | |---|---|---| | `id` | integer | PK | | `name`, `slug` | varchar | Unique slug for URLs | | `description` | text | Long copy | | `short_description` | varchar | Card / preview copy | | `price` | numeric | Optional | | `image_url` | varchar | Primary image | | `category_id` | integer | FK → product_categories | | `features` | array | Bullet list | | `use_cases` | array | Bullet list | | `specifications` | jsonb | Free-form JSON (legacy) | | `is_featured` | boolean | Show on homepage | | `is_active` | boolean | Visible on public site | | `display_order` | integer | Sort order | | `seo_title`, `seo_description`, `seo_keywords` | text | SEO metadata | | `created_by`, `updated_by` | integer | FK → admin_users | | `created_at` / `updated_at` | timestamp | Audit | ### `product_categories` | Column | Type | Notes | |---|---|---| | `id` | integer | PK | | `name`, `slug` | varchar | Unique slug | | `description` | text | | | `icon` | varchar | Lucide icon name | | `display_order` | integer | Sort order | | `is_active` | boolean | | | `created_at` / `updated_at` | timestamp | | ### `product_specs` Structured specifications for each product, grouped by category. | Column | Type | Notes | |---|---|---| | `id` | integer | PK | | `product_id` | integer | FK → products | | `spec_name` | varchar | e.g. "Resolution" | | `spec_value` | text | e.g. "4K UHD" | | `category` | varchar | e.g. "Display" | | `display_order` | integer | Sort within category | | `category_order` | integer | Sort of categories themselves | ### `product_images` Multiple images per product. | Column | Type | Notes | |---|---|---| | `id` | integer | PK | | `product_id` | integer | FK → products | | `image_url` | varchar | Public Blob URL | | `pathname` | varchar | Blob path (for delete) | | `alt_text` | varchar | Accessibility | | `caption` | text | Optional caption | | `is_primary` | boolean | Main display | | `display_order` | integer | Gallery order | ### `gallery_images` General-purpose site gallery. | Column | Type | Notes | |---|---|---| | `id` | integer | PK | | `filename` | varchar | Original filename | | `pathname` | text | Blob path | | `alt_text` | varchar | | | `caption` | text | | | `mime_type` | varchar | | | `file_size` | integer | Bytes | | `display_order` | integer | | | `is_active` | boolean | | ### `contact_submissions` Contact form messages. | Column | Type | Notes | |---|---|---| | `id` | integer | PK | | `name`, `email`, `phone`, `company` | varchar | | | `subject` | varchar | | | `message` | text | | | `status` | enum | `new`, `in_progress`, `resolved` | | `notes` | text | Internal admin notes | | `created_at` / `updated_at` | timestamp | | ### `section_content` Editable copy for site sections (hero, CTA blocks, etc.). | Column | Type | Notes | |---|---|---| | `id` | integer | PK | | `section_key` | varchar | Unique key (e.g. `home_hero`) | | `tagline`, `title`, `subtitle` | varchar | | | `description` | text | | | `cta_text`, `cta_url` | varchar | Call-to-action button | | `featured_product_id` | integer | FK → products | | `is_active` | boolean | | | `display_order` | integer | | ### `search_index` Denormalized full-text search corpus. | Column | Type | Notes | |---|---|---| | `id` | integer | PK | | `content_type` | varchar | `product`, `page`, `category`, etc. | | `content_id` | integer | Reference into source table | | `title`, `description`, `keywords` | text | Indexed text | | `url` | text | Public link | | `image_url` | text | Thumbnail | | `priority` | numeric | Boost factor | | `search_vector` | tsvector | Postgres full-text vector | | `is_active` | boolean | | | `indexed_at` | timestamp | | ### `settings` Key/value site settings. | Column | Type | Notes | |---|---|---| | `key` | varchar | PK | | `value` | text | | | `updated_at` | timestamptz | | --- ## 6. Public Website ### Pages | Path | Description | |---|---| | `/` | Home — hero, featured products, categories, gallery | | `/about` | About the company | | `/products` | Product catalog with filtering | | `/products/[slug]` | Product detail page (specs, images, features, CTA) | | `/contact` | Contact form | | `/careers` | Open positions | | `/careers/[slug]` | Job detail + application form | | `/education-program` | Education program info | | `/find-a-reseller` | Reseller locator | | `/privacy-policy` | Privacy policy | | `/terms-of-service` | Terms of service | | `/cookie-policy` | Cookie policy | | `/warranty-policy` | Warranty policy | | `/gdpr` | GDPR information | ### SEO - `app/sitemap.ts` generates XML sitemap dynamically from products and pages - `app/robots.ts` generates robots.txt - Each page sets metadata via Next.js `generateMetadata` ### Navigation - `components/navbar.tsx` — top navigation with mobile menu - `components/simple-footer.tsx` — site footer - `components/global-search.tsx` — site-wide search dialog --- ## 7. Admin Panel The admin panel lives at `/admin` and is gated by session-based authentication. Unauthenticated users are redirected to `/admin/login`. ### Layout - **Sidebar** (`components/admin/sidebar.tsx`) — fixed-position navigation. Mobile drawer auto-closes on route change and locks body scroll. - **TopBar** (`components/admin/top-bar.tsx`) — sticky bar with breadcrumbs, "View Site" link, and user menu (avatar, name, role, sign-out). - **Breadcrumbs** (`components/admin/breadcrumbs.tsx`) — automatically derived from the URL. ### Pages | Path | Purpose | |---|---| | `/admin` | Dashboard with stats, recent activity, quick actions, and reindex button | | `/admin/login` | Sign-in form | | `/admin/products` | Product list with search, filter, sort, inline rename, duplicate, delete | | `/admin/products/new` | Create product | | `/admin/products/[id]` | Edit product (name, description, pricing, SEO, features, etc.) | | `/admin/products/[id]/specs` | Manage spec rows grouped by category, with category reordering | | `/admin/products/[id]/images` | Upload, reorder, delete product images | | `/admin/categories` | Manage categories | | `/admin/contacts` | Inbox of contact submissions with status workflow | | `/admin/gallery` | Site-wide image gallery | | `/admin/sections` | Edit homepage / CTA copy by section_key | | `/admin/social-media` | Manage social media links | | `/admin/settings` | Site settings + admin user profile | ### Dashboard widgets - Total products, featured count, active count - Total messages by status - Products by category - Recent products (5 newest) - Recent messages (5 newest) - Quick actions: New Product, Reindex Search, View Site ### UX patterns - All destructive actions use `ConfirmDialog` - All success/error feedback uses toast notifications (no `alert()` popups) - Inline editing for product names directly in the list - Result count + "Clear filters" link when filters are active - Loading skeletons via `loading.tsx` --- ## 8. API Reference All routes are under `/api/`. Admin routes require authentication. ### Public APIs | Method | Path | Description | |---|---|---| | GET | `/api/products` | List active products (supports `?limit=`, `?category=`) | | GET | `/api/products/[slug]` | Product detail (with specs and images) | | GET | `/api/products/[slug]/images` | All images for a product | | GET | `/api/products/[slug]/images/primary` | Primary image only | | GET | `/api/products/categories` | List active categories | | GET | `/api/gallery` | Public gallery | | GET | `/api/search` | Full-text site search (`?q=`) | | POST | `/api/contact` | Submit contact form | | GET | `/api/careers` | List open jobs | | GET | `/api/careers/[slug]` | Job detail | | POST | `/api/careers/apply` | Submit job application | | GET | `/api/blob/serve` | Proxy/serve a blob asset | ### Admin APIs (require session cookie) #### Auth | Method | Path | Description | |---|---|---| | POST | `/api/admin/auth/login` | Sign in (username + password) | | POST | `/api/admin/auth/logout` | End session | | GET | `/api/admin/auth/me` | Current user info | #### Products | Method | Path | Description | |---|---|---| | GET | `/api/admin/products` | List ALL products (active + inactive) | | POST | `/api/admin/products` | Create product | | GET | `/api/admin/products/[id]` | Get single product | | PUT | `/api/admin/products/[id]` | Update product | | DELETE | `/api/admin/products/[id]` | Delete product | | POST | `/api/admin/products/[id]/duplicate` | Clone a product | #### Product specs | Method | Path | Description | |---|---|---| | GET | `/api/admin/products/[id]/specs` | List specs | | PUT | `/api/admin/products/[id]/specs` | Bulk replace all specs (preserves `category_order`) | | DELETE | `/api/admin/products/[id]/specs/[specId]` | Delete a single spec | #### Product images | Method | Path | Description | |---|---|---| | GET | `/api/admin/products/[id]/images` | List images | | POST | `/api/admin/products/[id]/images` | Upload new image (multipart) | | DELETE | `/api/admin/products/[id]/images/[imageId]` | Delete an image | #### Categories | Method | Path | Description | |---|---|---| | GET | `/api/admin/categories` | List all | | POST | `/api/admin/categories` | Create | | PUT | `/api/admin/categories/[id]` | Update | | DELETE | `/api/admin/categories/[id]` | Delete (fails if products attached) | #### Contacts | Method | Path | Description | |---|---|---| | GET | `/api/admin/contacts` | List submissions + stats | | PUT | `/api/admin/contacts/[id]` | Update status / notes | | DELETE | `/api/admin/contacts/[id]` | Delete submission | #### Gallery | Method | Path | Description | |---|---|---| | GET | `/api/admin/gallery` | List images | | POST | `/api/admin/gallery` | Upload | | PUT | `/api/admin/gallery/[id]` | Update metadata / order | | DELETE | `/api/admin/gallery/[id]` | Delete | #### Sections / Social / Settings | Method | Path | Description | |---|---|---| | GET / PUT | `/api/admin/sections` | Manage section content | | GET / PUT | `/api/admin/social-media` | Manage social links | | GET / PUT | `/api/admin/settings` | Site settings | #### Search & audit | Method | Path | Description | |---|---|---| | POST | `/api/admin/search/reindex` | Rebuild full-text search index | | POST | `/api/admin/audit/log` | Append entry to audit trail | --- ## 9. Authentication & Security ### Sign-in flow 1. User POSTs `username` + `password` to `/api/admin/auth/login` 2. Server hashes password with bcrypt + `ADMIN_SALT` and compares to `admin_users.password_hash` 3. On success, a row is inserted into `admin_sessions` with a random token 4. Token is set as an HTTP-only, Secure, SameSite=Lax cookie 5. Subsequent requests look up the session, check `expires_at`, and load the user ### Session lifecycle - Sessions expire after a configured TTL - Logout deletes the session row and clears the cookie - `/api/admin/auth/me` returns the current user or 401 ### Best practices in place - Passwords hashed with bcrypt + per-app salt - HTTP-only cookies (no JS access) - Parameterized SQL queries (Neon serverless tagged templates) - Server-side authorization on every `/api/admin/*` route - IP and User-Agent recorded with each session for audit ### Recommended additions - Rate limiting on `/api/admin/auth/login` via Upstash Redis - 2FA / TOTP for admin users - Row-Level Security policies on Neon (currently disabled) --- ## 10. Storage & Media All file storage uses **Vercel Blob**. ### Upload flow 1. Admin selects file in product/gallery image manager 2. Client POSTs `multipart/form-data` to the relevant API route 3. Server validates MIME type and size, then calls `put()` from `@vercel/blob` 4. Returned `{ url, pathname }` is stored in DB (`image_url`, `pathname`) 5. Public site references `image_url` directly ### Delete flow 1. Admin clicks delete 2. Server calls `del(pathname)` from `@vercel/blob` 3. DB row is removed ### Image processing - `sharp` is available for server-side resizing/optimization if needed - Next.js `` component handles responsive serving and lazy loading --- ## 11. Search ### Public search - Endpoint: `GET /api/search?q=` - Backed by `search_index.search_vector` (Postgres `tsvector`) - Returns ranked results across products, categories, gallery, and pages ### Reindex - Endpoint: `POST /api/admin/search/reindex` - Truncates `search_index` and rebuilds rows from `products`, `product_categories`, etc. - Triggered automatically on content changes; can be manually run from the dashboard ### Frontend - `components/global-search.tsx` provides a `Cmd+K` style command palette using `cmdk` --- ## 12. Deployment ### Vercel (recommended) 1. Push repo to GitHub 2. Import into Vercel 3. Connect Neon, Vercel Blob, and Upstash integrations — env vars are auto-provisioned 4. Manually add `ADMIN_SALT` in Project Settings → Environment Variables 5. Deploy ### Database migrations SQL scripts live in `/scripts/`. Run them against `POSTGRES_URL_NON_POOLING` using Neon's SQL editor or `psql`. ### First-time admin user After running migrations, insert your first admin user: ```sql INSERT INTO admin_users (username, email, name, password_hash, role, is_active) VALUES ( 'admin', 'admin@yourdomain.com', 'Administrator', '', 'admin', true ); ``` Generate the hash with bcrypt using the same `ADMIN_SALT`. --- ## 13. Local Development ### Prerequisites - Node.js 20+ - pnpm ### Setup ```bash pnpm install cp .env.example .env.local # Fill in DATABASE_URL, BLOB_READ_WRITE_TOKEN, KV_*, ADMIN_SALT pnpm dev ``` ### Useful scripts | Command | Purpose | |---|---| | `pnpm dev` | Start dev server with Turbopack | | `pnpm build` | Production build | | `pnpm start` | Run production build | | `pnpm lint` | Run ESLint | --- ## 14. Maintenance & Operations ### Routine tasks - **Reindex search** — after bulk content imports, click "Reindex Search" on the dashboard - **Backup database** — Neon provides point-in-time restore; export periodically - **Monitor sessions** — clean up expired rows in `admin_sessions` (consider a cron job) - **Rotate `ADMIN_SALT`** — requires re-hashing all admin passwords (force a password reset) ### Adding a new admin page 1. Create `app/admin//page.tsx` 2. Add nav item to `components/admin/sidebar.tsx` 3. Add breadcrumb mapping in `components/admin/breadcrumbs.tsx` 4. Build matching API routes under `app/api/admin//` ### Adding a new public page 1. Create `app//page.tsx` 2. Add `generateMetadata` for SEO 3. Add link to navbar / footer 4. Add to `app/sitemap.ts` ### Common pitfalls - Always run `useEffect` data fetches against `/api/admin/*` (not `/api/*`) when in the admin context — public APIs filter out inactive content - When deleting product images, delete the Blob too (handled automatically in delete routes) - `params` and `searchParams` in Next.js 16 are async — always `await` them in route handlers - Use `revalidatePath()` after admin mutations to refresh ISR caches --- ## License Proprietary — Vision Electronics. All rights reserved. --- *Generated by v0. For questions, contact your development team.*