Modern, fast, and mobile-first website for The Oaks Pub PDX built with Next.js, TypeScript, and Tailwind CSS.
- Node.js 18+
- npm or yarn
# Install dependencies
npm install
# Run development server
npm run dev
# Open http://localhost:3000 in your browser# Create optimized production build
npm run build
# Preview production build locally
npm startnpm run lint├── app/ # Next.js App Router pages and layouts
│ ├── layout.tsx # Root layout with metadata and schema
│ ├── page.tsx # Homepage
│ ├── menu/ # Food menu page
│ ├── drinks/ # Drinks menu page
│ ├── events/ # Events and specials
│ ├── contact/ # Contact page with form
│ ├── gallery/ # Photo gallery
│ ├── about/ # About page
│ ├── privacy/ # Privacy policy
│ ├── terms/ # Terms of service
│ ├── api/ # API routes
│ ├── sitemap.ts # Dynamic sitemap generation
│ └── robots.ts # Robots.txt generation
├── components/ # Reusable React components
│ ├── Navbar.tsx # Navigation with mobile menu
│ ├── Footer.tsx # Footer with NAP and hours
│ ├── StickyActionBar.tsx # Mobile sticky action bar
│ ├── MenuSection.tsx # Menu section display
│ ├── MenuItem.tsx # Menu item with dietary icons
│ ├── CTAButton.tsx # Call-to-action button
│ └── OpenNowBadge.tsx # Live open/closed status
├── content/ # JSON data files
│ ├── site.json # Business info (NAP, hours, etc.)
│ ├── menu.food.json # Food menu items
│ ├── menu.drinks.json # Drinks menu items
│ └── events.json # Events and specials
├── lib/ # Utility functions and schemas
│ ├── content.ts # Content loaders with validation
│ ├── schemas.ts # Zod schemas for type safety
│ ├── hours.ts # Hours calculation utilities
│ ├── analytics.ts # Event tracking
│ └── schema.ts # JSON-LD schema generation
└── public/ # Static assets (images, icons)
Edit content/site.json:
{
"businessName": "The Oaks Pub PDX",
"phone": "503-232-1728",
"email": "info@theoakspubpdx.com",
"address": {
"street": "1621 SE Bybee Blvd",
"city": "Portland",
"state": "OR",
"zip": "97202"
},
"hours": {
"monday": { "open": "11:00", "close": "21:00" },
...
}
}Edit content/menu.food.json or content/menu.drinks.json:
{
"lastUpdatedISO": "2026-02-13T00:00:00.000Z",
"sections": [
{
"id": "appetizers",
"title": "Appetizers",
"description": "Start your meal right",
"items": [
{
"name": "Wings",
"description": "Crispy chicken wings",
"price": "$12",
"dietary": ["gf"],
"spicy": true
}
]
}
]
}Dietary Codes:
v= Vegetarian 🌱vg= Vegan 🌿gf= Gluten-Free 🌾df= Dairy-Free 🥛n= Contains Nuts 🥜
Edit content/events.json:
{
"recurringEvents": [
{
"title": "Trivia Night",
"description": "Test your knowledge!",
"dayOfWeek": "wednesday",
"time": "7:00 PM"
}
],
"upcomingEvents": [
{
"title": "Live Music",
"description": "Local band performance",
"date": "2026-03-15",
"time": "8:00 PM"
}
],
"happyHour": {
"enabled": true,
"days": ["monday", "tuesday", "wednesday", "thursday", "friday"],
"startTime": "3:00 PM",
"endTime": "6:00 PM",
"description": "Half-price appetizers and drink specials",
"items": [
{ "name": "Draft Beer", "price": "$4" },
{ "name": "House Wine", "price": "$5" }
]
}
}- Push your code to GitHub
- Import the repository in Vercel
- Vercel will automatically detect Next.js and configure the build
- Set environment variables (optional):
RESEND_API_KEY- For email notifications from contact formCONTACT_TO_EMAIL- Email address to receive contact form submissions
- Deploy!
The site will automatically deploy on every push to main branch, with preview deployments for pull requests.
# Build the site
npm run build
# The output will be in the `.next` folder
# Upload to any Node.js hosting providerCreate a .env.local file for local development:
# Optional: Email integration for contact form
RESEND_API_KEY=your_resend_api_key_here
CONTACT_TO_EMAIL=info@theoakspubpdx.com
# Site URL (used for sitemap and schema)
NEXT_PUBLIC_SITE_URL=https://www.theoakspubpdx.com
# Online Ordering Configuration (Union POS)
# To enable online ordering, set the ordering URL
OAKS_ORDER_ONLINE_URL=https://order.unionpos.com/your-restaurant
# Order mode: pickup | delivery | both (default: pickup)
OAKS_ORDER_MODE=pickup
# Custom label for order buttons (optional, defaults based on mode)
OAKS_ORDER_LABEL=Order PickupNote: Without RESEND_API_KEY, contact form submissions will be logged to the console but not emailed.
The site supports online ordering integration with Union POS or similar external ordering platforms:
- Enable Ordering: Set
OAKS_ORDER_ONLINE_URLin Vercel or.env.local - Choose Mode: Set
OAKS_ORDER_MODEtopickup(default),delivery, orboth - Customize Label: Optionally set
OAKS_ORDER_LABELto override the default button text
Without ordering URL configured:
- Order buttons display "Call to Order" and link to phone number
- Sticky action bar shows "Hours" instead of "Order" button
- Menu banners show phone number for ordering
With ordering URL configured:
- Order buttons appear in header, hero, menu pages, and sticky bar
- All order links open in new tab
- Analytics tracks order clicks by source
- ✅ Fast, mobile-first design
- ✅ Sticky mobile action bar (Call, Directions, Order, Menu)
- ✅ Live "Open Now" badge based on hours
- ✅ Dietary filter system for menus (Vegetarian, Vegan, GF, DF)
- ✅ Print-friendly menu styling
- ✅ Contact form with spam protection (honeypot + rate limiting)
- ✅ Responsive navigation with mobile hamburger menu
- ✅ Server-side rendering with Next.js
- ✅ Dynamic sitemap generation
- ✅ Robots.txt configuration
- ✅ JSON-LD Restaurant schema
- ✅ OpenGraph and Twitter Card metadata
- ✅ Optimized for Lighthouse scores ≥95
- ✅ Vercel Analytics integration
- ✅ Event tracking for user actions
- ✅ Speed Insights
Edit app/globals.css to customize the color scheme:
@theme {
--color-primary-600: #16a34a; /* Main brand color */
--color-primary-700: #15803d; /* Hover state */
/* Add more custom colors as needed */
}The site uses system fonts by default for performance. To add custom fonts, edit app/layout.tsx.
The sticky action bar appears on mobile devices with 4 key actions:
- Call - Direct phone call
- Directions - Opens Google Maps
- Order - Links to delivery platform (if configured) or email
- Menu - Quick access to menu page
All interactions are tracked for analytics.
Content is validated at build time using Zod schemas. Invalid content will fail the build with a helpful error message.
- All pages load without errors
- Mobile action bar appears on mobile viewport
- NAP (Name, Address, Phone) is consistent across all pages
- Menu items render correctly
- Contact form submits successfully
- Hours display correctly
- "Open Now" badge shows correct status
- Sitemap is accessible at
/sitemap.xml - Robots.txt is accessible at
/robots.txt
This means a client component is trying to import server-side code. Make sure components that use getSiteConfig() or file system access are server components (no 'use client' directive).
Check that RESEND_API_KEY environment variable is set. Without it, submissions are logged but not emailed.
- Check that
content/menu.food.jsonorcontent/menu.drinks.jsonhave sections defined - Verify JSON is valid
- Check browser console for errors
For questions or issues:
- Email: info@theoakspubpdx.com
- Phone: 503-232-1728
Copyright © 2026 The Oaks Pub PDX. All rights reserved.