Skip to main content

Next.js Web App Bootstrap

Ultra-modern web application for Talbino: Marketing site + optional buyer web + admin console.

Tech Stack

ComponentTechnologyPurpose
FrameworkNext.js 14 (App Router)React framework with SSR/SSG
LanguageTypeScriptType safety
StylingTailwind CSSUtility-first CSS
UI Componentsshadcn/uiAccessible component library
i18nnext-intlInternationalization (ar/en)
AuthNextAuth.jsAuthentication
API ClientAxios + React QueryData fetching
FormsReact Hook Form + ZodForm validation
StateZustandGlobal state management
IconsLucide ReactIcon library
DeploymentVercelHosting + CDN

Project Structure

web/
├── src/
│ ├── app/ # App Router
│ │ ├── [locale]/ # Locale wrapper
│ │ │ ├── (marketing)/ # Marketing pages (public)
│ │ │ │ ├── page.tsx # Home
│ │ │ │ ├── how-it-works/
│ │ │ │ │ └── page.tsx
│ │ │ │ ├── faq/
│ │ │ │ │ └── page.tsx
│ │ │ │ ├── contact/
│ │ │ │ │ └── page.tsx
│ │ │ │ ├── terms/
│ │ │ │ │ └── page.tsx
│ │ │ │ └── privacy/
│ │ │ │ └── page.tsx
│ │ │ ├── (buyer)/ # Buyer dashboard (optional MVP)
│ │ │ │ ├── layout.tsx # Auth required
│ │ │ │ ├── requests/
│ │ │ │ │ ├── page.tsx # My requests
│ │ │ │ │ ├── new/
│ │ │ │ │ │ └── page.tsx # Create request
│ │ │ │ │ └── [id]/
│ │ │ │ │ └── page.tsx # Request detail
│ │ │ │ └── offers/
│ │ │ │ └── page.tsx # My offers
│ │ │ └── (admin)/ # Admin console
│ │ │ ├── layout.tsx # Admin auth required
│ │ │ ├── dashboard/
│ │ │ │ └── page.tsx # Admin dashboard
│ │ │ ├── sellers/
│ │ │ │ └── page.tsx # Seller approvals
│ │ │ └── reports/
│ │ │ └── page.tsx # Report moderation
│ │ ├── api/ # API routes (if needed)
│ │ │ └── auth/
│ │ │ └── [...nextauth]/
│ │ │ └── route.ts
│ │ ├── layout.tsx # Root layout
│ │ └── globals.css # Global styles
│ ├── components/
│ │ ├── ui/ # shadcn/ui components
│ │ │ ├── button.tsx
│ │ │ ├── card.tsx
│ │ │ ├── dialog.tsx
│ │ │ ├── form.tsx
│ │ │ ├── input.tsx
│ │ │ └── ...
│ │ ├── marketing/ # Marketing components
│ │ │ ├── hero.tsx
│ │ │ ├── features.tsx
│ │ │ ├── how-it-works.tsx
│ │ │ ├── testimonials.tsx
│ │ │ ├── cta.tsx
│ │ │ └── footer.tsx
│ │ ├── buyer/ # Buyer dashboard components
│ │ │ ├── request-card.tsx
│ │ │ ├── request-form.tsx
│ │ │ ├── offer-list.tsx
│ │ │ └── offer-card.tsx
│ │ ├── admin/ # Admin components
│ │ │ ├── seller-approval-card.tsx
│ │ │ ├── report-card.tsx
│ │ │ └── metrics-widget.tsx
│ │ └── shared/ # Shared components
│ │ ├── header.tsx
│ │ ├── nav.tsx
│ │ ├── language-switcher.tsx
│ │ └── empty-state.tsx
│ ├── lib/
│ │ ├── api.ts # API client
│ │ ├── auth.ts # Auth helpers
│ │ ├── utils.ts # Utility functions
│ │ └── validations.ts # Zod schemas
│ ├── hooks/
│ │ ├── use-requests.ts # React Query hooks
│ │ ├── use-offers.ts
│ │ └── use-auth.ts
│ ├── stores/
│ │ └── auth-store.ts # Zustand stores
│ └── types/
│ └── index.ts # TypeScript types
├── public/
│ ├── images/
│ ├── icons/
│ └── locales/ # i18n JSON files (if not using next-intl)
├── messages/ # next-intl translations
│ ├── en.json
│ └── ar.json
├── .env.local.example
├── next.config.js
├── tailwind.config.ts
├── tsconfig.json
└── package.json

