Skip to content

Commit e864ef8

Browse files
Alejo Ponce De LeonAlejo Ponce De Leon
authored andcommitted
Initial production-ready setup with Vercel configuration
1 parent b71996a commit e864ef8

34 files changed

+196597
-221
lines changed

.env.local.example

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,31 @@
1-
# Notion API Configuration
1+
# Notion API Configuration - Vercel environment variables
22
NOTION_API_KEY=your_notion_api_key_here
33
NOTION_DATABASE_ID=your_notion_database_id_here
44

55
# Site Configuration
66
NEXT_PUBLIC_SITE_NAME=oBoRo
7-
NEXT_PUBLIC_SITE_URL=http://localhost:3000
7+
NEXT_PUBLIC_SITE_URL=https://oboro-prod.vercel.app # Update with your Vercel URL or custom domain
88
NEXT_PUBLIC_WHATSAPP_NUMBER=your_whatsapp_number_here
99

10-
# Optional Analytics
11-
NEXT_PUBLIC_GA_ID=your_google_analytics_id_here # Optional
10+
# Deployment Configuration
11+
NODE_ENV=production # Vercel sets this automatically
12+
VERCEL_URL=your_vercel_url # Automatically set by Vercel
13+
VERCEL_ENV=production # Automatically set by Vercel
14+
15+
# Revalidation Configuration
16+
REVALIDATION_TOKEN=your_secure_revalidation_token_here # Used for webhook-triggered ISR
17+
18+
# SEO & Meta Settings
19+
NEXT_PUBLIC_DEFAULT_META_TITLE=oBoRo - Tienda Online
20+
NEXT_PUBLIC_DEFAULT_META_DESCRIPTION=Descubre nuestra colección de productos exclusivos en oBoRo.
21+
NEXT_PUBLIC_DEFAULT_META_IMAGE=/og-image.jpg
22+
23+
# Analytics (Optional)
24+
NEXT_PUBLIC_GA_ID=your_google_analytics_id_here
25+
NEXT_PUBLIC_PLAUSIBLE_DOMAIN=your_plausible_domain # Alternative to GA
26+
27+
# Error Tracking (Optional)
28+
NEXT_PUBLIC_SENTRY_DSN=your_sentry_dsn_here
29+
30+
# Performance Configuration
31+
NEXT_PUBLIC_IMAGE_DOMAIN=your_image_cdn_domain # If using a CDN for images

.github/workflows/ci.yml

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
name: CI/CD Pipeline
2+
3+
on:
4+
push:
5+
branches: [ main ]
6+
pull_request:
7+
branches: [ main ]
8+
9+
jobs:
10+
lint:
11+
name: Lint and Type Check
12+
runs-on: ubuntu-latest
13+
14+
steps:
15+
- uses: actions/checkout@v3
16+
17+
- name: Set up Node.js
18+
uses: actions/setup-node@v3
19+
with:
20+
node-version: 20
21+
cache: 'npm'
22+
23+
- name: Install dependencies
24+
run: npm ci
25+
26+
- name: Run ESLint
27+
run: npm run lint
28+
29+
- name: Type check
30+
run: npx tsc --noEmit
31+
32+
test:
33+
name: Run Tests
34+
runs-on: ubuntu-latest
35+
36+
steps:
37+
- uses: actions/checkout@v3
38+
39+
- name: Set up Node.js
40+
uses: actions/setup-node@v3
41+
with:
42+
node-version: 20
43+
cache: 'npm'
44+
45+
- name: Install dependencies
46+
run: npm ci
47+
48+
- name: Run tests
49+
run: npm test
50+
51+
build:
52+
name: Build and Check Bundle Size
53+
runs-on: ubuntu-latest
54+
55+
steps:
56+
- uses: actions/checkout@v3
57+
58+
- name: Set up Node.js
59+
uses: actions/setup-node@v3
60+
with:
61+
node-version: 20
62+
cache: 'npm'
63+
64+
- name: Install dependencies
65+
run: npm ci
66+
67+
- name: Build
68+
run: npm run build
69+
70+
- name: Analyze bundle size
71+
run: npx next-bundle-analyzer
72+
73+
- name: Upload build artifacts
74+
uses: actions/upload-artifact@v3
75+
with:
76+
name: build-output
77+
path: .next
78+
retention-days: 7
79+
80+
accessibility:
81+
name: Accessibility Check
82+
runs-on: ubuntu-latest
83+
needs: build
84+
85+
steps:
86+
- uses: actions/checkout@v3
87+
88+
- name: Set up Node.js
89+
uses: actions/setup-node@v3
90+
with:
91+
node-version: 20
92+
cache: 'npm'
93+
94+
- name: Install dependencies
95+
run: npm ci
96+
97+
- name: Install Playwright
98+
run: npx playwright install --with-deps
99+
100+
- name: Build
101+
run: npm run build
102+
103+
- name: Start server
104+
run: npm run start & sleep 5
105+
106+
- name: Run accessibility tests
107+
run: npx playwright test --grep "accessibility"
108+
109+
lighthouse:
110+
name: Lighthouse Performance Audit
111+
runs-on: ubuntu-latest
112+
needs: build
113+
114+
steps:
115+
- uses: actions/checkout@v3
116+
117+
- name: Set up Node.js
118+
uses: actions/setup-node@v3
119+
with:
120+
node-version: 20
121+
cache: 'npm'
122+
123+
- name: Install dependencies
124+
run: npm ci
125+
126+
- name: Build
127+
run: npm run build
128+
129+
- name: Start server
130+
run: npm run start & sleep 5
131+
132+
- name: Run Lighthouse CI
133+
run: |
134+
npm install -g @lhci/cli
135+
lhci autorun

