A comprehensive web platform showcasing traditional Indonesian cuisine with AI-powered recipe recognition
π Live Demo β’ π Documentation β’ π Report Bug β’ π‘ Request Feature
- π― Project Overview
- β¨ Key Features
- π Technology Stack
- π Project Structure
- πΌ Screenshots
- π Pages Documentation
- π¨ Styling Documentation
- π API Integration
- β‘ Installation & Setup
- π Development Workflow
- π§ͺ Testing
- π Deployment
- π€ Contributing
- π₯ Team
- π License
- π Acknowledgments
Foodinary is a cutting-edge web platform designed to preserve and celebrate Indonesia's rich culinary heritage. Our platform combines traditional recipe knowledge with modern AI technology to create an immersive culinary experience for food enthusiasts worldwide.
- Preserve Indonesian culinary traditions through digital documentation
- Educate users about the cultural significance of traditional dishes
- Innovate with AI-powered food recognition technology
- Connect food lovers through a comprehensive recipe database
- π Cultural Preservation: Maintain authentic Indonesian recipes and their historical context
- π€ AI Integration: Leverage machine learning for intelligent food recognition
- π₯ Community Building: Create a platform for culinary enthusiasts to explore and share
- π± User Experience: Provide intuitive, responsive design across all devices
- π Discovery: Enable easy exploration of Indonesian cuisine diversity
- Hero Section with compelling call-to-action
- Popular Cuisine Showcase featuring Indonesian favorites
- Feature Highlights with interactive previews
- Responsive Design optimized for all devices
- Comprehensive Database with 30+ authentic Indonesian recipes
- Smart Filtering by taste profiles (Asin, Gurih, Manis, Pedas, dll.)
- Advanced Search with real-time results
- Detailed Recipe Pages with step-by-step instructions
- Regional Information highlighting dish origins
- Photo Upload with drag-and-drop functionality
- Real-time Camera capture for instant analysis
- Machine Learning Integration for accurate food identification
- Recipe Suggestions based on detected dishes
- Multi-format Support (JPEG, PNG, WebP)
- Personal Statistics tracking cooking journey
- Recipe History with detailed timestamps
- Favorites Management for quick access
- Account Customization with profile settings
- Progress Tracking for culinary goals
- Secure Registration with email verification
- JWT Token Authentication for session management
- Password Recovery with reset functionality
- Protected Routes ensuring secure access
- Remember Me functionality for convenience
- Offline Support for core functionality
- Social Sharing capabilities
- Print-friendly recipe formats
- Nutritional Information (Coming Soon)
- Video Tutorials (Coming Soon)
| Technology | Version | Purpose |
|---|---|---|
| JavaScript | ES6+ | Core programming language with modern syntax |
| Webpack | 5.98.0 | Module bundling and build optimization |
| CSS3 | Latest | Advanced styling with Flexbox & Grid |
| HTML5 | Latest | Semantic markup structure |
| Tool | Version | Purpose |
|---|---|---|
| Webpack | 5.98.0 | Module bundler and asset optimization |
| Babel | 7.26.9 | JavaScript transpilation for browser compatibility |
| CSS Loader | 7.1.2 | CSS processing and optimization |
| Mini CSS Extract Plugin | 2.9.2 | CSS extraction for production builds |
| Clean Webpack Plugin | 4.0.0 | Build directory cleanup |
| Copy Webpack Plugin | 13.0.0 | Static asset copying |
| Package | Version | Purpose |
|---|---|---|
| SweetAlert2 | 11.22.0 | Beautiful, responsive popups and modals |
| IDB | 8.0.3 | IndexedDB wrapper for client-side storage |
| Package | Version | Purpose |
|---|---|---|
| Webpack Dev Server | 5.2.0 | Development server with hot reload |
| HTTP Server | 14.1.1 | Production build serving |
| Prettier | 3.5.3 | Code formatting and style consistency |
- β Chrome 80+
- β Firefox 75+
- β Safari 13+
- β Edge 80+
- β Mobile browsers (iOS Safari, Chrome Mobile)
frontend-backend/
βββ π dist/ # Production build output
βββ π src/ # Source code directory
β βββ π index.html # Main HTML entry point
β βββ π public/ # Static assets & resources
β β βββ π data/
β β β βββ π recipes.json # Recipe database (30+ dishes)
β β βββ π images/ # Image assets
β β βββ π landing-page/ # Hero & showcase images
β β βββ π meet-our-team/ # Team member photos
β β βββ π auth-image.png # Authentication visuals
β β βββ π logo.png # Brand assets
β βββ π scripts/ # JavaScript source code
β β βββ π index.js # Application entry point
β β βββ π config.js # Environment configuration
β β βββ π data/ # Data layer
β β β βββ π api.js # API communication layer
β β β βββ π database.js # Local storage management
β β βββ π pages/ # Page components
β β β βββ π app.js # Main application controller
β β β βββ π landingPage/ # Homepage components
β β β βββ π auth/ # Authentication pages
β β β β βββ π login/ # Login functionality
β β β β βββ π register/ # User registration
β β β β βββ π reset-password/ # Password recovery
β β β βββ π dashboard/ # User dashboard
β β β βββ π recipe/ # Recipe browsing & search
β β β βββ π cekResep/ # AI recipe recognition
β β β βββ π detail/ # Recipe detail pages
β β β βββ π favorite/ # User favorites
β β β βββ π history/ # User activity history
β β β βββ π about/ # About page & team info
β β β βββ π accountSettings/ # User preferences
β β βββ π routes/ # Routing system
β β β βββ π routes.js # Route definitions & guards
β β β βββ π url-parser.js # URL parsing utilities
β β βββ π utils/ # Utility functions
β β βββ π auth.js # Authentication helpers
β β βββ π camera.js # Camera API integration
β β βββ π index.js # General utilities
β βββ π styles/ # Stylesheet directory
β βββ π styles.css # Main stylesheet (2500+ lines)
β βββ π responsive.css # Mobile-first responsive design
βββ π package.json # Project dependencies & scripts
βββ π webpack.common.js # Shared webpack configuration
βββ π webpack.dev.js # Development environment config
βββ π webpack.prod.js # Production build optimization
βββ π .gitignore # Git ignore rules
βββ π README.md # Project documentation
Frontend Architecture Pattern: Component-Based SPA (Single Page Application)
- π Pages: Individual page components with render/afterRender lifecycle
- π Routes: Hash-based routing with authentication guards
- π Utils: Reusable utility functions and helpers
- π Data: API abstraction and local storage management
- π Styles: Modular CSS with responsive design principles
π Data Flow:
User Interaction β Page Component β API Layer β Backend β Database
β
DOM Update β State Change β Response Processing
Purpose: Homepage yang memperkenalkan Foodinary kepada pengunjung
π Sections:
- Navigation Bar: Responsive menu dengan authentication state
- Hero Section: Compelling introduction dengan CTA buttons
- Popular Cuisine: Showcase makanan Indonesia terpopuler
- Foodinary Experience: Feature highlights dengan badges
- Footer: Contact info dan quick links
π§ Key Implementation:
// Dynamic authentication state
const isLoggedIn = token && token !== 'null' && token !== 'undefined';
// Responsive navigation
${isLoggedIn
? `<a href="#/dashboard" class="btn-primary">Dashboard</a>`
: `<a href="#/login" class="btn-outline">Login</a>
<a href="#/register" class="btn-primary">Sign Up</a>`
}Purpose: Halaman eksplorasi dan pencarian resep
π Features:
- Search Bar: Real-time recipe search
- Taste Filters: Filter berdasarkan rasa (Asin, Gurih, Manis, Pedas, dll.)
- Recipe Grid: Responsive card layout
- Quick Actions: Direct link to recipe details
π Data Flow:
getRecipes() β renderRecipeCards() β Event Handlers β User InteractionPurpose: AI-powered food recognition system
π Advanced Features:
- Drag & Drop Upload: Intuitive file handling
- Camera Integration: Real-time photo capture
- Preview System: Image preview before analysis
- ML Processing: Backend integration for food recognition
π± Camera Implementation:
// Camera stream handling
navigator.mediaDevices.getUserMedia({ video: true }).then((stream) => {
videoElement.srcObject = stream;
});- User Greeting: Personalized welcome message
- Statistics Cards: Total History, Total Favorites
- Quick Navigation: Sidebar dengan active states
- User Avatar: Profile management access
- Activity Timeline: Chronological recipe interactions
- Delete Functionality: Remove unwanted history entries
- Search History: Find specific past activities
- Favorites Grid: Visual layout for saved recipes
- Quick Access: Direct navigation to recipe details
- Management Tools: Add/remove favorites
Features:
- Form Validation: Real-time input validation
- Remember Me: Persistent login sessions
- Error Handling: User-friendly error messages
- Password Toggle: Show/hide password functionality
Features:
- Complete Registration: Full user profile setup
- Password Confirmation: Double-password verification
- Terms Agreement: Terms & conditions acceptance
- Email Validation: Format and availability checking
Features:
- Email Recovery: Password reset via email
- Security Questions: Additional verification steps
- New Password Setup: Secure password creation
Purpose: Comprehensive recipe information display
π Sections:
- Recipe Header: Image, name, origin, taste profile
- Ingredients List: Detailed ingredients dengan quantities
- Equipment Needed: Required cooking tools
- Step-by-Step Instructions: Numbered cooking process
- Source Attribution: Recipe source references
Purpose: Platform information dan team showcase
π Content Sections:
- Mission Statement: Foodinary's purpose dan vision
- Team Showcase: Developer profiles dengan social links
- Contact Information: Multiple communication channels
- Project Background: Development story dan objectives
π₯ Team Integration:
// Dynamic team member rendering
teamMembers
.map(
(member) => `
<div class="team-card">
<div class="profile-image" style="background-image: url('${member.image}');">
</div>
<h3>${member.name}</h3>
<div class="role">${member.role}</div>
<p class="description">${member.description}</p>
<div class="social-links">
${member.socialLinks.map((link) => `<a href="${link.url}">${link.icon}</a>`).join("")}
</div>
</div>
`
)
.join("");Purpose: User preference management
π§ Settings Options:
- Profile Information: Update personal details
- Password Change: Secure password modification
- Privacy Preferences: Data handling preferences
- Notification Settings: Communication preferences
Stylesheet architecture menggunakan BEM methodology dan component-based styling:
/* Block Element Modifier (BEM) Pattern */
.component {
}
.component__element {
}
.component__element--modifier {
}Organized Sections:
| Line Range | Section | Description |
|---|---|---|
| 1-200 | Navigation | Header, menu, responsive navigation |
| 200-400 | Hero Section | Landing page hero styling |
| 400-600 | Recipe Cards | Card components dan grid layouts |
| 600-800 | Authentication | Login/register form styling |
| 800-1000 | Buttons & Controls | Interactive elements |
| 1000-1400 | Dashboard | Dashboard layout dan components |
| 1400-1600 | Forms | Form styling dan validation states |
| 1600-1800 | Recipe Finder | Search dan filter components |
| 1800-2000 | Footer | Footer layout dan social links |
| 2000-2200 | Utilities | Helper classes dan animations |
| 2200-2500+ | Responsive | Media queries dan breakpoints |
/* π§ Navigation System */
.navigation {
/* Main navigation container */
position: fixed;
top: 0;
width: 100%;
z-index: 1000;
}
.nav-container {
/* Flex container for nav items */
display: flex;
justify-content: space-between;
align-items: center;
}
.hamburger-menu {
/* Mobile menu trigger */
display: none;
flex-direction: column;
cursor: pointer;
}
/* π Dashboard Layout */
.db-container {
/* Dashboard grid container */
display: grid;
grid-template-columns: 250px 1fr;
min-height: 100vh;
}
.db-sidebar {
/* Sidebar navigation */
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
padding: 2rem 1rem;
}
.db-main-content {
/* Main content area */
padding: 2rem;
background-color: #f8f9fa;
}
/* π² Recipe Components */
.recipe-card {
/* Individual recipe card */
background: white;
border-radius: 12px;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
transition: transform 0.3s ease;
}
.recipe-card:hover {
transform: translateY(-5px);
box-shadow: 0 8px 25px rgba(0, 0, 0, 0.15);
}
/* π± Form Components */
.form-group {
/* Form input groups */
margin-bottom: 1.5rem;
position: relative;
}
.form-group input {
/* Form inputs styling */
width: 100%;
padding: 0.75rem 1rem;
border: 2px solid #e9ecef;
border-radius: 8px;
transition: border-color 0.3s ease;
}
.form-group input:focus {
border-color: #007bff;
outline: none;
box-shadow: 0 0 0 3px rgba(0, 123, 255, 0.1);
}Mobile-First Approach:
/* Base styles for mobile */
.container {
padding: 1rem;
}
/* Tablet styles */
@media (min-width: 768px) {
.container {
padding: 2rem;
}
.db-container {
grid-template-columns: 200px 1fr;
}
}
/* Desktop styles */
@media (min-width: 1024px) {
.container {
max-width: 1200px;
margin: 0 auto;
padding: 3rem;
}
.db-container {
grid-template-columns: 250px 1fr;
}
}
/* Large desktop */
@media (min-width: 1440px) {
.container {
max-width: 1400px;
}
}π Breakpoint System:
- Mobile:
< 768px- Single column, hamburger menu - Tablet:
768px - 1024px- Collapsed sidebar, grid adjustments - Desktop:
1024px - 1440px- Full sidebar, multi-column layouts - Large Desktop:
> 1440px- Max-width container, optimized spacing
Color Palette:
:root {
/* Primary Colors */
--primary-blue: #007bff;
--primary-dark: #0056b3;
/* Secondary Colors */
--secondary-gray: #6c757d;
--light-gray: #f8f9fa;
/* Accent Colors */
--success-green: #28a745;
--danger-red: #dc3545;
--warning-yellow: #ffc107;
/* Text Colors */
--text-primary: #212529;
--text-secondary: #6c757d;
--text-muted: #adb5bd;
}Typography Scale:
/* Heading Hierarchy */
h1 {
font-size: 2.5rem;
font-weight: 700;
}
h2 {
font-size: 2rem;
font-weight: 600;
}
h3 {
font-size: 1.75rem;
font-weight: 600;
}
h4 {
font-size: 1.5rem;
font-weight: 500;
}
/* Body Text */
body {
font-size: 1rem;
line-height: 1.6;
}
.text-small {
font-size: 0.875rem;
}
.text-large {
font-size: 1.125rem;
}Animation Library:
/* Smooth Transitions */
.animate-fadeInUp {
animation: fadeInUp 0.6s ease-out;
}
@keyframes fadeInUp {
from {
opacity: 0;
transform: translateY(30px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* Hover Effects */
.hover-lift {
transition: transform 0.3s ease;
}
.hover-lift:hover {
transform: translateY(-2px);
}Base Configuration:
// Environment Configuration
const BASE_URL = "https://backend-4lij.onrender.com";
const ACCESS_TOKEN_KEY = "accessToken";
// Request Headers
const getAuthHeaders = () => ({
"Content-Type": "application/json",
Authorization: `Bearer ${getAccessToken()}`
});// User Registration
const registerUser = async ({ name, email, password }) => {
const response = await fetch(`${BASE_URL}/auth/register`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ name, email, password })
});
return response.json();
};
// User Login
const loginUser = async ({ email, password }) => {
const response = await fetch(`${BASE_URL}/auth/login`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email, password })
});
return response.json();
};
// Password Reset
const resetPassword = async ({ email }) => {
const response = await fetch(`${BASE_URL}/auth/reset-password`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email })
});
return response.json();
};// Get All Recipes
const getRecipes = async () => {
const response = await fetch(`${BASE_URL}/recipes`);
return response.json();
};
// Get Recipe by ID
const getRecipeById = async (id) => {
const response = await fetch(`${BASE_URL}/recipes/${id}`);
return response.json();
};
// Search Recipes
const searchRecipes = async (query) => {
const response = await fetch(`${BASE_URL}/recipes/search?q=${encodeURIComponent(query)}`);
return response.json();
};
// Filter Recipes by Taste
const filterRecipesByTaste = async (taste) => {
const response = await fetch(`${BASE_URL}/recipes/filter?taste=${taste}`);
return response.json();
};// Get User Profile
const getUserProfile = async () => {
const response = await fetch(`${BASE_URL}/user/profile`, {
headers: getAuthHeaders()
});
return response.json();
};
// Update User Profile
const updateUserProfile = async (userData) => {
const response = await fetch(`${BASE_URL}/user/profile`, {
method: "PUT",
headers: getAuthHeaders(),
body: JSON.stringify(userData)
});
return response.json();
};
// Get User History
const getUserHistory = async () => {
const response = await fetch(`${BASE_URL}/user/history`, {
headers: getAuthHeaders()
});
return response.json();
};
// Get User Favorites
const getUserFavorites = async () => {
const response = await fetch(`${BASE_URL}/user/favorites`, {
headers: getAuthHeaders()
});
return response.json();
};// Upload Image for Recognition
const uploadImageForRecognition = async (imageFile) => {
const formData = new FormData();
formData.append("image", imageFile);
const response = await fetch(`${BASE_URL}/ai/recognize`, {
method: "POST",
headers: {
Authorization: `Bearer ${getAccessToken()}`
},
body: formData
});
return response.json();
};
// Get Recognition History
const getRecognitionHistory = async () => {
const response = await fetch(`${BASE_URL}/ai/history`, {
headers: getAuthHeaders()
});
return response.json();
};interface Recipe {
id: number;
name: string;
asal_daerah: string;
rasa_dominan: "Asin" | "Gurih" | "Manis" | "Pedas" | "Asam";
deskripsi: string;
bahan: string[];
alat: string[];
cara_membuat: string[];
gambar: string;
sumber: string;
created_at?: string;
updated_at?: string;
}interface User {
id: number;
name: string;
email: string;
avatar?: string;
created_at: string;
preferences?: {
favorite_tastes: string[];
dietary_restrictions: string[];
};
}interface RecognitionResult {
id: number;
image_url: string;
predicted_food: string;
confidence: number;
suggested_recipes: Recipe[];
timestamp: string;
user_id: number;
}// Authentication State Management
class AuthManager {
static setToken(token) {
localStorage.setItem(ACCESS_TOKEN_KEY, token);
}
static getToken() {
return localStorage.getItem(ACCESS_TOKEN_KEY);
}
static removeToken() {
localStorage.removeItem(ACCESS_TOKEN_KEY);
}
static isAuthenticated() {
const token = this.getToken();
return token && token !== "null" && token !== "undefined";
}
}
// Route Protection
const checkAuthenticatedRoute = (page) => {
if (!AuthManager.isAuthenticated()) {
location.hash = "/login";
return null;
}
return page;
};// Centralized Error Handler
const handleApiError = (error, context = "") => {
console.error(`API Error ${context}:`, error);
if (error.status === 401) {
// Unauthorized - redirect to login
AuthManager.removeToken();
location.hash = "/login";
return;
}
if (error.status === 403) {
// Forbidden
showErrorMessage("Access denied. Please check your permissions.");
return;
}
if (error.status >= 500) {
// Server error
showErrorMessage("Server error. Please try again later.");
return;
}
// Default error handling
showErrorMessage(error.message || "An unexpected error occurred.");
};
// API Wrapper with Error Handling
const apiCall = async (apiFunction, ...args) => {
try {
const result = await apiFunction(...args);
return result;
} catch (error) {
handleApiError(error, apiFunction.name);
throw error;
}
};// Request Caching
const cache = new Map();
const cachedFetch = async (url, options = {}) => {
const cacheKey = `${url}_${JSON.stringify(options)}`;
if (cache.has(cacheKey)) {
return cache.get(cacheKey);
}
const response = await fetch(url, options);
const data = await response.json();
// Cache for 5 minutes
cache.set(cacheKey, data);
setTimeout(() => cache.delete(cacheKey), 300000);
return data;
};
// Request Debouncing for Search
const debounce = (func, wait) => {
let timeout;
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout);
func(...args);
};
clearTimeout(timeout);
timeout = setTimeout(later, wait);
};
};
const debouncedSearch = debounce(searchRecipes, 300);Before getting started, ensure you have the following installed:
| Requirement | Version | Download Link |
|---|---|---|
| Node.js | 16.0+ | nodejs.org |
| npm | 8.0+ | Included with Node.js |
| Git | Latest | git-scm.com |
# 1οΈβ£ Clone the repository
git clone https://github.com/foodinary-project/frontend-backend.git
cd frontend-backend
# 2οΈβ£ Install dependencies
npm install
# 3οΈβ£ Start development server
npm run start-dev
# 4οΈβ£ Open your browser
# Navigate to http://localhost:3000# Clone the repository
git clone https://github.com/foodinary-project/frontend-backend.git
# Navigate to project directory
cd frontend-backend
# Verify project structure
ls -la# Create environment configuration (if needed)
cp src/scripts/config.example.js src/scripts/config.js
# Edit configuration file
# Update BASE_URL and other environment variables# Install all dependencies
npm install
# Verify installation
npm list --depth=0# Start development server with hot reload
npm run start-dev
# Server will start on http://localhost:3000
# Hot reload enabled - changes will reflect automatically# π§ Development
npm run start-dev # Start dev server with hot reload
# Port: 3000
# Source maps: enabled
# Minification: disabled
# π Production Build
npm run build # Create optimized production build
# Output: dist/ directory
# Minification: enabled
# Source maps: disabled
# π Serve Production
npm run serve # Serve production build locally
# Port: 8080
# Simulates production environmentmodule.exports = {
mode: "development",
devServer: {
static: "./dist",
port: 3000,
hot: true,
open: true,
historyApiFallback: true
},
devtool: "eval-source-map"
};module.exports = {
mode: "production",
optimization: {
minimize: true,
splitChunks: {
chunks: "all"
}
},
devtool: "source-map"
};β Port 3000 already in use
# Solution: Use different port
npx webpack serve --config webpack.dev.js --port 3001β Module not found errors
# Solution: Clear cache and reinstall
rm -rf node_modules package-lock.json
npm installβ Build fails on Windows
# Solution: Use cross-platform commands
npm install --save-dev cross-env
# Update package.json scripts with cross-envβ Hot reload not working
# Solution: Check firewall/proxy settings
# Ensure no browser extensions blocking websockets# Enable script execution
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope CurrentUser
# Install Node.js via chocolatey (optional)
choco install nodejs
# Clone and setup
git clone https://github.com/foodinary-project/frontend-backend.git
cd frontend-backend
npm install
npm run start-dev# Install Node.js via homebrew (optional)
brew install node
# Clone and setup
git clone https://github.com/foodinary-project/frontend-backend.git
cd frontend-backend
npm install
npm run start-dev# Install Node.js (Ubuntu/Debian)
curl -fsSL https://deb.nodesource.com/setup_18.x | sudo -E bash -
sudo apt-get install -y nodejs
# Clone and setup
git clone https://github.com/foodinary-project/frontend-backend.git
cd frontend-backend
npm install
npm run start-dev# Dockerfile
FROM node:18-alpine
WORKDIR /app
COPY package*.json ./
RUN npm install
COPY . .
EXPOSE 3000
CMD ["npm", "run", "start-dev"]# Build and run with Docker
docker build -t foodinary .
docker run -p 3000:3000 foodinary# Start development server
npm run start-dev
# Features enabled:
# β
Hot Module Replacement (HMR)
# β
Source Maps for debugging
# β
Development optimizations
# β
Error overlay in browser
# β
Automatic browser refreshDevelopment Features:
- Port:
3000(configurable) - Hot Reload: Instant code changes reflection
- Source Maps: Enhanced debugging experience
- Error Overlay: In-browser error display
- CSS Hot Reload: Styles update without page refresh
# Create optimized production build
npm run build
# Build optimizations:
# β
Code minification & compression
# β
Asset optimization
# β
Bundle splitting
# β
Tree shaking (dead code elimination)
# β
CSS extraction & optimizationProduction Features:
- Output Directory:
dist/ - Minification: JavaScript & CSS compressed
- Asset Optimization: Images & fonts optimized
- Bundle Analysis: Generated bundle stats
- Source Maps: Production-ready source maps
# Serve production build
npm run serve
# Server configuration:
# π Port: 8080
# π§ Static file serving
# π Production environment simulationmodule.exports = {
entry: {
app: "./src/scripts/index.js" // Application entry point
},
plugins: [
new HtmlWebpackPlugin({
template: "./src/index.html" // HTML template
}),
new CopyWebpackPlugin({
patterns: [
{
from: "./src/public/", // Static assets
to: "./dist/"
}
]
})
],
module: {
rules: [
{
test: /\.(png|jpe?g|gif)$/i,
type: "asset/resource" // Asset handling
}
]
}
};module.exports = merge(common, {
mode: "development",
devServer: {
static: "./dist",
port: 3000,
hot: true, // Hot Module Replacement
client: {
overlay: {
errors: true, // Show errors in browser
warnings: true
}
}
},
devtool: "eval-source-map" // Fast source maps
});module.exports = merge(common, {
mode: "production",
plugins: [
new CleanWebpackPlugin(), // Clean dist folder
new MiniCssExtractPlugin() // Extract CSS
],
module: {
rules: [
{
test: /\.js$/,
exclude: /node_modules/,
use: [
{
loader: "babel-loader",
options: {
presets: ["@babel/preset-env"] // ES6+ transpilation
}
}
]
}
]
}
});{
"scripts": {
"start-dev": "webpack serve --config webpack.dev.js",
"build": "webpack --config webpack.prod.js",
"serve": "http-server dist",
"lint": "prettier --check src/",
"format": "prettier --write src/"
}
}# Code formatting with Prettier
npm run format # Format all files
npm run lint # Check formatting
# Manual formatting commands
npx prettier --write "src/**/*.{js,css,html}"
npx prettier --check "src/**/*.{js,css,html}"# Analyze bundle size
npm run build -- --analyze
# Bundle composition:
# π¦ Main bundle: Application code
# π¦ Vendor bundle: Third-party libraries
# π¦ CSS bundle: Extracted stylesheets
# π¦ Assets: Images, fonts, static files// Source maps enabled - use browser DevTools
console.log("Debug info:", data);
// Breakpoints work in original source files
debugger;
// Hot reload preserves state
if (module.hot) {
module.hot.accept();
}# Serve production build locally
npm run serve
# Check production issues
# Source maps available for debugging
# Performance profiling available- β‘ Fast Builds: Optimized for development speed
- π Incremental Compilation: Only changed files rebuild
- π¦ Module Caching: Faster subsequent builds
- π Code Splitting: Lazy loading for better performance
- π³ Tree Shaking: Remove unused code
- π¦ Asset Optimization: Compressed images & fonts
- π§ Minification: Reduced bundle sizes
| Issue | Solution |
|---|---|
| Port in use | npx webpack serve --port 3001 |
| Module not found | rm -rf node_modules && npm install |
| Hot reload broken | Check firewall/proxy settings |
| Build failing | Clear cache: rm -rf dist/ && npm run build |
| CSS not updating | Hard refresh: Ctrl+Shift+R |
# Install testing dependencies
npm install --save-dev jest @testing-library/dom @testing-library/jest-dom
# Run tests
npm test
# Watch mode
npm run test:watch
# Coverage report
npm run test:coverage- Unit Tests: Component logic testing
- Integration Tests: API integration testing
- E2E Tests: User workflow testing
- Visual Tests: UI component testing
# Build for production
npm run build
# Deploy to GitHub Pages
npm install --save-dev gh-pages
npx gh-pages -d dist# Build command: npm run build
# Publish directory: dist
# Environment variables: Set in Netlify dashboard# Install Vercel CLI
npm install -g vercel
# Deploy
vercel --prod# Build and sync to S3
npm run build
aws s3 sync dist/ s3://your-bucket-nameWe welcome contributions from the community! Please follow these guidelines to contribute to Foodinary.
-
π΄ Fork the Repository
# Fork the repo on GitHub, then clone your fork git clone https://github.com/YOUR_USERNAME/frontend-backend.git cd frontend-backend
-
πΏ Create a Feature Branch
# Create and switch to a new branch git checkout -b feature/your-feature-name # or for bug fixes git checkout -b fix/bug-description
-
π§ Make Your Changes
- Follow the existing code style
- Add comments for complex logic
- Test your changes thoroughly
-
β Test Your Changes
# Run the development server npm run start-dev # Test your changes manually # Ensure no console errors
-
π Commit Your Changes
# Stage your changes git add . # Commit with descriptive message git commit -m "feat: add new recipe filter functionality"
-
π Push and Create Pull Request
# Push to your fork git push origin feature/your-feature-name # Create pull request on GitHub
- Use ES6+ JavaScript features
- Follow camelCase naming convention
- Use meaningful variable names
- Add JSDoc comments for functions
- Maintain consistent indentation (2 spaces)
type(scope): description
Examples:
feat(auth): add password reset functionality
fix(recipe): resolve search filter bug
docs(readme): update installation guide
style(css): improve responsive design
refactor(api): optimize data fetching
- Title: Clear, descriptive title
- Description: Explain what changes were made and why
- Screenshots: Include screenshots for UI changes
- Testing: Describe how you tested the changes
- Breaking Changes: Highlight any breaking changes
When reporting bugs, please include:
**Bug Description**
A clear description of the bug
**Steps to Reproduce**
1. Go to '...'
2. Click on '...'
3. See error
**Expected Behavior**
What you expected to happen
**Screenshots**
If applicable, add screenshots
**Environment:**
- OS: [e.g. Windows 10]
- Browser: [e.g. Chrome 96]
- Node.js version: [e.g. 16.14.0]For new features, please include:
**Feature Description**
Clear description of the feature
**Use Case**
Why is this feature needed?
**Proposed Solution**
How should it work?
**Additional Context**
Any other relevant information# 1. Clone your fork
git clone https://github.com/YOUR_USERNAME/frontend-backend.git
cd frontend-backend
# 2. Add upstream remote
git remote add upstream https://github.com/foodinary-project/frontend-backend.git
# 3. Install dependencies
npm install
# 4. Start development server
npm run start-dev
# 5. Keep your fork synced
git fetch upstream
git checkout main
git merge upstream/main- UI/UX enhancements
- Responsive design improvements
- New page components
- Animation and interactions
- New recipe filters
- User profile enhancements
- Social sharing features
- Offline functionality
- Bundle size optimization
- Loading speed improvements
- Caching strategies
- Progressive Web App features
- API documentation
- Code comments
- Tutorial creation
- Translation
- Unit test creation
- Integration testing
- E2E test scenarios
- Performance testing
Contributors will be:
- Credited in the project contributors list
- Mentioned in release notes for significant contributions
- Featured on our team page for ongoing contributors
- Indonesian Culinary Heritage - For the rich cultural inspiration
- Open Source Community - For the amazing tools and libraries
- Webpack Team - For the powerful build system
- SweetAlert2 - For beautiful user interactions
- Font Awesome - For comprehensive icon library
- Indonesian Recipe Database - Traditional recipe sources
- Cultural Research - Indonesian culinary history
- Design Inspiration - Modern web design trends
- Technical Documentation - MDN Web Docs, Webpack Documentation
- JavaScript ES6+ - Modern web development
- CSS3 - Beautiful styling capabilities
- HTML5 - Semantic web structure
- Webpack - Powerful module bundling
- Node.js - JavaScript runtime excellence
Β© 2025 Foodinary Team. All rights reserved.