Design System

Color Palette (Premium & Trustworthy)

/* Tailwind config */
colors: {
primary: {
50: '#f0f9ff',
100: '#e0f2fe',
500: '#0ea5e9', // Main brand color
600: '#0284c7',
700: '#0369a1',
},
success: {
500: '#10b981',
},
warning: {
500: '#f59e0b',
},
error: {
500: '#ef4444',
},
neutral: {
50: '#fafafa',
100: '#f5f5f5',
200: '#e5e5e5',
500: '#737373',
900: '#171717',
}
}

Typography

/* Arabic-first typography */
font-family: {
sans: ['Cairo', 'system-ui', 'sans-serif'], // Arabic-friendly
en: ['Inter', 'system-ui', 'sans-serif'], // English fallback
}

font-size: {
xs: '0.75rem',
sm: '0.875rem',
base: '1rem',
lg: '1.125rem',
xl: '1.25rem',
'2xl': '1.5rem',
'3xl': '1.875rem',
'4xl': '2.25rem',
}

RTL Support

// app/[locale]/layout.tsx
export default function LocaleLayout({
children,
params: { locale }
}: {
children: React.ReactNode;
params: { locale: string };
}) {
const dir = locale === 'ar' ? 'rtl' : 'ltr';

return (
<html lang={locale} dir={dir}>
<body className={cn(
locale === 'ar' ? 'font-sans' : 'font-en'
)}>
{children}
</body>
</html>
);
}

Page Wireframes

1. Marketing Home Page

Sections:

┌─────────────────────────────────────┐
│ Header (Logo, Nav, Language, CTA) │
├─────────────────────────────────────┤
│ Hero Section │
│ - Headline: "اطلب ما تريد، البائعون│
│ يتنافسون على عرضك" │
│ - Subheadline │
│ - CTA: "ابدأ الآن" (Download app) │
│ - Hero image/illustration │
├─────────────────────────────────────┤
│ How It Works (3 steps) │
│ 1. Post request (30 sec) │
│ 2. Receive competing offers │
│ 3. Choose best deal │
├─────────────────────────────────────┤
│ Key Features │
│ - Sellers compete for you │
│ - Transparent pricing │
│ - Trusted sellers only │
│ - Fast responses │
├─────────────────────────────────────┤
│ Trust Signals │
│ - Seller verification │
│ - Rating system │
│ - Report system │
├─────────────────────────────────────┤
│ CTA Section │
│ "Download the app and post your │
│ first request in 30 seconds" │
│ [App Store] [Google Play] │
├─────────────────────────────────────┤
│ Footer │
│ - Links (About, FAQ, Contact) │
│ - Social media │
│ - Copyright │
└─────────────────────────────────────┘

2. How It Works Page

Content:

  • Detailed explanation of buyer flow
  • Detailed explanation of seller flow
  • Screenshots/mockups of mobile apps
  • FAQ section
  • CTA to download app

3. FAQ Page

Categories:

  • For Buyers
  • For Sellers
  • Trust & Safety
  • Technical Support

4. Buyer Dashboard (Optional MVP)

My Requests Page:

┌─────────────────────────────────────┐
│ Header (Logo, Nav, Profile) │
├─────────────────────────────────────┤
│ [+ Post New Request] │
├─────────────────────────────────────┤
│ Active Requests (3) │
│ ┌─────────────────────────────────┐ │
│ │ iPhone 13 - 8K-10K EGP │ │
│ │ 5 offers • Posted 2h ago │ │
│ │ [View Offers] │ │
│ └─────────────────────────────────┘ │
├─────────────────────────────────────┤
│ Closed Requests (12) │
│ ... │
└─────────────────────────────────────┘