.gitignore

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ node_modules
99
# next.js
1010
/.next/
1111
/out/
12+
.swc
1213

1314
# production
1415
/build
@@ -21,9 +22,13 @@ node_modules
2122
npm-debug.log*
2223
yarn-debug.log*
2324
yarn-error.log*
25+
pnpm-debug.log*
2426

2527
# local env files
28+
.env
2629
.env*.local
30+
!.env.example
31+
!.env.local.example
2732

2833
# vercel
2934
.vercel
@@ -35,7 +40,17 @@ next-env.d.ts
3540
# IDE
3641
.vscode/
3742
.idea/
43+
*.sublime-project
44+
*.sublime-workspace
3845

3946
# Temporary files
4047
*.log
4148
*.csv
49+
tmp/
50+
.cache/
51+
52+
# Logs
53+
logs
54+
55+
# System Files
56+
Thumbs.db

PROGRESS.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,18 @@
2727
- Refactored conditional rendering to use CSS classes instead
2828
- Ensured consistent server/client rendering
2929

30+
## Navigation Improvements (2025-04-24)
31+
- ✓ Fixed Product Drop Navigation System:
32+
- Created ProductNavigationContext to centralize navigation state management
33+
- Fixed drop selection after returning from product detail pages
34+
- Made level automatically reset to 1 when changing drops
35+
- Added scroll offset to improve level heading visibility
36+
- Fixed hydration errors caused by browser extensions
37+
- ✓ Improved Test Coverage:
38+
- Added unit tests for the ProductNavigationContext
39+
- Created tests for drop selection and level reset functionality
40+
- Added helper tests for scroll position calculation
41+
3042
## Next Phase: Continued Development
3143
Following the project requirements from PLANNING.md and TASK.md, we need to:
3244

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
# oBoRo - Next.js Product Listing Site
1+
# oBoRo - Production E-commerce Site
22

3-
A modern, responsive e-commerce product listing website built with Next.js and Notion as a headless CMS.
3+
A modern, responsive e-commerce product listing website built with Next.js 15.2.4 and Notion as a headless CMS. This is the production repository optimized for Vercel deployment.
44

