← Back to work

AEW-AEC Conference Platform.

A full-stack event management system for the 11th UNESCO Africa Engineering Week and 9th African Engineering Conference 2025, covering multilingual content, registration workflows and payments.

Next.js 14TypeScriptNestJSFirebasePesaPal
View live site ↗
Screenshot of the AEW-AEC Conference Platform
1000+
Registrations
90%
Faster admin loads
3
Languages
5
Payment methods

Key features

Multilingual

English, French and Arabic, including RTL layout.

Payments

PesaPal gateway with multiple methods and real-time verification.

Registration

Multi-step flow with duplicate prevention and email validation.

Admin dashboard

Registration management, analytics and bulk operations.

Performance

Lazy loading and pagination for a 90% load-time improvement.

Email automation

Ticket generation, reminders and edit links.

Stack

Frontend

  • Next.js 14
  • TypeScript
  • Tailwind CSS
  • next-intl
  • React Hook Form

Backend

  • NestJS
  • Firebase
  • PesaPal API
  • Nodemailer
  • Vercel

Challenges & solutions

Duplicate email prevention

Block duplicate registrations while letting people finish incomplete ones.

Solution — Backend validation, real-time email verification and completion-status detection.

Payment callback handling

Reliable verification and registration completion across payment methods.

Solution — A callback system with fallbacks, proper error handling and automated ticket generation.

Performance at scale

The admin dashboard slowed down with thousands of registrations and tokens.

Solution — Pagination, lazy loading and batch operations, for a 90% improvement.

Technical highlights

Multilingual middleware

// middleware.ts
export function middleware(request: NextRequest) {
  // Handle internationalization
  const response = intlMiddleware(request);

  // Add cache control headers
  response.headers.set(
    'Cache-Control',
    'public, max-age=3600, s-maxage=86400'
  );

  return response;
}

Duplicate email check

// registration.service.ts
async checkDuplicateEmail(email: string) {
  const existing = await this.firestore
    .collection('registrations')
    .where('email', '==', email)
    .get();

  const completed = existing.docs.some(doc => {
    const data = doc.data();
    return data.paymentStatus === 'completed' ||
           data.registrationDate ||
           data.ticketSent;
  });

  if (completed) {
    throw new BadRequestException(
      'Email already registered'
    );
  }
}

Need something similar?

Full-stack development, payment integration, internationalization and performance work. Let's talk about your project.

Get in touch