A modern, responsive redesign of the We Want Waste skip selection interface with enhanced UX/UI, performance optimizations, and professional development practices.
This project demonstrates a scalable, production-ready React application built with modern tools and best practices for enterprise-level development.
remwaste/
├── apps/
│ └── core/ # Main application
packages (shadcn/ui)
│ ├── ui/ # Shared UI components (shadcn/ui)
Why Nx?
- Scalable Architecture: Designed for real-world applications with multiple portals (admin, client, etc.)
- Code Sharing: Reusable UI components and business logic across different applications
- Dependency Management: Clear boundaries between packages with proper dependency graphs
- Future-Proof: Easy to add new applications (admin portal, mobile app) to the same workspace
- Consistent Design Language: All components follow a unified design system
- Accessibility First: Built-in ARIA support and keyboard navigation
- Customizable: Easy theming and component variants
- Documentation Ready: Prepared for Storybook integration for component testing and documentation
- Enhanced Visual Hierarchy: Clear pricing display with VAT breakdown
- Intuitive Restrictions: Visual indicators for skip limitations (road placement, heavy waste)
- Mobile-First Responsive: Optimized layouts for all device sizes
- Loading States: Skeleton loaders and image placeholders for better perceived performance
🚀 CI/CD Pipeline (Cloudflare Pages + GitHub Actions) Automated Deployment The project uses GitHub Actions for CI/CD with Cloudflare Pages for hosting, providing fast global CDN distribution and automatic deployments.
- Intelligent Caching: Reduces API calls and improves performance
- Background Updates: Keeps data fresh without blocking UI
- Error Handling: Built-in retry logic and error boundaries
- Loading States: Automatic loading and error state management
// Lightweight, performant state management
export const useSkipsStore = create<SkipsStore>((set) => ({
selectedSkip: null,
selectSkip: (skip) => set(() => ({ selectedSkip: skip })),
clearSelection: () => set(() => ({ selectedSkip: null })),
}));
// Performance-optimized selectors
export const useSelectedSkip = () => useSkipsStore((state) => state.selectedSkip);
export const useSelectedSkipId = () => useSkipsStore((state) => state.selectedSkip?.id);
Why Zustand over Context?
- Better Performance: Selective subscriptions prevent unnecessary re-renders
- Simpler API: Less boilerplate than Context + useReducer
- DevTools Support: Easy debugging and state inspection
- TypeScript First: Excellent TypeScript integration
- Lazy Loading: Images load only when needed
- Placeholder States: Smooth loading experience with fallbacks
- Error Handling: Graceful fallbacks when images fail to load
- Responsive Images: Optimized sizing for different screen sizes
Benefits:
- Consistent Code Quality: Automated linting and formatting
- Prevent Bad Commits: Catch issues before they reach the repository
- Team Collaboration: Enforced coding standards across the team
Advantages:
- Clear Boundaries: Components, hooks, and services scoped to their respective views
- Easy Maintenance: Related code is co-located and easy to find
- Reusability: Clear separation between view-specific and reusable code
- Team Scalability: Multiple developers can work on different views without conflicts
- Real-time Price Calculation: Live VAT calculation and price breakdown
- Responsive Design: Seamless experience across mobile, tablet, and desktop
- Progressive Enhancement: Basic functionality works, enhanced features improve the experience
- Skeleton Components: Maintain layout during loading
- Progressive Loading: Images and content load independently
- User Feedback: Clear loading indicators and progress states
- Touch-Friendly Interfaces: Larger touch targets and proper spacing
- Optimized Layouts: Different layouts for mobile vs desktop
- Performance: Reduced bundle size and faster loading on mobile networks
- Accessibility: Proper contrast ratios and text sizing for mobile devices
// Type-safe API calls with proper error handling
export const skipService = {
getSkipsByLocation: async (params: GetSkipsByLocationParams): Promise<GetSkipsResponse> => {
try {
const response = await apiClient.get('/skips/by-location', { params });
return transformSkips(response.data);
} catch (error) {
throw new ApiError(error);
}
},
};
- Faster Decision Making: Clear pricing and restriction information
- Reduced Confusion: Intuitive interface with helpful guidance
- Mobile Accessibility: Better experience for mobile users
- Trust Building: Professional design increases user confidence
- Maintainable Code: Clean architecture and consistent patterns
- Performance: Optimized loading and smooth interactions
- Scalable: Ready for additional features and applications
- Developer Experience: Modern tooling and clear development patterns
- Node.js 18+
- pnpm
- Git
# Clone the repository
git clone <repository-url>
cd remwaste
# Install dependencies
pnpm install
# Start development
pnpm run dev
# Build for production
pnpm run build
The project uses GitHub Actions for CI/CD with Cloudflare Pages for hosting, providing fast global CDN distribution and automatic deployments.
- Zero Downtime: Instant deployments with rollback capability
- Global CDN: Fast loading worldwide
- Preview URLs: Test changes before merging
- Build Optimization: Automatic asset optimization and caching
- Storybook Integration: Component documentation and testing
- E2E Testing: Cypress or Playwright integration
- PWA Features: Offline support and app-like experience
- Analytics: User behavior tracking and optimization
- A/B Testing: Framework for testing design variations
- Admin Portal: Easily add admin interface using shared components
- Mobile App: React Native integration using shared business logic
- Multi-tenant: Support for different brands/regions
- Internationalization: Multi-language support