55
## Setup
66

Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
// Purpose: Test the product detail page navigation and URL parameter handling
2+
import React from 'react';
3+
import { render, screen } from '@testing-library/react';
4+
import '@testing-library/jest-dom';
5+
import ProductPage from '../producto/[id]/page';
6+
import { mockProducts } from '@/tests/utils';
7+
8+
// Mock Next.js navigation
9+
jest.mock('next/navigation', () => ({
10+
useRouter: () => ({
11+
replace: jest.fn(),
12+
back: jest.fn(),
13+
}),
14+
usePathname: () => '/producto/1',
15+
useSearchParams: () => ({
16+
// Add has() method to the mock
17+
has: jest.fn((param) => {
18+
if (param === 'dropId' || param === 'level') return true;
19+
return false;
20+
}),
21+
get: jest.fn((param) => {
22+
if (param === 'dropId') return 'DROP1';
23+
if (param === 'level') return '2';
24+
return null;
25+
}),
26+
toString: () => 'dropId=DROP1&level=2',
27+
}),
28+
}));
29+
30+
// Mock ProductDetail component
31+
jest.mock('@/components/ProductDetail', () => {
32+
return {
33+
__esModule: true,
34+
default: ({ product }: any) => (
35+
<div data-testid="product-detail">
36+
<h1 data-testid="product-name">{product.name}</h1>
37+
<p data-testid="product-drop">{product.dropId}</p>
38+
<p data-testid="product-level">{product.level}</p>
39+
</div>
40+
),
41+
};
42+
});
43+
44+
// Mock the context
45+
jest.mock('@/contexts/product-navigation-context', () => ({
46+
useProductNavigation: () => ({
47+
selectedDrop: 'DROP1',
48+
selectedLevel: 2,
49+
setDrop: jest.fn(),
50+
setLevel: jest.fn(),
51+
}),
52+
}));
53+
54+
// Mock the Notion API
55+
jest.mock('@/lib/notion', () => ({
56+
getProductById: jest.fn(async (id) => {
57+
const product = mockProducts.find(p => p.id === id);
58+
if (!product) {
59+
return null;
60+
}
61+
return product;
62+
}),
63+
getProductsByDrop: jest.fn(async () => mockProducts),
64+
}));
65+
66+
// Mock the not-found function
67+
jest.mock('next/navigation', () => ({
68+
...jest.requireActual('next/navigation'),
69+
notFound: jest.fn(),
70+
}));
71+
72+
describe('Product Detail Page', () => {
73+
// This is a simplified test as we can't easily test server components
74+
// We're mainly testing that the page receives and uses URL parameters correctly
75+
76+
it('loads product with correct parameters', async () => {
77+
// Test implementation here would depend on how to test server components
78+
// For now, we'll just verify our mocks are set up correctly
79+
80+
// In an actual test of the page, we would render the page component
81+
// with mocked params and searchParams, then check if the product detail
82+
// shows the correct information based on the URL parameters
83+
84+
const product = await mockProducts.find(p => p.id === '1');
85+
expect(product).not.toBeUndefined();
86+
87+
if (product) {
88+
expect(product.dropId).toBe('DROP1');
89+
}
90+
});
91+
92+
// Additional tests would verify:
93+
// 1. Navigation preservation (dropId and level in URL)
94+
// 2. Blocked product handling
95+
// 3. Product not found scenarios
96+
// However, these tests require more complex setup for server components
97+
});

