| .. | ||
| app | ||
| e2e | ||
| lib | ||
| src | ||
| .dockerignore | ||
| .env.example | ||
| .eslintrc.json | ||
| .gitignore | ||
| Dockerfile | ||
| middleware.ts | ||
| next.config.js | ||
| package-lock.json | ||
| package.json | ||
| playwright.config.ts | ||
| postcss.config.js | ||
| README.md | ||
| tailwind.config.ts | ||
| tsconfig.json | ||
Xpeditis Frontend
Next.js 14-based frontend for the Xpeditis maritime freight booking platform.
🏗️ Tech Stack
- Framework: Next.js 14 (App Router)
- Language: TypeScript 5
- Styling: Tailwind CSS + shadcn/ui
- State Management: TanStack Query (React Query)
- Forms: react-hook-form + zod
- HTTP Client: axios
- Icons: lucide-react
- Testing: Jest + React Testing Library + Playwright
🚀 Quick Start
Install Dependencies
npm install
Setup Environment
cp .env.example .env
Default values work for local development!
Start Development Server
npm run dev
Open: http://localhost:3000
📝 Available Scripts
Development
npm run dev- Start development server (port 3000)npm run build- Build for productionnpm run start- Start production servernpm run lint- Lint codenpm test- Run testsnpm run test:watch- Run tests in watch modenpm run test:e2e- Run Playwright E2E tests
📁 Project Structure
app/
├── layout.tsx # Root layout
├── page.tsx # Home page
├── globals.css # Global styles
├── (routes)/ # Route groups
│ ├── search/ # Rate search
│ ├── bookings/ # Booking management
│ ├── dashboard/ # User dashboard
│ └── auth/ # Authentication
└── api/ # API routes (if needed)
components/
├── ui/ # shadcn/ui components
├── search/ # Search-specific components
├── bookings/ # Booking-specific components
└── shared/ # Shared components
lib/
├── api/ # API client & hooks
├── hooks/ # Custom React hooks
├── types/ # TypeScript types
├── utils.ts # Utility functions
└── validators/ # Zod schemas
🎨 Styling
Tailwind CSS
Using Tailwind CSS with custom theme configuration.
CSS Variables (defined in globals.css):
--background,--foreground--primary,--secondary--muted,--accent--destructive,--border- etc.
shadcn/ui Components
Pre-built accessible components:
- Buttons, Cards, Dialogs
- Forms, Inputs, Selects
- Tables, Tabs, Dropdowns
- And more...
Adding components:
npx shadcn-ui@latest add button
npx shadcn-ui@latest add card
# etc.
🔌 API Integration
API Client Setup
// lib/api/client.ts
import axios from 'axios';
const apiClient = axios.create({
baseURL: process.env.NEXT_PUBLIC_API_URL || 'http://localhost:4000',
timeout: 10000,
headers: {
'Content-Type': 'application/json',
},
});
export default apiClient;
React Query Hooks
// lib/api/rates.ts
import { useQuery } from '@tanstack/react-query';
import apiClient from './client';
export function useSearchRates(params: RateSearchParams) {
return useQuery({
queryKey: ['rates', params],
queryFn: async () => {
const { data } = await apiClient.post('/api/v1/rates/search', params);
return data;
},
});
}
Usage in Components
// components/search/SearchResults.tsx
import { useSearchRates } from '@/lib/api/rates';
export function SearchResults({ params }: Props) {
const { data, isLoading, error } = useSearchRates(params);
if (isLoading) return <LoadingSkeleton />;
if (error) return <ErrorMessage error={error} />;
return <RatesList rates={data} />;
}
📋 Forms
Using react-hook-form + zod for type-safe form validation.
Example:
// lib/validators/search.ts
import { z } from 'zod';
export const searchSchema = z.object({
origin: z.string().min(3, 'Invalid port code'),
destination: z.string().min(3, 'Invalid port code'),
containerType: z.enum(['20DRY', '40DRY', '40HC']),
departureDate: z.string().datetime(),
});
export type SearchFormData = z.infer<typeof searchSchema>;
// components/search/SearchForm.tsx
import { useForm } from 'react-hook-form';
import { zodResolver } from '@hookform/resolvers/zod';
import { searchSchema, SearchFormData } from '@/lib/validators/search';
export function SearchForm() {
const { register, handleSubmit, formState: { errors } } = useForm<SearchFormData>({
resolver: zodResolver(searchSchema),
});
const onSubmit = (data: SearchFormData) => {
// Handle submission
};
return (
<form onSubmit={handleSubmit(onSubmit)}>
{/* Form fields */}
</form>
);
}
🧪 Testing
Unit Tests (Jest + React Testing Library)
npm test
Example:
// components/ui/Button.test.tsx
import { render, screen } from '@testing-library/react';
import { Button } from './Button';
describe('Button', () => {
it('renders children', () => {
render(<Button>Click me</Button>);
expect(screen.getByText('Click me')).toBeInTheDocument();
});
});
E2E Tests (Playwright)
npm run test:e2e
Example:
// e2e/search.spec.ts
import { test, expect } from '@playwright/test';
test('search for rates', async ({ page }) => {
await page.goto('/search');
await page.fill('[name="origin"]', 'NLRTM');
await page.fill('[name="destination"]', 'CNSHA');
await page.click('button[type="submit"]');
await expect(page.locator('.rate-card')).toBeVisible();
});
🔒 Authentication
Authentication will be handled with:
- JWT tokens from backend
- NextAuth.js (optional)
- Protected routes with middleware
Example (to be implemented):
// middleware.ts
export { default } from 'next-auth/middleware';
export const config = {
matcher: ['/dashboard/:path*', '/bookings/:path*'],
};
📱 Responsive Design
Mobile-first approach with Tailwind breakpoints:
<div className="
flex flex-col // Mobile: column
md:flex-row // Tablet: row
lg:gap-8 // Desktop: larger gap
xl:max-w-7xl // Extra large: max width
">
{/* Content */}
</div>
Breakpoints:
sm: 640pxmd: 768pxlg: 1024pxxl: 1280px2xl: 1536px
🎯 Performance
- Code splitting: Automatic with Next.js App Router
- Image optimization: Using Next.js
<Image>component - React Query caching: Automatic caching of API responses
- Bundle analysis:
npm run buildshows bundle sizes
🌐 Environment Variables
# API
NEXT_PUBLIC_API_URL=http://localhost:4000
NEXT_PUBLIC_API_PREFIX=api/v1
# Auth (NextAuth)
NEXTAUTH_URL=http://localhost:3000
NEXTAUTH_SECRET=your-secret
# OAuth
GOOGLE_CLIENT_ID=
GOOGLE_CLIENT_SECRET=
Note: Variables prefixed with NEXT_PUBLIC_ are exposed to the browser.
📖 Further Reading
🤝 Contributing
- Follow React/Next.js best practices
- Use TypeScript strict mode
- Write tests for components
- Ensure accessibility (WCAG 2.1 AA)
- Format with Prettier
- Lint with ESLint
📝 License
Proprietary - All rights reserved
Built with ❤️ using Next.js 14 and React 18