Development & Run Guide
The complete, code-verified playbook for cloning, configuring, and running Lastiendas. Every section is derived from real file analysis — no guesswork.
1. Verified Technology Stack
Next.js 15.5.4
App Router with 'use client' directives. Static
HTML export configured (output: 'export'). React 18.2.0 with hooks-first
architecture. Tailwind CSS v4 for utility styling.
PHP 8.2 REST API
Custom REST API without a framework. PDO (PHP Data Objects) for
all database queries. 85 PHP endpoint files organized into 17 modules. Bearer token session
authentication stored in user_sessions table.
MySQL 8.0
Managed via PDO with utf8mb4 charset. DSN-based
connection: mysql:host=localhost;dbname=u278171752_jess_DB. Full database dump
available in jess_DB.sql (194KB).
Hostinger SMTP
Custom SMTP mailer via api/utils/smtp_mailer.php
and hostinger_mailer.php. Template-based email system in
api/utils/email.php (39KB). Configuration in api/config/email.php.
2. Full Dependency Registry
NPM Production Dependencies
useState,
useEffect, useContext.
NEXT_PUBLIC_GOOGLE_MAPS_API_KEY env var. Used in
LocationPicker.js.
LocationPickerLeaflet.js — no API
key required.ManagePages.js and email templates. Configured in
src/config/tinymce.js.
NPM Dev Dependencies
tailwind.config.js and postcss.config.mjs.
ignoreDuringBuilds: true) in next.config.mjs.Environment Variables
NEXT_PUBLIC_GOOGLE_MAPS_API_KEY.env.local. Required for Google Maps location picker.
Must be prefixed with NEXT_PUBLIC_ for client-side access.API_BASE_URLsrc/config/api.js (NOT .env). Currently set to
https://lastiendas.pe/api. Change this single constant to switch environments.
3. Local Development Setup
Important: This project has two independent parts — the Next.js frontend and the PHP backend API. They must run on separate ports.
Frontend: localhost:3000 | PHP API:
requires a web server like XAMPP or PHP's built-in server at localhost:8000
Clone & Install Frontend
# Navigate to your projects folder
cd "G:/Client Projects/Sell (Jess)/67/65"
# Install all Node.js dependencies
npm install
# Verify installation
npm list next
Configure Backend Database Connection
Open
api/config/database.php. Switch to the LOCAL credentials block (lines 40-45) by
commenting out production and uncommenting local:
// FOR LOCAL DEVELOPMENT (XAMPP):
private $host = "localhost";
private $db_name = "marketplace"; // Your local DB name
private $username = "root";
private $password = ""; // XAMPP default = empty
public $conn;
// FOR PRODUCTION (lastiendas.pe):
// private $host = "localhost";
// private $db_name = "u278171752_jess_DB";
// private $username = "u278171752_jess_user";
// private $password = "Pak@12345$$";
// public $conn;
Import the Database
Use phpMyAdmin or the MySQL CLI to import the production dump:
# Method 1: MySQL CLI
mysql -u root -p marketplace < jess_DB.sql
# Method 2: phpMyAdmin (XAMPP)
# 1. Open http://localhost/phpmyadmin
# 2. Create database: 'marketplace' (or your chosen name)
# 3. Import: jess_DB.sql (194KB)
Switch API Base URL to Local
Open src/config/api.js and
comment out the production URL, uncomment localhost:
// SWITCH TO LOCAL:
const API_BASE_URL = 'http://localhost:8000/api'
// SWITCH TO PRODUCTION:
// const API_BASE_URL = 'https://lastiendas.pe/api'
Start the PHP Backend Server
The project includes a
start_api.bat script. Or use PHP CLI directly:
# Option A: Use the included batch file (Windows)
start_api.bat
# Option B: PHP built-in server (from project root)
# ⚠ Navigate to a level that exposes /api
php -S localhost:8000 -t .
# Option C: XAMPP
# Copy project to C:/xampp/htdocs/lastiendas/
# Access via: http://localhost/lastiendas/api/
Start the Next.js Frontend
# Development mode (hot reload, error messages)
npm run dev
# Frontend available at: http://localhost:3000
# Production build (for testing final output)
npm run build
npm run start
4. Complete File Structure Map
Frontend: /src
src/
├── app/ # Next.js App Router
│ ├── layout.js # Root layout — injects LanguageProvider
│ ├── page.js # Homepage (24KB) — main marketplace
│ ├── globals.css # Base styles
│ ├── admin/
│ │ └── page.js # Admin dashboard (608 lines, 25KB)
│ ├── seller/
│ │ └── page.js # Seller dashboard (493 lines, 20KB)
│ ├── auth/
│ │ └── page.js # Login/Register page
│ ├── product/
│ │ └── [...slug]/ # Dynamic product pages
│ │ └── ClientPage.js # Product detail view
│ ├── about-us/ # About page
│ ├── terms/ # Terms of service
│ └── information/ # Info pages (dynamic content)
│
├── components/ # Reusable UI components
│ ├── Navigation.js # Main site navigation (8.5KB)
│ ├── Footer.js # Global footer
│ ├── AuthModal.js # Login/Register modal (11.8KB)
│ ├── ProductGrid.js # Product listing grid (7.7KB)
│ ├── ProductModal.js # Product quick-view (6.7KB)
│ ├── StarRating.js # Star rating component (20KB)
│ ├── CompactRating.js # Compact version (21KB)
│ ├── SearchFilters.js # Search/filter bar (3KB)
│ ├── ToastContainer.js # Toast notifications (2KB)
│ ├── LocationPickerLeaflet.js # Map (Leaflet, no API key)
│ │
│ ├── admin/ # 25 Admin-only components
│ │ ├── Sidebar.js # Admin navigation sidebar
│ │ ├── TopBar.js # Admin top bar (8KB)
│ │ ├── DashboardOverview.js # Stats & metrics (11.8KB)
│ │ ├── ManageSellers.js # Seller management (45.9KB) ← LARGEST
│ │ ├── ManageProducts.js # Product moderation (20KB)
│ │ ├── UpcomingPayments.js # Payment tracking (27.8KB)
│ │ ├── NotificationsPanel.js # Email campaigns (20.7KB)
│ │ ├── EmailLogsPanel.js # Email history (13.8KB)
│ │ ├── EmailTemplateManager.js # Template editor (23KB)
│ │ ├── ReviewsManager.js # Review moderation (17.8KB)
│ │ ├── SponsoredProductsPanel.js # Sponsored ads (17.7KB)
│ │ ├── ManagePages.js # CMS pages (13.3KB)
│ │ └── ...13 more components
│ │
│ └── seller/ # 14 Seller-only components
│ ├── Sidebar.js # Seller nav sidebar
│ ├── TopBar.js # Seller top bar (7.9KB)
│ ├── DashboardSummary.js # Seller stats (7KB)
│ ├── ProductsTable.js # Product list (14.6KB)
│ ├── AddProductForm.js # Add/edit products (35.5KB) ← LARGEST
│ ├── MyCategoriesPanel.js # Category management (20.5KB)
│ ├── SubscriptionPanel.js # Payment info (19.4KB)
│ ├── ShopInfo.js # Shop settings (24.8KB)
│ └── ...6 more components
│
├── config/
│ ├── api.js # ALL API endpoints (109 lines) ← KEY FILE
│ └── tinymce.js # Rich text editor config
│
└── lib/
├── language.js # i18n context provider + useLanguage hook
├── theme.js # Theme utilities
├── searchLocation.js # Location search logic
└── languages/
├── en.json # English translations
└── es.json # Spanish translations
Backend: /api
api/
├── .htaccess # CORS + mod_rewrite rules
│
├── config/ # Core configuration
│ ├── database.php # PDO Database class (71 lines)
│ ├── auth.php # Token verification (112 lines)
│ ├── cors.php # CORS headers handler
│ ├── email.php # SMTP config (active)
│ └── email.example.php # Template for new config
│
├── admin/ # 31 admin-protected endpoints
│ ├── approve_user.php # Seller approval (5.3KB)
│ ├── pending_users.php # List pending (1.8KB)
│ ├── reject_user.php # Reject user (1.8KB)
│ ├── delete_user.php # Remove user (1.2KB)
│ ├── create_user.php # Add seller (4.6KB)
│ ├── sellers.php # All sellers data (3KB)
│ ├── reviews.php # Review moderation (6.4KB)
│ ├── pages.php # CMS CRUD (4.7KB)
│ ├── payment_settings.php # Payment config (2.5KB)
│ ├── payments_overview.php # Payment status (6.1KB)
│ ├── send_payment_reminders.php # Auto reminders (10.9KB)
│ ├── send_bulk_reminders.php # Bulk emails (11.1KB)
│ ├── send_manual_reminder.php # Single reminder (4.1KB)
│ ├── sponsored_requests.php # Sponsored ads CRUD (14.6KB)
│ ├── email_logs.php # Email history (6.4KB)
│ ├── email_templates.php # Template CRUD (7KB)
│ └── ...15 more endpoints
│
├── auth/
│ ├── login.php # Login + token issue (96 lines)
│ └── register.php # Seller registration (temp table)
│
├── products/
│ └── index.php # Products CRUD (GET/POST/PUT/DELETE/PATCH)
│
├── seller/ # 14 seller endpoints
│ ├── profile.php # Seller profile
│ ├── subscription.php # Subscription status
│ ├── categories.php # Seller categories
│ ├── payment_history.php # Payment records
│ └── ...10 more endpoints
│
├── categories/ # Category management
├── cities/ # City management
├── sponsored/ # Sponsored product endpoints
├── cron/ # Scheduled tasks
│ └── send_payment_reminders.php
├── upload/ # File upload handlers
│ ├── product_image.php # Image uploads
│ └── receipt.php # Payment receipt uploads
└── utils/ # Shared utilities
├── email.php # Core email engine (39KB)
├── smtp_mailer.php # SMTP wrapper (4.6KB)
├── hostinger_mailer.php # Hostinger-specific (10.7KB)
├── template_email_service.php # Template email (19KB)
└── payment_reminder_utils.php # Reminder logic (10.7KB)
5. Authentication System — How It Works
The auth system uses a
database-stored session token approach (not JWT). When a user
logs in, a 64-char hex token is generated and stored in the user_sessions table with a
24-hour expiry.
Login Flow (api/auth/login.php)
// 1. Accept POST: { username, password }
// 2. Query user_accounts JOIN user_details
$query = "SELECT ua.id, ua.username, ua.password_hash,
ua.user_type, ud.company_name ...
FROM user_accounts ua
LEFT JOIN user_details ud ON ud.user_id = ua.id";
// 3. Compare: password === password_hash (plaintext dev)
// 4. Generate token:
$token = bin2hex(random_bytes(32));
// 5. Store in DB:
INSERT INTO user_sessions (user_id, token, expires_at)
VALUES (?, ?, DATE_ADD(NOW(), INTERVAL 24 HOUR));
// 6. Return token to frontend
Frontend Auth Guard (src/app/admin/page.js)
// On mount, check localStorage:
const token = localStorage.getItem('auth_token')
const userData = localStorage.getItem('user_data')
if (!token || !userData) {
router.replace('/auth') // Redirect if not logged in
return
}
const parsed = JSON.parse(userData)
if (parsed?.user_type !== 'admin') {
router.replace('/seller') // Wrong user type
return
}
setUser(parsed)
fetchAdminData(parsed) // Load dashboard data
Token Verification (api/config/auth.php)
Every protected admin endpoint calls
verifyToken(). It extracts the Bearer <token> from the
Authorization header and validates it against the user_sessions table, checking
expiry.
$query = "SELECT us.user_id, ua.username, ua.user_type
FROM user_sessions us
JOIN user_accounts ua ON ua.id = us.user_id
WHERE us.token = ? AND us.expires_at > NOW()";
Sending Auth Headers (Frontend)
Every admin API call includes the Authorization header:
const token = localStorage.getItem('auth_token')
const authHeaders = token
? { 'Authorization': `Bearer ${token}` }
: undefined
const res = await fetch(API_ENDPOINTS.ADMIN_SELLERS, {
headers: authHeaders
})
6. The API Registry — src/config/api.js
This is the single source of truth for all 40+ API endpoints. To add a new endpoint, you ONLY touch this file and nothing else.
// src/config/api.js
const API_BASE_URL = 'https://lastiendas.pe/api' // ← Change this for local dev
export const API_ENDPOINTS = {
// Auth
AUTH_LOGIN: `${API_BASE_URL}/auth/login.php`,
AUTH_REGISTER: `${API_BASE_URL}/auth/register.php`,
// Products (full CRUD via single index.php)
PRODUCTS: `${API_BASE_URL}/products/`,
// Categories & Cities
CATEGORIES: `${API_BASE_URL}/categories/`,
CITIES: `${API_BASE_URL}/cities/`,
// Admin Endpoints (all protected)
ADMIN_PENDING_USERS: `${API_BASE_URL}/admin/pending_users.php`,
ADMIN_APPROVE_USER: `${API_BASE_URL}/admin/approve_user.php`,
ADMIN_SELLERS: `${API_BASE_URL}/admin/sellers.php`,
ADMIN_PAYMENTS_OVERVIEW: `${API_BASE_URL}/admin/payments_overview.php`,
ADMIN_SPONSORED_REQUESTS: `${API_BASE_URL}/admin/sponsored_requests.php`,
// ... 35+ more endpoints
}
How to Add a New Endpoint
// Step 1: Create PHP file in /api/
// e.g., api/admin/my_feature.php
// Step 2: Add to api.js
ADMIN_MY_FEATURE: `${API_BASE_URL}/admin/my_feature.php`,
// Step 3: Use in any component
import { API_ENDPOINTS } from '@/config/api'
const res = await fetch(API_ENDPOINTS.ADMIN_MY_FEATURE)
Helper: buildApiUrl()
// Append query params without string interpolation
import { buildApiUrl, API_ENDPOINTS } from '@/config/api'
const url = buildApiUrl(API_ENDPOINTS.PRODUCTS, {
seller_id: currentUser.id,
limit: 100,
lang: 'es'
})
// Result: https://lastiendas.pe/api/products/?seller_id=3&limit=100&lang=es
7. Internationalization (i18n) System
Language Provider — src/lib/language.js
Wraps the entire app via
src/app/layout.js. Uses React Context. Persists choice in
localStorage. Defaults to Spanish ('es').
// How to use in any component:
import { useLanguage } from '@/lib/language'
const { t, language, changeLanguage } = useLanguage()
// Translate a key (dot-notation)
t('admin.dashboard') // → "Panel de Control"
// Change language
changeLanguage('en') // Switches globally
Translation Files
To add a new translation key:
// en.json
{
"admin": {
"myNewFeature": "My New Feature"
}
}
// Usage: t('admin.myNewFeature')
8. Production Deployment
Live Production — Currently
deployed at https://lastiendas.pe (Hostinger)
Frontend Build & Deploy
# 1. Ensure API_BASE_URL = production URL in api.js
# 2. Build static export
npm run build
# Output: /out directory (static HTML/CSS/JS)
# 3. Upload /out folder to Hostinger public_html
# 4. The .htaccess handles routing
next.config.mjs sets
output: 'export' — generates a static site in /out folder, ready for
shared hosting.
Backend Deploy
# 1. Upload /api directory to server root
# 2. Edit api/config/database.php:
# Set production credentials
# 3. Ensure api/.htaccess is present
# (handles CORS headers + routing)
# 4. Verify cron jobs:
# api/cron/send_payment_reminders.php
# 5. Run setup if needed:
# setup.php (creates DB tables)
CORS Configuration (api/.htaccess)
The api/.htaccess and
api/config/cors.php handle cross-origin requests. All PHP files include
cors.php at the top to output the necessary headers for the frontend domain to
communicate.
# api/.htaccess — Key rules
Header always set Access-Control-Allow-Origin "*"
Header always set Access-Control-Allow-Methods "GET, POST, PUT, DELETE, PATCH, OPTIONS"
Header always set Access-Control-Allow-Headers "Content-Type, Authorization"
9. Quick Command Reference
NPM Scripts
localhost:3000/out folder for production upload