app/apple-icon.tsx

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import { ImageResponse } from 'next/og'
2+
3+
// Route segment config
4+
export const runtime = 'edge'
5+
6+
// Image metadata
7+
export const size = {
8+
width: 180,
9+
height: 180,
10+
}
11+
export const contentType = 'image/png'
12+
13+
// Image generation
14+
export default function Icon() {
15+
return new ImageResponse(
16+
(
17+
// ImageResponse JSX element
18+
<div
19+
style={{
20+
fontSize: 24,
21+
background: 'black',
22+
width: '100%',
23+
height: '100%',
24+
display: 'flex',
25+
alignItems: 'center',
26+
justifyContent: 'center',
27+
borderRadius: '50%',
28+
padding: '12%',
29+
}}
30+
>
31+
{/* Use SVG directly for the Apple icon */}
32+
<svg width="100%" height="100%" viewBox="0 0 900 900" xmlns="http://www.w3.org/2000/svg">
33+
<path
34+
d="M485.00 91.00C485.50 91.00 486.50 91.00 487.00 91.00C495.22 87.50 508.14 90.42 516.23 94.77C524.32 99.12 536.87 98.51 546.01 99.99C555.14 101.47 562.76 106.89 570.75 111.25C578.73 115.62 589.29 116.10 598.01 118.99C606.73 121.89 611.87 128.10 619.25 132.75C626.62 137.40 637.31 138.47 644.70 143.30C652.09 148.13 657.70 153.10 663.30 159.70C668.91 166.29 679.03 166.81 685.08 172.92C691.13 179.02 699.03 183.60 702.77 191.23C706.50 198.86 715.70 201.72 720.75 208.25C725.80 214.77 734.05 218.95 736.30 227.70C738.55 236.44 746.96 240.98 752.25 247.75C757.54 254.53 763.10 261.32 764.92 270.08C766.75 278.83 773.33 285.20 778.25 292.75C783.17 300.30 786.56 308.82 787.98 318.02C789.39 327.23 791.68 335.00 797.77 343.23C803.86 351.46 798.98 365.91 802.00 375.00C805.02 384.09 810.34 390.84 810.99 401.01C811.64 411.17 808.94 420.78 810.78 430.22C812.61 439.66 817.58 451.10 814.00 461.00C810.42 470.90 809.69 478.73 811.00 489.00C812.31 499.27 810.35 510.25 806.07 519.07C801.79 527.90 801.48 539.55 800.22 549.22C798.96 558.89 792.56 564.55 788.75 572.75C784.94 580.96 784.68 592.63 781.03 601.02C777.37 609.42 771.48 614.30 766.75 621.75C762.03 629.21 761.79 639.24 756.75 646.75C751.70 654.25 745.53 659.40 739.25 665.25C732.98 671.10 732.86 681.53 726.25 687.25C719.63 692.96 715.71 700.24 707.92 703.92C700.14 707.60 697.22 717.22 690.70 722.70C684.18 728.17 677.65 733.79 669.92 736.92C662.20 740.05 658.00 748.69 650.92 752.92C643.84 757.15 636.32 765.34 627.92 765.92C619.53 766.51 612.01 775.73 605.75 778.75C599.50 781.77 584.98 790.82 580.00 787.00C579.51 787.07 579.00 787.00 579.00 787.00C574.79 790.11 566.77 790.68 561.92 794.92C557.07 799.16 550.13 799.27 543.93 801.93C537.73 804.59 526.97 800.77 520.92 802.93C514.88 805.09 509.52 809.73 502.33 809.33C495.14 808.93 485.87 812.17 478.33 809.67C470.79 807.17 463.49 812.80 456.00 812.00C455.51 811.95 455.00 812.00 455.00 812.00C447.84 817.30 436.30 810.52 427.99 810.01C419.68 809.49 408.88 810.07 399.93 810.07C390.97 810.08 382.88 806.41 374.92 803.08C366.96 799.75 354.86 802.80 346.93 799.07C338.99 795.35 333.02 791.94 326.30 787.70C319.57 783.47 307.70 786.64 300.93 782.07C294.15 777.51 286.75 775.92 281.25 769.75C275.74 763.58 265.28 764.21 258.98 760.03C252.67 755.84 244.24 753.48 240.75 746.25C237.26 739.02 226.79 738.06 223.00 732.00C222.86 731.78 222.00 732.00 222.00 732.00C216.60 731.50 210.69 724.32 207.25 720.75C203.81 717.18 195.81 711.47 196.00 706.00C196.02 705.50 196.00 705.00 196.00 705.00C179.78 696.34 164.77 680.98 161.00 663.00C148.11 656.81 140.79 642.68 136.00 630.00C135.96 629.50 136.00 629.00 136.00 629.00C132.37 626.22 131.69 617.19 127.25 613.75C122.80 610.31 119.80 602.57 118.22 597.78C116.65 592.98 110.58 583.36 114.00 579.00C114.03 578.50 114.00 578.00 114.00 578.00C111.51 577.33 110.54 569.54 107.25 566.75C103.95 563.96 103.72 557.74 101.08 553.92C98.43 550.11 98.00 542.74 98.00 538.00C98.00 533.58 99.20 527.23 98.00 523.00C89.84 520.10 91.58 505.46 89.22 497.78C86.86 490.10 92.89 475.10 89.07 466.93C85.25 458.75 84.05 445.16 87.00 436.00C89.95 426.84 91.66 417.15 90.00 407.00C88.34 396.85 92.61 386.99 96.31 378.31C100.00 369.62 97.66 358.46 100.33 349.33C103.00 340.19 107.65 333.83 111.92 325.92C116.19 318.00 115.12 307.09 118.98 298.98C122.83 290.86 127.76 285.11 133.25 278.25C138.74 271.39 138.50 260.78 143.77 253.77C149.04 246.76 154.26 241.21 160.25 235.25C166.23 229.29 167.45 220.62 173.75 214.75C180.05 208.88 184.42 202.18 191.70 197.70C198.97 193.21 201.44 184.57 208.25 179.25C215.05 173.93 219.79 167.83 228.08 165.08C236.36 162.33 240.17 154.46 247.25 149.25C254.32 144.03 260.97 138.79 269.70 136.69C278.42 134.60 284.89 129.36 292.25 124.25C299.60 119.13 307.99 116.70 317.01 115.01C326.03 113.32 333.33 108.99 341.67 104.67C350.02 100.35 361.13 98.97 371.00 99.00C380.87 99.03 387.72 90.28 398.00 91.00C398.50 91.03 399.50 91.00 400.00 91.00C408.69 87.30 420.24 93.02 429.01 91.01C437.78 88.99 448.32 84.18 458.00 87.00C467.68 89.82 474.89 91.00 485.00 91.00Z"
35+
fill="white"
36+
stroke="white"
37+
strokeWidth="8"
38+
/>
39+
</svg>
40+
</div>
41+
),
42+
// ImageResponse options
43+
{
44+
...size,
45+
}
46+
)
47+
}

0 commit comments

Comments
 (0)