5. Admin Dashboard

Dashboard Page:

┌─────────────────────────────────────┐
│ Admin Header │
├─────────────────────────────────────┤
│ Metrics Row │
│ [Active Requests: 45] │
│ [Pending Sellers: 8] │
│ [Pending Reports: 3] │
│ [Deals Today: 12] │
├─────────────────────────────────────┤
│ Pending Seller Approvals │
│ ┌─────────────────────────────────┐ │
│ │ Ahmed's Electronics │ │
│ │ Nasr City • Applied 2h ago │ │
│ │ [Approve] [Reject] [View] │ │
│ └─────────────────────────────────┘ │
├─────────────────────────────────────┤
│ Recent Reports │
│ ... │
└─────────────────────────────────────┘

Key Components

RequestCard Component

// components/buyer/request-card.tsx
interface RequestCardProps {
request: {
id: string;
product_brand: string;
product_model: string;
budget_max: number;
offer_count: number;
created_at: string;
status: 'active' | 'closed';
};
}

export function RequestCard({ request }: RequestCardProps) {
const t = useTranslations('requests');

return (
<Card>
<CardHeader>
<CardTitle>{request.product_brand} {request.product_model}</CardTitle>
<CardDescription>
{t('budget_up_to', { amount: request.budget_max })}
</CardDescription>
</CardHeader>
<CardContent>
<div className="flex items-center gap-2">
<Badge variant={request.status === 'active' ? 'success' : 'secondary'}>
{t(`status.${request.status}`)}
</Badge>
<span className="text-sm text-muted-foreground">
{request.offer_count} {t('offers')}
</span>
</div>
</CardContent>
<CardFooter>
<Button asChild>
<Link href={`/requests/${request.id}`}>
{t('view_offers')}
</Link>
</Button>
</CardFooter>
</Card>
);
}

LanguageSwitcher Component

// components/shared/language-switcher.tsx
export function LanguageSwitcher() {
const router = useRouter();
const pathname = usePathname();
const locale = useLocale();

const switchLanguage = (newLocale: string) => {
const newPathname = pathname.replace(`/${locale}`, `/${newLocale}`);
router.push(newPathname);
};

return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" size="sm">
<Globe className="h-4 w-4 me-2" />
{locale === 'ar' ? 'العربية' : 'English'}
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent>
<DropdownMenuItem onClick={() => switchLanguage('ar')}>
العربية
</DropdownMenuItem>
<DropdownMenuItem onClick={() => switchLanguage('en')}>
English
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
}

API Integration

API Client Setup

// lib/api.ts
import axios from 'axios';

const api = axios.create({
baseURL: process.env.NEXT_PUBLIC_API_URL,
headers: {
'Content-Type': 'application/json',
},
});

// Request interceptor for auth token
api.interceptors.request.use((config) => {
const token = localStorage.getItem('access_token');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});

// Response interceptor for error handling
api.interceptors.response.use(
(response) => response,
async (error) => {
if (error.response?.status === 401) {
// Handle token refresh or redirect to login
}
return Promise.reject(error);
}
);

export default api;

React Query Hooks

// hooks/use-requests.ts
import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query';
import api from '@/lib/api';

export function useRequests() {
return useQuery({
queryKey: ['requests'],
queryFn: async () => {
const { data } = await api.get('/requests/my');
return data.data.requests;
},
});
}

export function useCreateRequest() {
const queryClient = useQueryClient();

return useMutation({
mutationFn: async (requestData: CreateRequestInput) => {
const { data } = await api.post('/requests', requestData);
return data.data;
},
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['requests'] });
},
});
}

Internationalization

Translation Files

// messages/ar.json
{
"common": {
"app_name": "طلبينو",
"get_started": "ابدأ الآن",
"download_app": "حمل التطبيق"
},
"home": {
"hero_title": "اطلب ما تريد، البائعون يتنافسون على عرضك",
"hero_subtitle": "منصة عكسية حيث تنشر طلبك ويتنافس البائعون لتقديم أفضل عرض",
"how_it_works": "كيف يعمل",
"step1_title": "انشر طلبك",
"step1_desc": "حدد المنتج والميزانية في 30 ثانية",
"step2_title": "استقبل العروض",
"step2_desc": "البائعون يتنافسون بأفضل الأسعار",
"step3_title": "اختر الأفضل",
"step3_desc": "قارن واختر العرض المناسب"
},
"requests": {
"my_requests": "طلباتي",
"post_request": "انشر طلب جديد",
"budget_up_to": "حتى {amount} جنيه",
"offers": "عروض",
"view_offers": "عرض العروض",
"status": {
"active": "نشط",
"closed": "مغلق"
}
}
}
// messages/en.json
{
"common": {
"app_name": "Talbino",
"get_started": "Get Started",
"download_app": "Download App"
},
"home": {
"hero_title": "Post What You Want, Sellers Compete for Your Request",
"hero_subtitle": "A reverse marketplace where you post your request and sellers compete with their best offers",
"how_it_works": "How It Works",
"step1_title": "Post Your Request",
"step1_desc": "Specify product and budget in 30 seconds",
"step2_title": "Receive Offers",
"step2_desc": "Sellers compete with best prices",
"step3_title": "Choose the Best",
"step3_desc": "Compare and select the right offer"
},
"requests": {
"my_requests": "My Requests",
"post_request": "Post New Request",
"budget_up_to": "Up to EGP {amount}",
"offers": "offers",
"view_offers": "View Offers",
"status": {
"active": "Active",
"closed": "Closed"
}
}
}

Environment Variables

# .env.local.example
NEXT_PUBLIC_API_URL=http://localhost:8000/api/v1
NEXT_PUBLIC_WS_URL=ws://localhost:8000/ws
NEXT_PUBLIC_APP_URL=http://localhost:3000

# NextAuth (if using)
NEXTAUTH_URL=http://localhost:3000
NEXTAUTH_SECRET=your-secret-here

# Analytics (optional)
NEXT_PUBLIC_GA_ID=G-XXXXXXXXXX

Development Workflow

Setup

# Install dependencies
npm install

# Run development server
npm run dev

# Build for production
npm run build

# Start production server
npm start

# Lint
npm run lint

# Type check
npm run type-check

Key Scripts

{
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint",
"type-check": "tsc --noEmit",
"format": "prettier --write \"src/**/*.{ts,tsx}\"",
"test": "jest",
"test:watch": "jest --watch"
}
}

Deployment

Vercel Deployment

  1. Connect GitHub repository to Vercel
  2. Configure environment variables
  3. Deploy automatically on push to main

Build Configuration

// next.config.js
const withNextIntl = require('next-intl/plugin')();

module.exports = withNextIntl({
reactStrictMode: true,
images: {
domains: ['s3.amazonaws.com'],
},
i18n: {
locales: ['ar', 'en'],
defaultLocale: 'ar',
},
});

Performance Optimization

  • Image Optimization: Use Next.js Image component
  • Code Splitting: Automatic with App Router
  • Static Generation: Use SSG for marketing pages
  • CDN: Vercel Edge Network
  • Caching: React Query for API responses

Accessibility

  • ARIA labels: All interactive elements
  • Keyboard navigation: Full support
  • Screen reader: Semantic HTML
  • Color contrast: WCAG AA compliant
  • RTL support: Full bidirectional text support

MVP Scope

Phase 1 (Week 1-2):

  • Marketing pages (Home, How It Works, FAQ)
  • Basic styling and responsive design
  • i18n setup (ar/en)
  • RTL support

Phase 2 (Week 3-4):

  • Admin dashboard (seller approvals, reports)
  • Authentication integration
  • API integration

Optional (Post-MVP):

  • Buyer web dashboard
  • Advanced analytics
  • SEO optimization