DEV Community

Nadim Chowdhury
Nadim Chowdhury

Posted on • Edited on

How to create full-stack Tech reviews, specifications, blog website using Next JS & Nest JS?

To create an effective frontend for showcasing mobile specifications, reviews, and related content, the website should have several well-designed pages and features. Here’s a breakdown of the key pages and the detailed information they should include:

1. Homepage

The homepage serves as the gateway to your website, providing a snapshot of the most important content and easy access to various sections.

Features and Sections:

  • Hero Section: A dynamic banner showcasing featured products, new releases, or trending articles, with a compelling CTA (Call to Action) button.
  • Search Bar: Prominently placed search bar allowing users to quickly find specific products or articles.
  • Featured Products: A section highlighting the latest or top-rated mobile phones with images, brief descriptions, and quick links to detailed pages.
  • Latest Reviews: Display recent reviews with summaries, star ratings, and links to the full reviews.
  • Popular Categories: Quick links to different product categories such as mobile phones, laptops, cameras, etc.
  • Trending Articles: A carousel or grid of the most popular articles or news pieces related to technology.
  • Subscription CTA: An invitation to subscribe to a newsletter or updates.
  • Footer: Includes quick links, contact information, social media links, and legal disclaimers.

2. Product Listing Page

This page displays all the products in a specific category (e.g., all mobile phones) and provides filtering and sorting options.

Features and Sections:

  • Category Header: The name of the category, a brief description, and possibly a banner image.
  • Filters and Sorting Options: Allow users to filter products by brand, price range, specifications (RAM, storage, etc.), ratings, and sort by relevance, price, or popularity.
  • Product Grid/List: Displays products in a grid or list format, showing product images, names, key specifications (like RAM, storage, battery life), and prices.
  • Pagination: Navigational links for accessing additional pages of products.
  • Sidebar (Optional): Additional filtering options, promotional content, or links to related categories.

3. Product Detail Page

The product detail page is crucial as it provides comprehensive information about a specific mobile phone or other tech products.

Features and Sections:

  • Product Header: Product name, brand, model, release date, and average user rating.
  • Product Gallery: A carousel of high-quality images and possibly videos showing the product from different angles.
  • Key Specifications: A concise overview of essential specs like processor, RAM, storage, display, battery life, and camera.
  • Detailed Specifications: A full table or accordion-style list breaking down all specifications, including technical details like chipset, GPU, OS version, dimensions, weight, etc.
  • User Reviews and Ratings: Section showing user reviews, ratings, and a form for submitting new reviews.
  • Comparison Tool: A button or link to compare this product with others (if a comparison tool is available).
  • Related Products: Recommendations for similar or complementary products.
  • Buy Now/CTA: Links to purchase options or third-party retailers.

4. Review Page

A dedicated page for an in-depth review of a specific product.

Features and Sections:

  • Review Header: Product image, name, and star rating.
  • Reviewer Details: Information about the reviewer, date of review, and review summary.
  • Review Content: Comprehensive review text broken into sections like design, performance, battery life, camera quality, etc., with supporting images and videos.
  • Pros and Cons: A summary of the product's strengths and weaknesses.
  • User Comments: Section for user comments and discussions about the review.
  • Rating Breakdown: A visual representation of the rating distribution (e.g., bar chart showing how many users gave 5 stars, 4 stars, etc.).

5. Blog and Article Page

For displaying individual blog posts or articles.

Features and Sections:

  • Article Header: Title, author name, publication date, and category tags.
  • Article Content: Rich text content with images, videos, and embedded media.
  • Social Sharing Buttons: Buttons for sharing the article on social media platforms.
  • Author Bio: Brief biography of the author with a link to other articles they have written.
  • Comments Section: Area for readers to leave comments and discuss the article.
  • Related Articles: Links to similar or related articles to keep readers engaged.

6. Comparison Page

Allows users to compare multiple products side-by-side.

Features and Sections:

  • Comparison Table: Displays selected products with rows for different specifications like price, display, processor, camera, battery, etc., allowing users to easily compare features.
  • Add/Remove Products: Functionality to add or remove products from the comparison table.
  • Quick Summary: A brief summary highlighting the key differences and similarities between the selected products.
  • CTA: Links to individual product detail pages for more information.

7. Search Results Page

Displays the results for user search queries.

Features and Sections:

  • Search Query Display: Shows what the user searched for and the number of results found.
  • Filters and Sorting Options: Similar to the product listing page, allowing users to refine their search results.
  • Search Results List/Grid: Displays products, reviews, articles, etc., that match the search query with brief descriptions and links to detailed pages.
  • Pagination: For navigating through multiple pages of search results.

8. About Us Page

Provides information about the website, its mission, and the team behind it.

Features and Sections:

  • Mission Statement: A brief description of the website's purpose and goals.
  • Team Profiles: Photos and bios of the team members or contributors.
  • History: Background information on how the website was started and its evolution.
  • Contact Information: Email addresses, social media links, and possibly a contact form for inquiries.

9. Contact Us Page

Allows users to get in touch with the website team.

Features and Sections:

  • Contact Form: Fields for name, email, subject, and message, with a submit button.
  • Contact Information: Email addresses, phone numbers, and physical address (if applicable).
  • Social Media Links: Icons and links to the website's social media profiles.

10. User Profile Page

For registered users to manage their account and view their activity.

Features and Sections:

  • Profile Information: Display and edit user information like name, email, and password.
  • Activity Log: A list of the user’s reviews, comments, and articles (if they contribute content).
  • Saved Products/Articles: Section for saved or bookmarked products and articles for easy access later.

11. FAQ Page

A dedicated page to answer frequently asked questions.

Features and Sections:

  • Accordion Style FAQ: Common questions with expandable answers.
  • Search Bar: A search function to quickly find specific questions or topics.

General Features Across All Pages

  • Responsive Design: Ensure all pages are optimized for various screen sizes, including desktops, tablets, and smartphones.
  • SEO Optimization: Implement SEO best practices like meta tags, structured data, and optimized images for better search engine rankings.
  • Accessibility: Follow web accessibility guidelines to ensure the site is usable by people with disabilities.
  • Performance Optimization: Use lazy loading for images, optimize scripts, and leverage caching to ensure fast page loads.

By implementing these features and organizing content across these pages, you'll create a comprehensive and user-friendly website for displaying mobile specifications, reviews, and related tech content.

(Backend)

To create a backend using NestJS for a website showcasing mobile specifications, reviews, and related content, we will define several modules, controllers, and services. This backend will serve the necessary data to the frontend, allowing it to dynamically populate the homepage and other sections.

Here's a basic structure of the NestJS backend for the homepage:

1. Setup NestJS Project

First, initialize a NestJS project if you haven't already:

npm i -g @nestjs/cli
nest new mobile-specs-backend
cd mobile-specs-backend
Enter fullscreen mode Exit fullscreen mode

2. Install Required Packages

Install any necessary packages such as TypeORM for database integration, and the necessary database driver:

npm install --save @nestjs/typeorm typeorm mysql2
Enter fullscreen mode Exit fullscreen mode

Replace mysql2 with the appropriate package if you're using a different database (e.g., pg for PostgreSQL).

3. Configure Database Connection

Edit the src/app.module.ts to set up the database connection:

import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { HomePageModule } from './home-page/home-page.module'; // Import HomePageModule

@Module({
  imports: [
    TypeOrmModule.forRoot({
      type: 'mysql', // Use your preferred database type
      host: 'localhost',
      port: 3306,
      username: 'root',
      password: 'password',
      database: 'mobile_specs',
      autoLoadEntities: true,
      synchronize: true,
    }),
    HomePageModule, // Register HomePageModule
  ],
})
export class AppModule {}
Enter fullscreen mode Exit fullscreen mode

4. Create HomePage Module, Controller, and Service

Generate the homepage module, controller, and service:

nest generate module home-page
nest generate controller home-page
nest generate service home-page
Enter fullscreen mode Exit fullscreen mode

5. Define Entity Models

Create entity models for products, reviews, categories, and articles. These models represent the tables in your database.

For example, create a file src/home-page/entities/product.entity.ts:

import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';

@Entity()
export class Product {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  name: string;

  @Column()
  brand: string;

  @Column()
  model: string;

  @Column()
  releaseDate: Date;

  @Column()
  averageRating: number;

  @Column()
  imageUrl: string;

  @Column()
  description: string;

  @Column()
  price: number;

  // Add more fields as needed
}
Enter fullscreen mode Exit fullscreen mode

Similarly, define other entities like Review, Category, and Article.

6. Update HomePage Module

Update src/home-page/home-page.module.ts to import the necessary entities and set up the service:

import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { HomePageController } from './home-page.controller';
import { HomePageService } from './home-page.service';
import { Product } from './entities/product.entity';
import { Review } from './entities/review.entity';
import { Category } from './entities/category.entity';
import { Article } from './entities/article.entity';

@Module({
  imports: [TypeOrmModule.forFeature([Product, Review, Category, Article])],
  controllers: [HomePageController],
  providers: [HomePageService],
})
export class HomePageModule {}
Enter fullscreen mode Exit fullscreen mode

7. Implement HomePage Service

The service will handle the logic for fetching data. Update src/home-page/home-page.service.ts:

import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Product } from './entities/product.entity';
import { Review } from './entities/review.entity';
import { Category } from './entities/category.entity';
import { Article } from './entities/article.entity';

@Injectable()
export class HomePageService {
  constructor(
    @InjectRepository(Product)
    private productsRepository: Repository<Product>,
    @InjectRepository(Review)
    private reviewsRepository: Repository<Review>,
    @InjectRepository(Category)
    private categoriesRepository: Repository<Category>,
    @InjectRepository(Article)
    private articlesRepository: Repository<Article>,
  ) {}

  async getFeaturedProducts(): Promise<Product[]> {
    return await this.productsRepository.find({
      take: 5, // Example: fetch top 5 featured products
      order: { averageRating: 'DESC' },
    });
  }

  async getLatestReviews(): Promise<Review[]> {
    return await this.reviewsRepository.find({
      take: 5, // Example: fetch latest 5 reviews
      order: { createdAt: 'DESC' },
    });
  }

  async getPopularCategories(): Promise<Category[]> {
    return await this.categoriesRepository.find();
  }

  async getTrendingArticles(): Promise<Article[]> {
    return await this.articlesRepository.find({
      take: 5, // Example: fetch top 5 trending articles
      order: { views: 'DESC' },
    });
  }
}
Enter fullscreen mode Exit fullscreen mode

8. Implement HomePage Controller

Update src/home-page/home-page.controller.ts to define the API endpoints:

import { Controller, Get } from '@nestjs/common';
import { HomePageService } from './home-page.service';
import { Product } from './entities/product.entity';
import { Review } from './entities/review.entity';
import { Category } from './entities/category.entity';
import { Article } from './entities/article.entity';

@Controller('home-page')
export class HomePageController {
  constructor(private readonly homePageService: HomePageService) {}

  @Get('featured-products')
  async getFeaturedProducts(): Promise<Product[]> {
    return await this.homePageService.getFeaturedProducts();
  }

  @Get('latest-reviews')
  async getLatestReviews(): Promise<Review[]> {
    return await this.homePageService.getLatestReviews();
  }

  @Get('popular-categories')
  async getPopularCategories(): Promise<Category[]> {
    return await this.homePageService.getPopularCategories();
  }

  @Get('trending-articles')
  async getTrendingArticles(): Promise<Article[]> {
    return await this.homePageService.getTrendingArticles();
  }
}
Enter fullscreen mode Exit fullscreen mode

9. Testing the API

Start your NestJS server:

npm run start
Enter fullscreen mode Exit fullscreen mode

You can test the endpoints using tools like Postman or cURL to ensure they return the expected data. For example:

  • GET http://localhost:3000/home-page/featured-products
  • GET http://localhost:3000/home-page/latest-reviews
  • GET http://localhost:3000/home-page/popular-categories
  • GET http://localhost:3000/home-page/trending-articles

10. Extend and Refine

  • Extend the Entities: Add more fields and relations between entities as needed.
  • Authentication & Authorization: If needed, integrate authentication modules (e.g., JWT).
  • Caching and Optimization: Implement caching mechanisms for frequently accessed data.
  • Unit Testing: Write unit tests for services and controllers to ensure reliability.

This setup provides a foundational backend in NestJS for the homepage of your mobile specifications website. From here, you can expand the backend to include other pages and functionality as required.

To create a backend for the Product Listing Page using NestJS, we will need to implement functionality that supports filtering, sorting, and pagination of products. This will involve creating endpoints that accept query parameters to filter and sort the data according to user preferences.

Steps to Implement Product Listing Page Backend in NestJS

  1. Define Entity Models: Ensure that your Product entity includes all relevant fields for filtering and sorting.
  2. Create Product Module, Service, and Controller: Handle the logic for fetching and returning products based on filters, sorting, and pagination.
  3. Implement Filtering, Sorting, and Pagination: Use query parameters to fetch products accordingly.

1. Update the Product Entity

Assuming you already have a Product entity, make sure it includes fields that can be used for filtering and sorting (like brand, price, ram, storage, rating, etc.).

Here’s an example of a more detailed Product entity:

import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';

@Entity()
export class Product {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  name: string;

  @Column()
  brand: string;

  @Column()
  model: string;

  @Column()
  releaseDate: Date;

  @Column()
  averageRating: number;

  @Column('decimal', { precision: 5, scale: 2 })
  price: number;

  @Column()
  ram: string;

  @Column()
  storage: string;

  @Column()
  batteryLife: string;

  @Column()
  imageUrl: string;

  @Column()
  description: string;

  // Additional fields as needed
}
Enter fullscreen mode Exit fullscreen mode

2. Create Product Module, Controller, and Service

Generate the product module, controller, and service if you haven't already:

nest generate module products
nest generate controller products
nest generate service products
Enter fullscreen mode Exit fullscreen mode

3. Update the Product Module

Edit src/products/products.module.ts to include necessary imports:

import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { ProductsController } from './products.controller';
import { ProductsService } from './products.service';
import { Product } from './entities/product.entity';

@Module({
  imports: [TypeOrmModule.forFeature([Product])],
  controllers: [ProductsController],
  providers: [ProductsService],
})
export class ProductsModule {}
Enter fullscreen mode Exit fullscreen mode

4. Implement Product Service

The service will handle the logic for fetching products with filters, sorting, and pagination.

Update src/products/products.service.ts:

import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, SelectQueryBuilder } from 'typeorm';
import { Product } from './entities/product.entity';

@Injectable()
export class ProductsService {
  constructor(
    @InjectRepository(Product)
    private productsRepository: Repository<Product>,
  ) {}

  async findAll(query: any): Promise<{ data: Product[]; total: number }> {
    const { brand, minPrice, maxPrice, ram, storage, rating, sort, page, limit } = query;

    const qb: SelectQueryBuilder<Product> = this.productsRepository.createQueryBuilder('product');

    // Filtering
    if (brand) {
      qb.andWhere('product.brand = :brand', { brand });
    }
    if (minPrice) {
      qb.andWhere('product.price >= :minPrice', { minPrice: parseFloat(minPrice) });
    }
    if (maxPrice) {
      qb.andWhere('product.price <= :maxPrice', { maxPrice: parseFloat(maxPrice) });
    }
    if (ram) {
      qb.andWhere('product.ram = :ram', { ram });
    }
    if (storage) {
      qb.andWhere('product.storage = :storage', { storage });
    }
    if (rating) {
      qb.andWhere('product.averageRating >= :rating', { rating: parseFloat(rating) });
    }

    // Sorting
    if (sort) {
      const sortFields = sort.split(',');
      sortFields.forEach((field) => {
        const [key, order] = field.split(':');
        qb.addOrderBy(`product.${key}`, order.toUpperCase() as 'ASC' | 'DESC');
      });
    }

    // Pagination
    const pageNumber = page ? parseInt(page) : 1;
    const pageSize = limit ? parseInt(limit) : 10;
    qb.skip((pageNumber - 1) * pageSize).take(pageSize);

    // Execute query and get results
    const [data, total] = await qb.getManyAndCount();
    return { data, total };
  }
}
Enter fullscreen mode Exit fullscreen mode

5. Implement Product Controller

Update src/products/products.controller.ts to handle requests:

import { Controller, Get, Query } from '@nestjs/common';
import { ProductsService } from './products.service';
import { Product } from './entities/product.entity';

@Controller('products')
export class ProductsController {
  constructor(private readonly productsService: ProductsService) {}

  @Get()
  async getProducts(
    @Query('brand') brand: string,
    @Query('minPrice') minPrice: string,
    @Query('maxPrice') maxPrice: string,
    @Query('ram') ram: string,
    @Query('storage') storage: string,
    @Query('rating') rating: string,
    @Query('sort') sort: string,
    @Query('page') page: string,
    @Query('limit') limit: string,
  ): Promise<{ data: Product[]; total: number }> {
    return await this.productsService.findAll({
      brand,
      minPrice,
      maxPrice,
      ram,
      storage,
      rating,
      sort,
      page,
      limit,
    });
  }
}
Enter fullscreen mode Exit fullscreen mode

6. Testing the API

Start your NestJS server:

npm run start
Enter fullscreen mode Exit fullscreen mode

You can test the endpoint using tools like Postman or cURL. For example:

  • GET http://localhost:3000/products?brand=Samsung&minPrice=100&maxPrice=1000&ram=8GB&sort=price:asc&page=1&limit=10

Summary

This setup creates a flexible product listing API that supports filtering by brand, price range, RAM, storage, and rating, as well as sorting and pagination. You can further extend and refine this setup by adding more filters and sorting options based on your requirements.

To create a backend for the Product Detail Page using NestJS, we need to provide endpoints that deliver comprehensive information about a specific product, including its details, images, specifications, user reviews, related products, and purchase options.

Backend Requirements for the Product Detail Page

  1. Product Details: Fetch detailed information about a specific product, such as name, brand, model, release date, and user rating.
  2. Product Gallery: Fetch images and videos for the product.
  3. Key and Detailed Specifications: Provide a summary of essential specs and a detailed list of technical specifications.
  4. User Reviews and Ratings: Fetch user reviews and ratings, and allow submission of new reviews.
  5. Related Products: Recommend similar or complementary products.
  6. Comparison Tool: Provide functionality to compare the current product with others.
  7. Buy Now/CTA: Provide links to purchase options or third-party retailers.

Step-by-Step Implementation

  1. Define Entity Models: Ensure your Product, Image, Review, and any other related entities are properly defined.
  2. Create Product Module, Service, and Controller: Handle the logic for fetching product details, images, reviews, and related products.
  3. Implement Routes and Logic: Create endpoints to fetch all necessary data for the product detail page.

1. Update the Entity Models

Define or update your entities to include necessary fields. Here’s an example of how your entities might look:

Product Entity

import { Entity, Column, PrimaryGeneratedColumn, OneToMany } from 'typeorm';
import { Image } from './image.entity';
import { Review } from './review.entity';

@Entity()
export class Product {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  name: string;

  @Column()
  brand: string;

  @Column()
  model: string;

  @Column()
  releaseDate: Date;

  @Column()
  averageRating: number;

  @Column('decimal', { precision: 5, scale: 2 })
  price: number;

  @Column()
  processor: string;

  @Column()
  ram: string;

  @Column()
  storage: string;

  @Column()
  display: string;

  @Column()
  batteryLife: string;

  @Column()
  camera: string;

  @Column()
  chipset: string;

  @Column()
  gpu: string;

  @Column()
  osVersion: string;

  @Column()
  dimensions: string;

  @Column()
  weight: string;

  @OneToMany(() => Image, (image) => image.product)
  images: Image[];

  @OneToMany(() => Review, (review) => review.product)
  reviews: Review[];

  // Additional fields as needed
}
Enter fullscreen mode Exit fullscreen mode

Image Entity

import { Entity, Column, PrimaryGeneratedColumn, ManyToOne } from 'typeorm';
import { Product } from './product.entity';

@Entity()
export class Image {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  url: string;

  @ManyToOne(() => Product, (product) => product.images)
  product: Product;
}
Enter fullscreen mode Exit fullscreen mode

Review Entity

import { Entity, Column, PrimaryGeneratedColumn, ManyToOne } from 'typeorm';
import { Product } from './product.entity';

@Entity()
export class Review {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  username: string;

  @Column()
  rating: number;

  @Column()
  comment: string;

  @Column()
  date: Date;

  @ManyToOne(() => Product, (product) => product.reviews)
  product: Product;
}
Enter fullscreen mode Exit fullscreen mode

2. Update the Product Module

Edit src/products/products.module.ts to include necessary imports:

import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { ProductsController } from './products.controller';
import { ProductsService } from './products.service';
import { Product } from './entities/product.entity';
import { Image } from './entities/image.entity';
import { Review } from './entities/review.entity';

@Module({
  imports: [TypeOrmModule.forFeature([Product, Image, Review])],
  controllers: [ProductsController],
  providers: [ProductsService],
})
export class ProductsModule {}
Enter fullscreen mode Exit fullscreen mode

3. Implement Product Service

The service will handle the logic for fetching a product’s details, images, reviews, related products, and other necessary information.

Update src/products/products.service.ts:

import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Product } from './entities/product.entity';
import { Image } from './entities/image.entity';
import { Review } from './entities/review.entity';

@Injectable()
export class ProductsService {
  constructor(
    @InjectRepository(Product)
    private productsRepository: Repository<Product>,
    @InjectRepository(Image)
    private imagesRepository: Repository<Image>,
    @InjectRepository(Review)
    private reviewsRepository: Repository<Review>,
  ) {}

  async findOne(productId: number): Promise<Product> {
    return this.productsRepository.findOne({
      where: { id: productId },
      relations: ['images', 'reviews'],
    });
  }

  async findRelatedProducts(productId: number): Promise<Product[]> {
    // Example logic to find related products (by brand, category, etc.)
    const product = await this.productsRepository.findOne({ where: { id: productId } });
    if (!product) return [];

    return this.productsRepository.find({
      where: {
        brand: product.brand,
        id: Not(product.id),
      },
      take: 4, // Example to return 4 related products
    });
  }

  async addReview(productId: number, reviewData: Partial<Review>): Promise<Review> {
    const review = this.reviewsRepository.create({ ...reviewData, product: { id: productId } });
    return this.reviewsRepository.save(review);
  }
}
Enter fullscreen mode Exit fullscreen mode

4. Implement Product Controller

Update src/products/products.controller.ts to handle requests:

import { Controller, Get, Param, Post, Body } from '@nestjs/common';
import { ProductsService } from './products.service';
import { Product } from './entities/product.entity';
import { Review } from './entities/review.entity';

@Controller('products')
export class ProductsController {
  constructor(private readonly productsService: ProductsService) {}

  @Get(':id')
  async getProductDetail(@Param('id') id: string): Promise<Product> {
    return await this.productsService.findOne(parseInt(id));
  }

  @Get(':id/related')
  async getRelatedProducts(@Param('id') id: string): Promise<Product[]> {
    return await this.productsService.findRelatedProducts(parseInt(id));
  }

  @Post(':id/reviews')
  async addReview(
    @Param('id') id: string,
    @Body() reviewData: Partial<Review>,
  ): Promise<Review> {
    return await this.productsService.addReview(parseInt(id), reviewData);
  }
}
Enter fullscreen mode Exit fullscreen mode

5. Testing the API

Start your NestJS server:

npm run start
Enter fullscreen mode Exit fullscreen mode

Test the endpoints using tools like Postman or cURL. Here are some example requests:

  • Get Product Details: GET http://localhost:3000/products/1
  • Get Related Products: GET http://localhost:3000/products/1/related
  • Add a Review: POST http://localhost:3000/products/1/reviews with a JSON body like:
  {
    "username": "John Doe",
    "rating": 5,
    "comment": "Great product!",
    "date": "2024-08-26"
  }
Enter fullscreen mode Exit fullscreen mode

Summary

This setup provides a comprehensive backend for the Product Detail Page, allowing you to fetch product details, images, reviews, related products, and handle review submissions. You can extend and refine this setup further based on your specific requirements, such as adding more fields, handling images differently, or integrating third-party APIs for purchasing options.

To build the backend for the Review Page and Blog and Article Page using NestJS, we'll define appropriate endpoints to handle each feature's requirements. These pages need to handle data such as detailed reviews, user comments, article content, and related articles.

Backend Requirements

  1. Review Page:

    • Fetch detailed review information, including product image, name, star rating, and review sections.
    • Handle user comments and discussions related to the review.
    • Display a rating breakdown for the review.
  2. Blog and Article Page:

    • Fetch blog or article content with its metadata (title, author, publication date, tags).
    • Provide author bio and links to related articles.
    • Handle user comments and enable social sharing.

Step-by-Step Implementation

1. Review Page Backend

Entity Models
  • ReviewDetail Entity: Captures detailed reviews, reviewer details, and review content.
  • Comment Entity: Captures user comments related to a review.

Here's an example of how these entities could be structured:

// review-detail.entity.ts
import { Entity, Column, PrimaryGeneratedColumn, OneToMany } from 'typeorm';
import { Comment } from './comment.entity';

@Entity()
export class ReviewDetail {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  productName: string;

  @Column()
  productImage: string;

  @Column()
  starRating: number;

  @Column()
  reviewerName: string;

  @Column()
  reviewDate: Date;

  @Column('text')
  reviewSummary: string;

  @Column('text')
  designReview: string;

  @Column('text')
  performanceReview: string;

  @Column('text')
  batteryLifeReview: string;

  @Column('text')
  cameraQualityReview: string;

  @Column('json')
  prosCons: { pros: string[]; cons: string[] };

  @OneToMany(() => Comment, (comment) => comment.review)
  comments: Comment[];
}

// comment.entity.ts
import { Entity, Column, PrimaryGeneratedColumn, ManyToOne } from 'typeorm';
import { ReviewDetail } from './review-detail.entity';

@Entity()
export class Comment {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  username: string;

  @Column('text')
  comment: string;

  @Column()
  date: Date;

  @ManyToOne(() => ReviewDetail, (reviewDetail) => reviewDetail.comments)
  review: ReviewDetail;
}
Enter fullscreen mode Exit fullscreen mode
Review Module, Service, and Controller
  • Module: Configure the ReviewDetail and Comment entities in the module.
  • Service: Implement logic to fetch review details and handle user comments.
  • Controller: Define API endpoints for fetching review details and submitting comments.
Review Module (review.module.ts)
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { ReviewService } from './review.service';
import { ReviewController } from './review.controller';
import { ReviewDetail } from './entities/review-detail.entity';
import { Comment } from './entities/comment.entity';

@Module({
  imports: [TypeOrmModule.forFeature([ReviewDetail, Comment])],
  providers: [ReviewService],
  controllers: [ReviewController],
})
export class ReviewModule {}
Enter fullscreen mode Exit fullscreen mode
Review Service (review.service.ts)
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { ReviewDetail } from './entities/review-detail.entity';
import { Comment } from './entities/comment.entity';

@Injectable()
export class ReviewService {
  constructor(
    @InjectRepository(ReviewDetail)
    private reviewRepository: Repository<ReviewDetail>,
    @InjectRepository(Comment)
    private commentRepository: Repository<Comment>,
  ) {}

  async getReviewDetail(reviewId: number): Promise<ReviewDetail> {
    return this.reviewRepository.findOne({
      where: { id: reviewId },
      relations: ['comments'],
    });
  }

  async addComment(reviewId: number, commentData: Partial<Comment>): Promise<Comment> {
    const comment = this.commentRepository.create({
      ...commentData,
      review: { id: reviewId },
    });
    return this.commentRepository.save(comment);
  }

  async getRatingBreakdown(reviewId: number): Promise<any> {
    // Example: Calculate rating breakdown
    const review = await this.reviewRepository.findOne({ where: { id: reviewId } });
    if (!review) return null;

    const comments = await this.commentRepository.find({ where: { review: { id: reviewId } } });
    const ratingBreakdown = comments.reduce((acc, comment) => {
      acc[comment.rating] = (acc[comment.rating] || 0) + 1;
      return acc;
    }, {});

    return ratingBreakdown;
  }
}
Enter fullscreen mode Exit fullscreen mode
Review Controller (review.controller.ts)
import { Controller, Get, Param, Post, Body } from '@nestjs/common';
import { ReviewService } from './review.service';
import { ReviewDetail } from './entities/review-detail.entity';
import { Comment } from './entities/comment.entity';

@Controller('reviews')
export class ReviewController {
  constructor(private readonly reviewService: ReviewService) {}

  @Get(':id')
  async getReviewDetail(@Param('id') id: string): Promise<ReviewDetail> {
    return await this.reviewService.getReviewDetail(parseInt(id));
  }

  @Post(':id/comments')
  async addComment(@Param('id') id: string, @Body() commentData: Partial<Comment>): Promise<Comment> {
    return await this.reviewService.addComment(parseInt(id), commentData);
  }

  @Get(':id/rating-breakdown')
  async getRatingBreakdown(@Param('id') id: string): Promise<any> {
    return await this.reviewService.getRatingBreakdown(parseInt(id));
  }
}
Enter fullscreen mode Exit fullscreen mode

2. Blog and Article Page Backend

Entity Models
  • Article Entity: Captures article details, including title, content, author, and related metadata.
  • Author Entity: Captures author details.
  • Comment Entity: Reuse or create a new entity for article comments if it differs from the review comments.

Here's an example:

// article.entity.ts
import { Entity, Column, PrimaryGeneratedColumn, ManyToOne, OneToMany } from 'typeorm';
import { Author } from './author.entity';
import { Comment } from './comment.entity';

@Entity()
export class Article {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  title: string;

  @Column()
  authorName: string;

  @Column()
  publicationDate: Date;

  @Column('text')
  content: string;

  @Column('simple-array')
  tags: string[];

  @ManyToOne(() => Author, (author) => author.articles)
  author: Author;

  @OneToMany(() => Comment, (comment) => comment.article)
  comments: Comment[];
}

// author.entity.ts
import { Entity, Column, PrimaryGeneratedColumn, OneToMany } from 'typeorm';
import { Article } from './article.entity';

@Entity()
export class Author {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  name: string;

  @Column()
  bio: string;

  @OneToMany(() => Article, (article) => article.author)
  articles: Article[];
}

// comment.entity.ts (updated for both reviews and articles)
import { Entity, Column, PrimaryGeneratedColumn, ManyToOne } from 'typeorm';
import { ReviewDetail } from './review-detail.entity';
import { Article } from './article.entity';

@Entity()
export class Comment {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  username: string;

  @Column('text')
  comment: string;

  @Column()
  date: Date;

  @ManyToOne(() => ReviewDetail, (reviewDetail) => reviewDetail.comments, { nullable: true })
  review: ReviewDetail;

  @ManyToOne(() => Article, (article) => article.comments, { nullable: true })
  article: Article;
}
Enter fullscreen mode Exit fullscreen mode
Article Module, Service, and Controller

Configure the article-related entities, services, and controllers.

Article Module (article.module.ts)
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { ArticleService } from './article.service';
import { ArticleController } from './article.controller';
import { Article } from './entities/article.entity';
import { Author } from './entities/author.entity';
import { Comment } from './entities/comment.entity';

@Module({
  imports: [TypeOrmModule.forFeature([Article, Author, Comment])],
  providers: [ArticleService],
  controllers: [ArticleController],
})
export class ArticleModule {}
Enter fullscreen mode Exit fullscreen mode
Article Service (article.service.ts)
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Article } from './entities/article.entity';
import { Author } from './entities/author.entity';
import { Comment } from './entities/comment.entity';

@Injectable()
export class ArticleService {
  constructor(
    @InjectRepository(Article)
    private articleRepository: Repository<Article>,
    @InjectRepository(Author)
    private authorRepository: Repository<Author>,
    @InjectRepository(Comment)
    private commentRepository: Repository<Comment>,
  ) {}

  async getArticleDetail(articleId: number): Promise<Article> {
    return this.articleRepository.findOne({
      where: { id:

 articleId },
      relations: ['author', 'comments'],
    });
  }

  async getRelatedArticles(articleId: number): Promise<Article[]> {
    const article = await this.articleRepository.findOne({ where: { id: articleId } });
    if (!article) return [];

    return this.articleRepository.find({
      where: { tags: In(article.tags), id: Not(article.id) },
      take: 4, // Example to return 4 related articles
    });
  }

  async addComment(articleId: number, commentData: Partial<Comment>): Promise<Comment> {
    const comment = this.commentRepository.create({
      ...commentData,
      article: { id: articleId },
    });
    return this.commentRepository.save(comment);
  }
}
Enter fullscreen mode Exit fullscreen mode
Article Controller (article.controller.ts)
import { Controller, Get, Param, Post, Body } from '@nestjs/common';
import { ArticleService } from './article.service';
import { Article } from './entities/article.entity';
import { Comment } from './entities/comment.entity';

@Controller('articles')
export class ArticleController {
  constructor(private readonly articleService: ArticleService) {}

  @Get(':id')
  async getArticleDetail(@Param('id') id: string): Promise<Article> {
    return await this.articleService.getArticleDetail(parseInt(id));
  }

  @Get(':id/related')
  async getRelatedArticles(@Param('id') id: string): Promise<Article[]> {
    return await this.articleService.getRelatedArticles(parseInt(id));
  }

  @Post(':id/comments')
  async addComment(@Param('id') id: string, @Body() commentData: Partial<Comment>): Promise<Comment> {
    return await this.articleService.addComment(parseInt(id), commentData);
  }
}
Enter fullscreen mode Exit fullscreen mode

Summary

This setup provides a comprehensive backend for both the Review Page and Blog and Article Page. The backend handles fetching detailed review and article information, managing comments, and providing related content. You can extend and refine this setup further based on your specific requirements, such as adding more fields, integrating additional services, or enhancing the comment system.

To implement the backend for the Comparison Page and Search Results Page using NestJS, we'll focus on creating endpoints to manage product comparisons and search functionalities.

Backend Requirements

  1. Comparison Page:

    • Fetch product details for comparison.
    • Allow users to add or remove products from the comparison list.
    • Generate a quick summary of key differences and similarities.
  2. Search Results Page:

    • Fetch and display search results based on user queries.
    • Provide filtering and sorting options.
    • Implement pagination for navigating search results.

Step-by-Step Implementation

1. Comparison Page Backend

Entity Models
  • Product Entity: Captures product details required for comparison, including specifications like price, display, processor, camera, and battery.

Here’s an example of a simplified Product entity:

// product.entity.ts
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';

@Entity()
export class Product {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  name: string;

  @Column()
  brand: string;

  @Column('decimal')
  price: number;

  @Column()
  display: string;

  @Column()
  processor: string;

  @Column()
  camera: string;

  @Column()
  battery: string;

  // Add more columns as needed for other specifications
}
Enter fullscreen mode Exit fullscreen mode
Comparison Module, Service, and Controller
  • Module: Configure the Product entity in the module.
  • Service: Implement logic to fetch product details for comparison and manage the comparison list.
  • Controller: Define API endpoints for managing product comparisons.
Comparison Module (comparison.module.ts)
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { ComparisonService } from './comparison.service';
import { ComparisonController } from './comparison.controller';
import { Product } from './entities/product.entity';

@Module({
  imports: [TypeOrmModule.forFeature([Product])],
  providers: [ComparisonService],
  controllers: [ComparisonController],
})
export class ComparisonModule {}
Enter fullscreen mode Exit fullscreen mode
Comparison Service (comparison.service.ts)
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { Product } from './entities/product.entity';

@Injectable()
export class ComparisonService {
  constructor(
    @InjectRepository(Product)
    private productRepository: Repository<Product>,
  ) {}

  async getProductsForComparison(productIds: number[]): Promise<Product[]> {
    return this.productRepository.findByIds(productIds);
  }

  async addProductToComparison(productId: number): Promise<Product> {
    return this.productRepository.findOne({ where: { id: productId } });
  }

  async removeProductFromComparison(productId: number): Promise<void> {
    // In a real application, manage comparison lists with user-specific sessions or database entries
    return;
  }

  async getQuickSummary(productIds: number[]): Promise<any> {
    const products = await this.getProductsForComparison(productIds);
    const summary = {
      differences: [],
      similarities: [],
    };

    // Example logic to generate summary
    if (products.length > 1) {
      const baseProduct = products[0];
      products.slice(1).forEach((product) => {
        for (const key in baseProduct) {
          if (baseProduct[key] !== product[key]) {
            summary.differences.push({ key, values: products.map(p => p[key]) });
          } else {
            summary.similarities.push({ key, value: baseProduct[key] });
          }
        }
      });
    }

    return summary;
  }
}
Enter fullscreen mode Exit fullscreen mode
Comparison Controller (comparison.controller.ts)
import { Controller, Get, Param, Post, Body, Delete } from '@nestjs/common';
import { ComparisonService } from './comparison.service';
import { Product } from './entities/product.entity';

@Controller('comparison')
export class ComparisonController {
  constructor(private readonly comparisonService: ComparisonService) {}

  @Get(':ids')
  async getProductsForComparison(@Param('ids') ids: string): Promise<Product[]> {
    const productIds = ids.split(',').map((id) => parseInt(id));
    return await this.comparisonService.getProductsForComparison(productIds);
  }

  @Post('add')
  async addProductToComparison(@Body('productId') productId: number): Promise<Product> {
    return await this.comparisonService.addProductToComparison(productId);
  }

  @Delete('remove')
  async removeProductFromComparison(@Body('productId') productId: number): Promise<void> {
    return await this.comparisonService.removeProductFromComparison(productId);
  }

  @Get('summary/:ids')
  async getQuickSummary(@Param('ids') ids: string): Promise<any> {
    const productIds = ids.split(',').map((id) => parseInt(id));
    return await this.comparisonService.getQuickSummary(productIds);
  }
}
Enter fullscreen mode Exit fullscreen mode

2. Search Results Page Backend

Search Module, Service, and Controller

The search functionality should allow users to search for products, reviews, articles, etc., based on a query and apply filters or sorting.

Search Module (search.module.ts)
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { SearchService } from './search.service';
import { SearchController } from './search.controller';
import { Product } from './entities/product.entity';
// Import other entities like Review, Article if needed

@Module({
  imports: [TypeOrmModule.forFeature([Product])], // Add other entities here
  providers: [SearchService],
  controllers: [SearchController],
})
export class SearchModule {}
Enter fullscreen mode Exit fullscreen mode
Search Service (search.service.ts)
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, Like } from 'typeorm';
import { Product } from './entities/product.entity';
// Import other entities like Review, Article if needed

@Injectable()
export class SearchService {
  constructor(
    @InjectRepository(Product)
    private productRepository: Repository<Product>,
    // Inject other repositories like Review, Article if needed
  ) {}

  async searchProducts(query: string, filters: any, sort: string, page: number, limit: number): Promise<any> {
    const [results, total] = await this.productRepository.findAndCount({
      where: { name: Like(`%${query}%`) }, // Example: searching in product names
      skip: (page - 1) * limit,
      take: limit,
      order: { price: sort === 'price' ? 'ASC' : 'DESC' },
    });

    return {
      results,
      total,
      page,
      pages: Math.ceil(total / limit),
    };
  }

  // Implement similar methods for searching reviews, articles, etc.
}
Enter fullscreen mode Exit fullscreen mode
Search Controller (search.controller.ts)
import { Controller, Get, Query } from '@nestjs/common';
import { SearchService } from './search.service';

@Controller('search')
export class SearchController {
  constructor(private readonly searchService: SearchService) {}

  @Get('products')
  async searchProducts(
    @Query('query') query: string,
    @Query('filters') filters: string,
    @Query('sort') sort: string,
    @Query('page') page: string = '1',
    @Query('limit') limit: string = '10',
  ): Promise<any> {
    const filtersObject = filters ? JSON.parse(filters) : {};
    return await this.searchService.searchProducts(query, filtersObject, sort, parseInt(page), parseInt(limit));
  }

  // Implement similar endpoints for searching reviews, articles, etc.
}
Enter fullscreen mode Exit fullscreen mode

Summary

The provided backend setup for the Comparison Page and Search Results Page in NestJS includes modules, services, and controllers to manage product comparison and search functionalities. This implementation covers basic operations such as fetching comparison data, adding/removing products, generating quick summaries, and searching products with pagination, filtering, and sorting. You can expand this setup further based on your application's needs, adding more sophisticated filtering logic, managing user sessions for comparison lists, or enhancing the search capabilities to include other content types like reviews and articles.

To implement the backend for the About Us Page, Contact Us Page, User Profile Page, and FAQ Page using NestJS, we will create various modules, services, and controllers to manage the required functionalities.

Backend Requirements

  1. About Us Page:

    • Provide the website’s mission statement, team profiles, history, and contact information.
  2. Contact Us Page:

    • Implement a contact form for users to send inquiries.
    • Display contact information and social media links.
  3. User Profile Page:

    • Manage user profile information, including display and editing capabilities.
    • Provide an activity log and sections for saved products/articles.
  4. FAQ Page:

    • Display a list of frequently asked questions in an accordion style.
    • Implement a search bar to find specific questions.

Step-by-Step Implementation

1. About Us Page Backend

The About Us Page is largely static and doesn’t require much backend functionality beyond serving static content. However, if the team profiles, mission statement, or history are dynamically fetched from a database, we can create entities and services for them.

Entity Models
  • TeamMember Entity: Captures details about team members.
// team-member.entity.ts
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';

@Entity()
export class TeamMember {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  name: string;

  @Column()
  role: string;

  @Column()
  bio: string;

  @Column()
  photoUrl: string;
}
Enter fullscreen mode Exit fullscreen mode
About Us Module, Service, and Controller
  • Module: Configure the TeamMember entity in the module.
  • Service: Implement logic to fetch the mission statement, team profiles, history, etc.
  • Controller: Define API endpoints for fetching the About Us page content.
About Us Module (about.module.ts)
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { AboutService } from './about.service';
import { AboutController } from './about.controller';
import { TeamMember } from './entities/team-member.entity';

@Module({
  imports: [TypeOrmModule.forFeature([TeamMember])],
  providers: [AboutService],
  controllers: [AboutController],
})
export class AboutModule {}
Enter fullscreen mode Exit fullscreen mode
About Us Service (about.service.ts)
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { TeamMember } from './entities/team-member.entity';

@Injectable()
export class AboutService {
  constructor(
    @InjectRepository(TeamMember)
    private teamMemberRepository: Repository<TeamMember>,
  ) {}

  async getMissionStatement(): Promise<string> {
    // In a real app, fetch this from a database or a config file
    return 'Our mission is to provide excellent service and quality products.';
  }

  async getTeamMembers(): Promise<TeamMember[]> {
    return this.teamMemberRepository.find();
  }

  async getHistory(): Promise<string> {
    // In a real app, fetch this from a database or a config file
    return 'Our company was founded in 2020 with a mission to revolutionize tech products.';
  }

  async getContactInfo(): Promise<any> {
    // Return contact information
    return {
      email: 'info@website.com',
      phone: '+123456789',
      address: '123 Tech Street, Tech City, Country',
      socialMedia: {
        facebook: 'https://facebook.com/website',
        twitter: 'https://twitter.com/website',
        instagram: 'https://instagram.com/website',
      },
    };
  }
}
Enter fullscreen mode Exit fullscreen mode
About Us Controller (about.controller.ts)
import { Controller, Get } from '@nestjs/common';
import { AboutService } from './about.service';

@Controller('about')
export class AboutController {
  constructor(private readonly aboutService: AboutService) {}

  @Get('mission')
  getMissionStatement(): Promise<string> {
    return this.aboutService.getMissionStatement();
  }

  @Get('team')
  getTeamMembers(): Promise<any[]> {
    return this.aboutService.getTeamMembers();
  }

  @Get('history')
  getHistory(): Promise<string> {
    return this.aboutService.getHistory();
  }

  @Get('contact-info')
  getContactInfo(): Promise<any> {
    return this.aboutService.getContactInfo();
  }
}
Enter fullscreen mode Exit fullscreen mode

2. Contact Us Page Backend

The Contact Us Page involves capturing user inquiries and displaying contact details. For user inquiries, we’ll create an endpoint to handle form submissions.

Entity Models
  • ContactForm Entity: Captures details of user inquiries.
// contact-form.entity.ts
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';

@Entity()
export class ContactForm {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  name: string;

  @Column()
  email: string;

  @Column()
  subject: string;

  @Column()
  message: string;
}
Enter fullscreen mode Exit fullscreen mode
Contact Us Module, Service, and Controller
Contact Module (contact.module.ts)
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { ContactService } from './contact.service';
import { ContactController } from './contact.controller';
import { ContactForm } from './entities/contact-form.entity';

@Module({
  imports: [TypeOrmModule.forFeature([ContactForm])],
  providers: [ContactService],
  controllers: [ContactController],
})
export class ContactModule {}
Enter fullscreen mode Exit fullscreen mode
Contact Service (contact.service.ts)
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { ContactForm } from './entities/contact-form.entity';

@Injectable()
export class ContactService {
  constructor(
    @InjectRepository(ContactForm)
    private contactFormRepository: Repository<ContactForm>,
  ) {}

  async submitContactForm(contactForm: ContactForm): Promise<ContactForm> {
    return this.contactFormRepository.save(contactForm);
  }

  async getContactInfo(): Promise<any> {
    // Return contact information
    return {
      email: 'support@website.com',
      phone: '+123456789',
      address: '123 Tech Street, Tech City, Country',
      socialMedia: {
        facebook: 'https://facebook.com/website',
        twitter: 'https://twitter.com/website',
        instagram: 'https://instagram.com/website',
      },
    };
  }
}
Enter fullscreen mode Exit fullscreen mode
Contact Controller (contact.controller.ts)
import { Controller, Post, Body, Get } from '@nestjs/common';
import { ContactService } from './contact.service';
import { ContactForm } from './entities/contact-form.entity';

@Controller('contact')
export class ContactController {
  constructor(private readonly contactService: ContactService) {}

  @Post('submit')
  submitContactForm(@Body() contactForm: ContactForm): Promise<ContactForm> {
    return this.contactService.submitContactForm(contactForm);
  }

  @Get('info')
  getContactInfo(): Promise<any> {
    return this.contactService.getContactInfo();
  }
}
Enter fullscreen mode Exit fullscreen mode

3. User Profile Page Backend

The User Profile Page includes profile management, activity logs, and saved items. We'll assume a simple User entity and manage profile updates and activity.

Entity Models
  • User Entity: Captures user details.
// user.entity.ts
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';

@Entity()
export class User {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  name: string;

  @Column()
  email: string;

  @Column()
  password: string;

  // Additional user fields as needed
}
Enter fullscreen mode Exit fullscreen mode
  • ActivityLog Entity: Captures user activity details.
// activity-log.entity.ts
import { Entity, Column, PrimaryGeneratedColumn, ManyToOne } from 'typeorm';
import { User } from './user.entity';

@Entity()
export class ActivityLog {
  @PrimaryGeneratedColumn()
  id: number;

  @ManyToOne(() => User, (user) => user.id)
  user: User;

  @Column()
  activityType: string;

  @Column()
  details: string;

  @Column('timestamp')
  timestamp: Date;
}
Enter fullscreen mode Exit fullscreen mode
User Profile Module, Service, and Controller
User Module (user.module.ts)
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { UserService } from './user.service';
import { UserController } from './user.controller';
import { User } from './entities/user.entity';
import { ActivityLog } from './entities/activity-log.entity';

@Module({
  imports: [TypeOrmModule.forFeature([User, ActivityLog])],
  providers: [UserService],
  controllers: [UserController],
})
export class UserModule {}
Enter fullscreen mode Exit fullscreen mode
User Service (user.service.ts)
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository } from 'typeorm';
import { User } from './entities/user.entity';
import { ActivityLog } from './entities/activity-log.entity';

@Injectable()
export class UserService {
  constructor(
    @InjectRepository(User)
    private userRepository: Repository<User>,
    @InjectRepository(ActivityLog)
    private activityLogRepository: Repository<ActivityLog>,
  ) {}

  async getUserProfile(userId: number): Promise<User> {
    return

 this.userRepository.findOne({ where: { id: userId } });
  }

  async updateUserProfile(userId: number, updateData: Partial<User>): Promise<User> {
    await this.userRepository.update(userId, updateData);
    return this.userRepository.findOne({ where: { id: userId } });
  }

  async getUserActivityLog(userId: number): Promise<ActivityLog[]> {
    return this.activityLogRepository.find({ where: { user: { id: userId } } });
  }

  async getUserSavedItems(userId: number): Promise<any> {
    // Mock implementation; replace with actual logic to fetch saved products/articles
    return [
      { type: 'product', id: 1, name: 'Product 1' },
      { type: 'article', id: 2, title: 'Article 1' },
    ];
  }
}
Enter fullscreen mode Exit fullscreen mode
User Controller (user.controller.ts)
import { Controller, Get, Param, Put, Body } from '@nestjs/common';
import { UserService } from './user.service';
import { User } from './entities/user.entity';

@Controller('user')
export class UserController {
  constructor(private readonly userService: UserService) {}

  @Get(':id/profile')
  getUserProfile(@Param('id') userId: number): Promise<User> {
    return this.userService.getUserProfile(userId);
  }

  @Put(':id/profile')
  updateUserProfile(@Param('id') userId: number, @Body() updateData: Partial<User>): Promise<User> {
    return this.userService.updateUserProfile(userId, updateData);
  }

  @Get(':id/activity-log')
  getUserActivityLog(@Param('id') userId: number): Promise<any[]> {
    return this.userService.getUserActivityLog(userId);
  }

  @Get(':id/saved-items')
  getUserSavedItems(@Param('id') userId: number): Promise<any[]> {
    return this.userService.getUserSavedItems(userId);
  }
}
Enter fullscreen mode Exit fullscreen mode

4. FAQ Page Backend

The FAQ Page will store frequently asked questions and provide a search function.

Entity Models
  • FAQ Entity: Captures FAQ details.
// faq.entity.ts
import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm';

@Entity()
export class FAQ {
  @PrimaryGeneratedColumn()
  id: number;

  @Column()
  question: string;

  @Column()
  answer: string;
}
Enter fullscreen mode Exit fullscreen mode
FAQ Module, Service, and Controller
FAQ Module (faq.module.ts)
import { Module } from '@nestjs/common';
import { TypeOrmModule } from '@nestjs/typeorm';
import { FAQService } from './faq.service';
import { FAQController } from './faq.controller';
import { FAQ } from './entities/faq.entity';

@Module({
  imports: [TypeOrmModule.forFeature([FAQ])],
  providers: [FAQService],
  controllers: [FAQController],
})
export class FAQModule {}
Enter fullscreen mode Exit fullscreen mode
FAQ Service (faq.service.ts)
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Repository, Like } from 'typeorm';
import { FAQ } from './entities/faq.entity';

@Injectable()
export class FAQService {
  constructor(
    @InjectRepository(FAQ)
    private faqRepository: Repository<FAQ>,
  ) {}

  async getAllFAQs(): Promise<FAQ[]> {
    return this.faqRepository.find();
  }

  async searchFAQs(query: string): Promise<FAQ[]> {
    return this.faqRepository.find({
      where: { question: Like(`%${query}%`) },
    });
  }
}
Enter fullscreen mode Exit fullscreen mode
FAQ Controller (faq.controller.ts)
import { Controller, Get, Query } from '@nestjs/common';
import { FAQService } from './faq.service';

@Controller('faq')
export class FAQController {
  constructor(private readonly faqService: FAQService) {}

  @Get()
  getAllFAQs(): Promise<FAQ[]> {
    return this.faqService.getAllFAQs();
  }

  @Get('search')
  searchFAQs(@Query('query') query: string): Promise<FAQ[]> {
    return this.faqService.searchFAQs(query);
  }
}
Enter fullscreen mode Exit fullscreen mode

Summary

The provided backend setup for the About Us Page, Contact Us Page, User Profile Page, and FAQ Page in NestJS includes entities, modules, services, and controllers to manage these functionalities. This setup allows for dynamic content management for team profiles, user inquiries, user profile updates, and FAQ searches. You can expand this setup by adding authentication for user profile management, integrating email services for contact forms, and implementing more advanced search features for the FAQ page.

(Frontend)

Here's a basic implementation of the homepage using Next.js and Tailwind CSS:

pages/index.js

import Head from 'next/head';

export default function Home() {
  return (
    <div>
      <Head>
        <title>Mobile Specs & Reviews</title>
      </Head>
      <header className="bg-gray-900 text-white p-4">
        <nav className="container mx-auto flex justify-between items-center">
          <h1 className="text-xl font-bold">Mobile Hub</h1>
          <div>
            <input
              type="text"
              placeholder="Search products..."
              className="p-2 rounded text-black"
            />
          </div>
        </nav>
      </header>

      <main className="container mx-auto p-4">
        {/* Hero Section */}
        <section className="bg-blue-600 text-white p-10 rounded-lg mb-8">
          <h2 className="text-3xl font-bold mb-4">Featured Products</h2>
          <p>Check out the latest and greatest mobile phones available now!</p>
          <button className="mt-4 bg-white text-blue-600 p-2 rounded">
            Shop Now
          </button>
        </section>

        {/* Featured Products */}
        <section className="mb-8">
          <h2 className="text-2xl font-bold mb-4">Top Rated Mobile Phones</h2>
          <div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4">
            <div className="bg-white p-4 rounded-lg shadow-md">
              <img
                src="https://via.placeholder.com/150"
                alt="Product Image"
                className="w-full h-40 object-cover mb-2"
              />
              <h3 className="text-lg font-bold mb-2">Mobile Phone 1</h3>
              <p>Brief description of the mobile phone.</p>
            </div>
            {/* Repeat for other products */}
          </div>
        </section>

        {/* Latest Reviews */}
        <section className="mb-8">
          <h2 className="text-2xl font-bold mb-4">Latest Reviews</h2>
          <div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4">
            <div className="bg-white p-4 rounded-lg shadow-md">
              <h3 className="text-lg font-bold mb-2">Review for Phone 1</h3>
              <p>Summary of the review...</p>
            </div>
            {/* Repeat for other reviews */}
          </div>
        </section>

        {/* Popular Categories */}
        <section className="mb-8">
          <h2 className="text-2xl font-bold mb-4">Popular Categories</h2>
          <div className="grid grid-cols-2 md:grid-cols-4 gap-4">
            <div className="bg-white p-4 rounded-lg shadow-md">
              <h3 className="text-lg font-bold mb-2">Mobile Phones</h3>
            </div>
            <div className="bg-white p-4 rounded-lg shadow-md">
              <h3 className="text-lg font-bold mb-2">Laptops</h3>
            </div>
            <div className="bg-white p-4 rounded-lg shadow-md">
              <h3 className="text-lg font-bold mb-2">Cameras</h3>
            </div>
            <div className="bg-white p-4 rounded-lg shadow-md">
              <h3 className="text-lg font-bold mb-2">Accessories</h3>
            </div>
          </div>
        </section>

        {/* Trending Articles */}
        <section className="mb-8">
          <h2 className="text-2xl font-bold mb-4">Trending Articles</h2>
          <div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4">
            <div className="bg-white p-4 rounded-lg shadow-md">
              <h3 className="text-lg font-bold mb-2">Article Title 1</h3>
              <p>Summary of the article...</p>
            </div>
            {/* Repeat for other articles */}
          </div>
        </section>

        {/* Subscription CTA */}
        <section className="bg-blue-600 text-white p-8 rounded-lg">
          <h2 className="text-2xl font-bold mb-4">Subscribe to our Newsletter</h2>
          <p>Stay updated with the latest mobile reviews and news.</p>
          <input
            type="email"
            placeholder="Enter your email"
            className="p-2 rounded text-black mt-4"
          />
          <button className="ml-2 bg-white text-blue-600 p-2 rounded">
            Subscribe
          </button>
        </section>
      </main>

      <footer className="bg-gray-900 text-white p-4 mt-8">
        <div className="container mx-auto text-center">
          <p>&copy; 2024 Mobile Hub. All rights reserved.</p>
        </div>
      </footer>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Explanation:

  • Hero Section: A dynamic banner for showcasing featured products or articles with a CTA button.
  • Search Bar: Positioned prominently for easy access.
  • Featured Products: A grid showcasing top-rated mobile phones.
  • Latest Reviews: A section for displaying recent reviews.
  • Popular Categories: Quick links to popular product categories.
  • Trending Articles: A grid of trending articles.
  • Subscription CTA: A call to action for newsletter subscription.
  • Footer: Contains quick links, contact information, and disclaimers.

Tailwind CSS Integration

If Tailwind CSS is not already set up in your Next.js project, follow these steps:

  1. Install Tailwind CSS:
   npm install -D tailwindcss postcss autoprefixer
   npx tailwindcss init -p
Enter fullscreen mode Exit fullscreen mode
  1. Configure tailwind.config.js:
   module.exports = {
     content: [
       './pages/**/*.{js,ts,jsx,tsx}',
       './components/**/*.{js,ts,jsx,tsx}',
     ],
     theme: {
       extend: {},
     },
     plugins: [],
   };
Enter fullscreen mode Exit fullscreen mode
  1. Create styles/globals.css and import Tailwind CSS:
   @tailwind base;
   @tailwind components;
   @tailwind utilities;
Enter fullscreen mode Exit fullscreen mode
  1. Import globals.css in _app.js:
   import '../styles/globals.css';

   function MyApp({ Component, pageProps }) {
     return <Component {...pageProps} />;
   }

   export default MyApp;
Enter fullscreen mode Exit fullscreen mode

This setup should give you a clean and responsive homepage ready for customization.

Here’s the frontend code for the Product Listing Page and Product Detail Page using Next.js and Tailwind CSS.

2. Product Listing Page

pages/products/[category].js

This page will display products in a specific category, allowing filtering and sorting options.

import { useState } from 'react';
import { useRouter } from 'next/router';

export default function ProductListing() {
  const router = useRouter();
  const { category } = router.query;

  const [filter, setFilter] = useState({
    brand: '',
    priceRange: '',
    ram: '',
    storage: '',
  });

  const products = [
    // Sample product data
    { id: 1, name: 'Phone 1', brand: 'Brand A', price: 299, ram: '4GB', storage: '64GB', image: 'https://via.placeholder.com/150' },
    { id: 2, name: 'Phone 2', brand: 'Brand B', price: 399, ram: '6GB', storage: '128GB', image: 'https://via.placeholder.com/150' },
    // More products...
  ];

  const filteredProducts = products.filter(product => {
    return (
      (filter.brand === '' || product.brand === filter.brand) &&
      (filter.priceRange === '' || (product.price >= filter.priceRange[0] && product.price <= filter.priceRange[1])) &&
      (filter.ram === '' || product.ram === filter.ram) &&
      (filter.storage === '' || product.storage === filter.storage)
    );
  });

  return (
    <div className="container mx-auto p-4">
      {/* Category Header */}
      <header className="mb-8">
        <h1 className="text-3xl font-bold">{category} Phones</h1>
        <p className="text-gray-600">Explore the latest {category} phones with detailed specs and reviews.</p>
      </header>

      <div className="flex">
        {/* Filters Sidebar */}
        <aside className="w-1/4 p-4 bg-gray-100 rounded-lg">
          <h2 className="text-xl font-bold mb-4">Filters</h2>
          <div className="mb-4">
            <label className="block mb-2 font-semibold">Brand</label>
            <select
              className="p-2 w-full rounded"
              value={filter.brand}
              onChange={(e) => setFilter({ ...filter, brand: e.target.value })}
            >
              <option value="">All</option>
              <option value="Brand A">Brand A</option>
              <option value="Brand B">Brand B</option>
              {/* Add more brands */}
            </select>
          </div>
          <div className="mb-4">
            <label className="block mb-2 font-semibold">Price Range</label>
            <select
              className="p-2 w-full rounded"
              value={filter.priceRange}
              onChange={(e) => setFilter({ ...filter, priceRange: e.target.value })}
            >
              <option value="">All</option>
              <option value={[0, 299]}>$0 - $299</option>
              <option value={[300, 599]}>$300 - $599</option>
              {/* Add more price ranges */}
            </select>
          </div>
          <div className="mb-4">
            <label className="block mb-2 font-semibold">RAM</label>
            <select
              className="p-2 w-full rounded"
              value={filter.ram}
              onChange={(e) => setFilter({ ...filter, ram: e.target.value })}
            >
              <option value="">All</option>
              <option value="4GB">4GB</option>
              <option value="6GB">6GB</option>
              {/* Add more RAM options */}
            </select>
          </div>
          <div className="mb-4">
            <label className="block mb-2 font-semibold">Storage</label>
            <select
              className="p-2 w-full rounded"
              value={filter.storage}
              onChange={(e) => setFilter({ ...filter, storage: e.target.value })}
            >
              <option value="">All</option>
              <option value="64GB">64GB</option>
              <option value="128GB">128GB</option>
              {/* Add more storage options */}
            </select>
          </div>
        </aside>

        {/* Product Grid */}
        <section className="w-3/4 p-4">
          <div className="flex justify-end mb-4">
            <select
              className="p-2 rounded"
              onChange={(e) => {
                // Add sorting logic
              }}
            >
              <option value="relevance">Sort by Relevance</option>
              <option value="price">Sort by Price</option>
              {/* Add more sorting options */}
            </select>
          </div>
          <div className="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4">
            {filteredProducts.map((product) => (
              <div key={product.id} className="bg-white p-4 rounded-lg shadow-md">
                <img
                  src={product.image}
                  alt={product.name}
                  className="w-full h-40 object-cover mb-2"
                />
                <h3 className="text-lg font-bold mb-2">{product.name}</h3>
                <p>Brand: {product.brand}</p>
                <p>Price: ${product.price}</p>
                <p>RAM: {product.ram}</p>
                <p>Storage: {product.storage}</p>
              </div>
            ))}
          </div>
        </section>
      </div>

      {/* Pagination */}
      <div className="flex justify-center mt-8">
        <button className="px-4 py-2 mx-1 bg-gray-200 rounded">Previous</button>
        <button className="px-4 py-2 mx-1 bg-blue-600 text-white rounded">1</button>
        <button className="px-4 py-2 mx-1 bg-gray-200 rounded">2</button>
        <button className="px-4 py-2 mx-1 bg-gray-200 rounded">Next</button>
      </div>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

3. Product Detail Page

pages/products/[category]/[id].js

This page will display the details of a specific product, including specifications, reviews, and a gallery.

import { useRouter } from 'next/router';

export default function ProductDetail() {
  const router = useRouter();
  const { id } = router.query;

  const product = {
    id: 1,
    name: 'Phone 1',
    brand: 'Brand A',
    releaseDate: '2024-08-01',
    rating: 4.5,
    images: [
      'https://via.placeholder.com/300',
      'https://via.placeholder.com/300',
      'https://via.placeholder.com/300',
    ],
    keySpecs: {
      processor: 'Octa-core 2.8 GHz',
      ram: '6GB',
      storage: '128GB',
      display: '6.5-inch AMOLED',
      battery: '4500mAh',
      camera: '48MP + 12MP Dual',
    },
    detailedSpecs: {
      chipset: 'Snapdragon 888',
      gpu: 'Adreno 660',
      os: 'Android 12',
      dimensions: '160.8 x 74.2 x 8.4 mm',
      weight: '190g',
    },
    reviews: [
      { id: 1, user: 'John Doe', rating: 5, comment: 'Great phone!' },
      { id: 2, user: 'Jane Smith', rating: 4, comment: 'Good value for money.' },
      // More reviews...
    ],
  };

  return (
    <div className="container mx-auto p-4">
      {/* Product Header */}
      <header className="mb-8">
        <h1 className="text-3xl font-bold">{product.name}</h1>
        <p className="text-gray-600">Brand: {product.brand} | Release Date: {product.releaseDate}</p>
        <p className="text-yellow-500">Rating: {product.rating} / 5</p>
      </header>

      {/* Product Gallery */}
      <section className="mb-8">
        <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-4">
          {product.images.map((image, index) => (
            <img
              key={index}
              src={image}
              alt={`${product.name} Image ${index + 1}`}
              className="w-full h-64 object-cover rounded-lg"
            />
          ))}
        </div>
      </section>

      {/* Key Specifications */}
      <section className="mb-8">
        <h2 className="text-2xl font-bold mb-4">Key Specifications</h2>
        <ul className="grid grid-cols-1 sm:grid-cols-2 gap-4">
          <li>Processor: {product.keySpecs.processor}</li>
          <li>RAM: {product.keySpecs.ram}</li>
          <li>Storage: {product.key

Specs.storage}</li>
          <li>Display: {product.keySpecs.display}</li>
          <li>Battery: {product.keySpecs.battery}</li>
          <li>Camera: {product.keySpecs.camera}</li>
        </ul>
      </section>

      {/* Detailed Specifications */}
      <section className="mb-8">
        <h2 className="text-2xl font-bold mb-4">Detailed Specifications</h2>
        <table className="table-auto w-full bg-white rounded-lg shadow-md p-4">
          <tbody>
            <tr>
              <td className="p-2 font-semibold">Chipset</td>
              <td className="p-2">{product.detailedSpecs.chipset}</td>
            </tr>
            <tr>
              <td className="p-2 font-semibold">GPU</td>
              <td className="p-2">{product.detailedSpecs.gpu}</td>
            </tr>
            <tr>
              <td className="p-2 font-semibold">OS</td>
              <td className="p-2">{product.detailedSpecs.os}</td>
            </tr>
            <tr>
              <td className="p-2 font-semibold">Dimensions</td>
              <td className="p-2">{product.detailedSpecs.dimensions}</td>
            </tr>
            <tr>
              <td className="p-2 font-semibold">Weight</td>
              <td className="p-2">{product.detailedSpecs.weight}</td>
            </tr>
          </tbody>
        </table>
      </section>

      {/* User Reviews */}
      <section className="mb-8">
        <h2 className="text-2xl font-bold mb-4">User Reviews</h2>
        <ul>
          {product.reviews.map((review) => (
            <li key={review.id} className="mb-4">
              <p className="font-semibold">{review.user}</p>
              <p className="text-yellow-500">Rating: {review.rating} / 5</p>
              <p>{review.comment}</p>
            </li>
          ))}
        </ul>
      </section>

      {/* Buy Now/CTA */}
      <section className="mb-8">
        <button className="bg-blue-600 text-white px-4 py-2 rounded">
          Buy Now
        </button>
        <button className="bg-gray-200 text-gray-800 px-4 py-2 rounded ml-4">
          Compare
        </button>
      </section>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Explanation:

Product Listing Page:

  • Category Header: Displays the name and description of the current category.
  • Filters Sidebar: Allows filtering products by brand, price, RAM, and storage.
  • Product Grid: Displays filtered products in a grid format.
  • Pagination: Links to navigate between different pages of products.

Product Detail Page:

  • Product Header: Shows the product’s name, brand, release date, and average rating.
  • Product Gallery: A carousel of images displaying the product.
  • Key Specifications: A concise overview of essential product specs.
  • Detailed Specifications: A detailed table of technical specs.
  • User Reviews: A list of user reviews and ratings.
  • Buy Now/CTA: Buttons for purchasing the product or comparing it with others.

This code will create a clean and responsive user interface for both product listing and detail pages using Next.js and Tailwind CSS.

Here’s the frontend code for the Review Page and Blog/Article Page using Next.js and Tailwind CSS.

4. Review Page

// pages/review/[id].js
import React from 'react';
import { useRouter } from 'next/router';

export default function ReviewPage() {
  const router = useRouter();
  const { id } = router.query;

  // Sample data
  const review = {
    productImage: '/images/sample-product.jpg',
    productName: 'Sample Product',
    starRating: 4.5,
    reviewer: {
      name: 'John Doe',
      date: 'August 26, 2024',
      summary: 'A thorough and detailed review of the Sample Product.',
    },
    content: [
      {
        sectionTitle: 'Design',
        text: 'The design of this product is sleek and modern...',
        images: ['/images/design1.jpg', '/images/design2.jpg'],
      },
      {
        sectionTitle: 'Performance',
        text: 'The performance is top-notch, with no lags...',
        images: [],
      },
      // Add more sections as needed
    ],
    pros: ['Great design', 'High performance', 'Long battery life'],
    cons: ['Expensive', 'Limited color options'],
    ratingDistribution: {
      5: 80,
      4: 15,
      3: 3,
      2: 1,
      1: 1,
    },
    comments: [
      { user: 'Alice', comment: 'Great review, very helpful!' },
      { user: 'Bob', comment: 'I found the cons section to be accurate.' },
    ],
  };

  return (
    <div className="container mx-auto p-4">
      {/* Review Header */}
      <div className="flex items-center mb-8">
        <img src={review.productImage} alt={review.productName} className="w-24 h-24 mr-4" />
        <div>
          <h1 className="text-3xl font-bold">{review.productName}</h1>
          <p className="text-yellow-500">Rating: {review.starRating} / 5</p>
        </div>
      </div>

      {/* Reviewer Details */}
      <div className="mb-8">
        <p className="font-semibold">{review.reviewer.name}</p>
        <p className="text-gray-500">{review.reviewer.date}</p>
        <p>{review.reviewer.summary}</p>
      </div>

      {/* Review Content */}
      {review.content.map((section, index) => (
        <div key={index} className="mb-8">
          <h2 className="text-2xl font-bold mb-4">{section.sectionTitle}</h2>
          <p>{section.text}</p>
          <div className="flex mt-4">
            {section.images.map((image, idx) => (
              <img key={idx} src={image} alt={section.sectionTitle} className="w-1/3 h-auto mr-4" />
            ))}
          </div>
        </div>
      ))}

      {/* Pros and Cons */}
      <div className="mb-8">
        <h2 className="text-2xl font-bold mb-4">Pros and Cons</h2>
        <div className="flex">
          <div className="w-1/2">
            <h3 className="font-semibold">Pros</h3>
            <ul className="list-disc list-inside">
              {review.pros.map((pro, index) => (
                <li key={index}>{pro}</li>
              ))}
            </ul>
          </div>
          <div className="w-1/2">
            <h3 className="font-semibold">Cons</h3>
            <ul className="list-disc list-inside">
              {review.cons.map((con, index) => (
                <li key={index}>{con}</li>
              ))}
            </ul>
          </div>
        </div>
      </div>

      {/* Rating Breakdown */}
      <div className="mb-8">
        <h2 className="text-2xl font-bold mb-4">Rating Breakdown</h2>
        <div className="space-y-2">
          {Object.entries(review.ratingDistribution).map(([stars, count]) => (
            <div key={stars} className="flex items-center">
              <span className="w-12">{stars} stars</span>
              <div className="bg-gray-300 w-full h-4 rounded-md overflow-hidden">
                <div className="bg-yellow-500 h-full" style={{ width: `${count}%` }}></div>
              </div>
              <span className="ml-4">{count}%</span>
            </div>
          ))}
        </div>
      </div>

      {/* User Comments */}
      <div className="mb-8">
        <h2 className="text-2xl font-bold mb-4">User Comments</h2>
        <ul>
          {review.comments.map((comment, index) => (
            <li key={index} className="mb-4">
              <p className="font-semibold">{comment.user}</p>
              <p>{comment.comment}</p>
            </li>
          ))}
        </ul>
      </div>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

5. Blog/Article Page

// pages/blog/[id].js
import React from 'react';
import { useRouter } from 'next/router';

export default function ArticlePage() {
  const router = useRouter();
  const { id } = router.query;

  // Sample data
  const article = {
    title: 'Understanding Mobile Technology in 2024',
    author: 'Jane Smith',
    date: 'August 25, 2024',
    category: 'Technology',
    content: `
      <p>Mobile technology has advanced rapidly over the years...</p>
      <p>In 2024, we are seeing new trends like...</p>
      <!-- Add more content as needed -->
    `,
    authorBio: 'Jane Smith is a technology writer with over 10 years of experience...',
    relatedArticles: [
      { id: 2, title: 'The Future of Smartphones' },
      { id: 3, title: '5G Technology: What You Need to Know' },
    ],
  };

  return (
    <div className="container mx-auto p-4">
      {/* Article Header */}
      <div className="mb-8">
        <h1 className="text-4xl font-bold">{article.title}</h1>
        <div className="text-gray-500">
          <p>By {article.author}</p>
          <p>{article.date} - {article.category}</p>
        </div>
      </div>

      {/* Article Content */}
      <div className="prose" dangerouslySetInnerHTML={{ __html: article.content }}></div>

      {/* Social Sharing Buttons */}
      <div className="my-8">
        <button className="bg-blue-600 text-white px-4 py-2 rounded">Share on Facebook</button>
        <button className="bg-blue-400 text-white px-4 py-2 rounded ml-4">Share on Twitter</button>
      </div>

      {/* Author Bio */}
      <div className="mb-8">
        <h2 className="text-2xl font-bold mb-4">About the Author</h2>
        <p>{article.authorBio}</p>
      </div>

      {/* Comments Section */}
      <div className="mb-8">
        <h2 className="text-2xl font-bold mb-4">Comments</h2>
        {/* Add comment form or list of comments here */}
      </div>

      {/* Related Articles */}
      <div className="mb-8">
        <h2 className="text-2xl font-bold mb-4">Related Articles</h2>
        <ul>
          {article.relatedArticles.map((related) => (
            <li key={related.id}>
              <a href={`/blog/${related.id}`} className="text-blue-600">
                {related.title}
              </a>
            </li>
          ))}
        </ul>
      </div>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Explanation:

Review Page:

  • Review Header: Displays the product’s image, name, and star rating.
  • Reviewer Details: Shows the reviewer’s name, review date, and summary.
  • Review Content: Contains detailed sections like design, performance, etc., with images.
  • Pros and Cons: Lists the strengths and weaknesses of the product.
  • Rating Breakdown: Visual representation of the rating distribution.
  • User Comments: Section for displaying user comments.

Blog/Article Page:

  • Article Header: Displays the article’s title, author, date, and category.
  • Article Content: Renders rich content, including text, images, and videos.
  • Social Sharing Buttons: Buttons for sharing the article on social media.
  • Author Bio: Brief biography of the article’s author.
  • Comments Section: Placeholder for user comments.
  • Related Articles: Links to other related blog posts or articles.

This code provides a solid structure for both the review and article pages, ensuring a responsive and user-friendly interface.

Here’s the frontend code for the Comparison Page and Search Results Page using Next.js and Tailwind CSS.

6. Comparison Page

// pages/comparison.js
import React, { useState } from 'react';

export default function ComparisonPage() {
  // Sample data
  const products = [
    {
      id: 1,
      name: 'Product A',
      image: '/images/product-a.jpg',
      price: '$999',
      display: '6.5-inch OLED',
      processor: 'A15 Bionic',
      camera: '12MP + 12MP',
      battery: '4000mAh',
    },
    {
      id: 2,
      name: 'Product B',
      image: '/images/product-b.jpg',
      price: '$799',
      display: '6.1-inch LCD',
      processor: 'Snapdragon 888',
      camera: '48MP + 8MP + 2MP',
      battery: '4500mAh',
    },
    // Add more products as needed
  ];

  const [selectedProducts, setSelectedProducts] = useState(products);

  const removeProduct = (id) => {
    setSelectedProducts(selectedProducts.filter(product => product.id !== id));
  };

  return (
    <div className="container mx-auto p-4">
      <h1 className="text-3xl font-bold mb-8">Product Comparison</h1>

      {/* Comparison Table */}
      <table className="min-w-full bg-white shadow-md rounded-lg overflow-hidden">
        <thead>
          <tr className="bg-gray-200">
            <th className="px-6 py-3">Feature</th>
            {selectedProducts.map((product, index) => (
              <th key={index} className="px-6 py-3 relative">
                {product.name}
                <button
                  onClick={() => removeProduct(product.id)}
                  className="absolute top-0 right-0 text-red-500"
                >
                  Remove
                </button>
              </th>
            ))}
          </tr>
        </thead>
        <tbody>
          <tr>
            <td className="px-6 py-4 font-semibold">Image</td>
            {selectedProducts.map((product, index) => (
              <td key={index} className="px-6 py-4">
                <img src={product.image} alt={product.name} className="w-24 h-24" />
              </td>
            ))}
          </tr>
          <tr>
            <td className="px-6 py-4 font-semibold">Price</td>
            {selectedProducts.map((product, index) => (
              <td key={index} className="px-6 py-4">{product.price}</td>
            ))}
          </tr>
          <tr>
            <td className="px-6 py-4 font-semibold">Display</td>
            {selectedProducts.map((product, index) => (
              <td key={index} className="px-6 py-4">{product.display}</td>
            ))}
          </tr>
          <tr>
            <td className="px-6 py-4 font-semibold">Processor</td>
            {selectedProducts.map((product, index) => (
              <td key={index} className="px-6 py-4">{product.processor}</td>
            ))}
          </tr>
          <tr>
            <td className="px-6 py-4 font-semibold">Camera</td>
            {selectedProducts.map((product, index) => (
              <td key={index} className="px-6 py-4">{product.camera}</td>
            ))}
          </tr>
          <tr>
            <td className="px-6 py-4 font-semibold">Battery</td>
            {selectedProducts.map((product, index) => (
              <td key={index} className="px-6 py-4">{product.battery}</td>
            ))}
          </tr>
        </tbody>
      </table>

      {/* Quick Summary */}
      <div className="mt-8">
        <h2 className="text-2xl font-bold mb-4">Quick Summary</h2>
        <p>Product A has a better display and processor, while Product B offers a better camera and battery life.</p>
      </div>

      {/* CTA */}
      <div className="mt-8">
        <a href={`/product/${selectedProducts[0]?.id}`} className="text-blue-600 underline">
          View Product A Details
        </a>
        <a href={`/product/${selectedProducts[1]?.id}`} className="text-blue-600 underline ml-4">
          View Product B Details
        </a>
      </div>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

7. Search Results Page

// pages/search.js
import React, { useState } from 'react';

export default function SearchResultsPage() {
  // Sample data
  const [searchQuery] = useState('iPhone');
  const results = [
    {
      id: 1,
      name: 'iPhone 13',
      description: 'Latest model with A15 Bionic chip, 5G, and improved cameras.',
      image: '/images/iphone-13.jpg',
    },
    {
      id: 2,
      name: 'iPhone 12',
      description: 'Previous generation, still a powerful device with A14 Bionic chip.',
      image: '/images/iphone-12.jpg',
    },
    // Add more results as needed
  ];

  return (
    <div className="container mx-auto p-4">
      <h1 className="text-3xl font-bold mb-4">Search Results for "{searchQuery}"</h1>
      <p className="text-gray-600 mb-8">{results.length} results found</p>

      {/* Filters and Sorting Options */}
      <div className="mb-8">
        <button className="bg-blue-600 text-white px-4 py-2 rounded mr-4">Sort by Relevance</button>
        <button className="bg-blue-600 text-white px-4 py-2 rounded">Filter Results</button>
      </div>

      {/* Search Results List/Grid */}
      <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
        {results.map((result) => (
          <div key={result.id} className="border p-4 rounded-lg shadow-md">
            <img src={result.image} alt={result.name} className="w-full h-48 object-cover mb-4" />
            <h2 className="text-2xl font-bold">{result.name}</h2>
            <p className="text-gray-700">{result.description}</p>
            <a href={`/product/${result.id}`} className="text-blue-600 underline mt-4 inline-block">
              View Details
            </a>
          </div>
        ))}
      </div>

      {/* Pagination */}
      <div className="mt-8">
        <button className="bg-gray-300 text-gray-700 px-4 py-2 rounded mr-2">Previous</button>
        <button className="bg-gray-300 text-gray-700 px-4 py-2 rounded">Next</button>
      </div>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Explanation:

Comparison Page:

  • Comparison Table: Displays the selected products with specifications like price, display, processor, camera, and battery.
  • Add/Remove Products: Users can remove products from the comparison table.
  • Quick Summary: Provides a brief summary of the key differences and similarities between the products.
  • CTA: Links to individual product detail pages for more information.

Search Results Page:

  • Search Query Display: Shows what the user searched for and the number of results found.
  • Filters and Sorting Options: Buttons to sort and filter search results.
  • Search Results List/Grid: Displays the search results with product images, names, descriptions, and links to detailed pages.
  • Pagination: Provides navigation for multiple pages of search results.

This code structure is designed to create a responsive and user-friendly interface for both the comparison and search results pages.

Here’s the frontend code for the About Us Page, Contact Us Page, User Profile Page, and FAQ Page using Next.js and Tailwind CSS.

8. About Us Page

// pages/about.js
import React from 'react';

export default function AboutUsPage() {
  const teamMembers = [
    {
      name: 'John Doe',
      role: 'Founder & CEO',
      bio: 'John is the visionary behind the website, leading the team with a passion for technology.',
      image: '/images/john-doe.jpg',
    },
    {
      name: 'Jane Smith',
      role: 'Lead Developer',
      bio: 'Jane oversees all technical aspects, ensuring a smooth user experience.',
      image: '/images/jane-smith.jpg',
    },
    // Add more team members as needed
  ];

  return (
    <div className="container mx-auto p-4">
      <h1 className="text-3xl font-bold mb-8">About Us</h1>

      {/* Mission Statement */}
      <section className="mb-12">
        <h2 className="text-2xl font-bold mb-4">Our Mission</h2>
        <p>
          Our mission is to provide users with comprehensive and accurate mobile device information, reviews, and comparisons, helping them make informed decisions.
        </p>
      </section>

      {/* Team Profiles */}
      <section className="mb-12">
        <h2 className="text-2xl font-bold mb-4">Meet the Team</h2>
        <div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-8">
          {teamMembers.map((member, index) => (
            <div key={index} className="text-center">
              <img
                src={member.image}
                alt={member.name}
                className="w-32 h-32 rounded-full mx-auto mb-4"
              />
              <h3 className="text-xl font-bold">{member.name}</h3>
              <p className="text-gray-600">{member.role}</p>
              <p className="mt-2">{member.bio}</p>
            </div>
          ))}
        </div>
      </section>

      {/* History */}
      <section className="mb-12">
        <h2 className="text-2xl font-bold mb-4">Our History</h2>
        <p>
          Founded in 2022, our website started as a small project aimed at providing users with detailed mobile phone specifications. Over time, we have expanded our content to include reviews, comparisons, and a broader range of tech products.
        </p>
      </section>

      {/* Contact Information */}
      <section>
        <h2 className="text-2xl font-bold mb-4">Contact Us</h2>
        <p>Email: contact@website.com</p>
        <p>Follow us on social media:</p>
        <div className="flex space-x-4 mt-4">
          <a href="#" className="text-blue-600">Facebook</a>
          <a href="#" className="text-blue-600">Twitter</a>
          <a href="#" className="text-blue-600">Instagram</a>
        </div>
      </section>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

9. Contact Us Page

// pages/contact.js
import React, { useState } from 'react';

export default function ContactUsPage() {
  const [form, setForm] = useState({
    name: '',
    email: '',
    subject: '',
    message: '',
  });

  const handleChange = (e) => {
    setForm({ ...form, [e.target.name]: e.target.value });
  };

  const handleSubmit = (e) => {
    e.preventDefault();
    // Handle form submission
    console.log(form);
  };

  return (
    <div className="container mx-auto p-4">
      <h1 className="text-3xl font-bold mb-8">Contact Us</h1>

      {/* Contact Form */}
      <form onSubmit={handleSubmit} className="mb-12">
        <div className="mb-4">
          <label className="block text-lg font-semibold mb-2">Name</label>
          <input
            type="text"
            name="name"
            value={form.name}
            onChange={handleChange}
            className="w-full p-2 border rounded"
            required
          />
        </div>
        <div className="mb-4">
          <label className="block text-lg font-semibold mb-2">Email</label>
          <input
            type="email"
            name="email"
            value={form.email}
            onChange={handleChange}
            className="w-full p-2 border rounded"
            required
          />
        </div>
        <div className="mb-4">
          <label className="block text-lg font-semibold mb-2">Subject</label>
          <input
            type="text"
            name="subject"
            value={form.subject}
            onChange={handleChange}
            className="w-full p-2 border rounded"
            required
          />
        </div>
        <div className="mb-4">
          <label className="block text-lg font-semibold mb-2">Message</label>
          <textarea
            name="message"
            value={form.message}
            onChange={handleChange}
            className="w-full p-2 border rounded"
            required
          ></textarea>
        </div>
        <button type="submit" className="bg-blue-600 text-white px-4 py-2 rounded">Submit</button>
      </form>

      {/* Contact Information */}
      <section>
        <h2 className="text-2xl font-bold mb-4">Contact Information</h2>
        <p>Email: contact@website.com</p>
        <p>Phone: (123) 456-7890</p>
        <p>Address: 123 Main Street, Anytown, USA</p>
      </section>

      {/* Social Media Links */}
      <section className="mt-8">
        <h2 className="text-2xl font-bold mb-4">Follow Us</h2>
        <div className="flex space-x-4">
          <a href="#" className="text-blue-600">Facebook</a>
          <a href="#" className="text-blue-600">Twitter</a>
          <a href="#" className="text-blue-600">Instagram</a>
        </div>
      </section>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

10. User Profile Page

// pages/profile.js
import React, { useState } from 'react';

export default function UserProfilePage() {
  const [userInfo, setUserInfo] = useState({
    name: 'John Doe',
    email: 'john@example.com',
  });

  const [activityLog] = useState([
    { id: 1, type: 'Review', content: 'Reviewed iPhone 13' },
    { id: 2, type: 'Comment', content: 'Commented on Galaxy S21 review' },
    // Add more activities as needed
  ]);

  const [savedItems] = useState([
    { id: 1, name: 'iPhone 13', link: '/product/iphone-13' },
    { id: 2, name: 'Galaxy S21', link: '/product/galaxy-s21' },
    // Add more saved items as needed
  ]);

  const handleUpdateProfile = (e) => {
    e.preventDefault();
    // Handle profile update
    console.log('Profile updated:', userInfo);
  };

  return (
    <div className="container mx-auto p-4">
      <h1 className="text-3xl font-bold mb-8">User Profile</h1>

      {/* Profile Information */}
      <section className="mb-12">
        <h2 className="text-2xl font-bold mb-4">Profile Information</h2>
        <form onSubmit={handleUpdateProfile}>
          <div className="mb-4">
            <label className="block text-lg font-semibold mb-2">Name</label>
            <input
              type="text"
              name="name"
              value={userInfo.name}
              onChange={(e) => setUserInfo({ ...userInfo, name: e.target.value })}
              className="w-full p-2 border rounded"
            />
          </div>
          <div className="mb-4">
            <label className="block text-lg font-semibold mb-2">Email</label>
            <input
              type="email"
              name="email"
              value={userInfo.email}
              onChange={(e) => setUserInfo({ ...userInfo, email: e.target.value })}
              className="w-full p-2 border rounded"
            />
          </div>
          <button type="submit" className="bg-blue-600 text-white px-4 py-2 rounded">Update Profile</button>
        </form>
      </section>

      {/* Activity Log */}
      <section className="mb-12">
        <h2 className="text-2xl font-bold mb-4">Activity Log</h2>
        <ul>
          {activityLog.map((activity) => (
            <li key={activity.id} className="mb-2">
              {activity.type}:

 {activity.content}
            </li>
          ))}
        </ul>
      </section>

      {/* Saved Products/Articles */}
      <section>
        <h2 className="text-2xl font-bold mb-4">Saved Products/Articles</h2>
        <ul>
          {savedItems.map((item) => (
            <li key={item.id} className="mb-2">
              <a href={item.link} className="text-blue-600 underline">
                {item.name}
              </a>
            </li>
          ))}
        </ul>
      </section>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

11. FAQ Page

// pages/faq.js
import React, { useState } from 'react';

export default function FAQPage() {
  const [faqs] = useState([
    {
      question: 'How do I create an account?',
      answer: 'To create an account, click on the "Sign Up" button at the top right corner and fill out the registration form.',
    },
    {
      question: 'How do I reset my password?',
      answer: 'Click on the "Forgot Password" link on the login page and follow the instructions to reset your password.',
    },
    // Add more FAQs as needed
  ]);

  const [searchTerm, setSearchTerm] = useState('');

  const filteredFAQs = faqs.filter((faq) =>
    faq.question.toLowerCase().includes(searchTerm.toLowerCase())
  );

  return (
    <div className="container mx-auto p-4">
      <h1 className="text-3xl font-bold mb-8">Frequently Asked Questions</h1>

      {/* Search Bar */}
      <input
        type="text"
        placeholder="Search FAQs..."
        value={searchTerm}
        onChange={(e) => setSearchTerm(e.target.value)}
        className="w-full p-2 border rounded mb-8"
      />

      {/* Accordion Style FAQ */}
      <div className="space-y-4">
        {filteredFAQs.map((faq, index) => (
          <details key={index} className="border p-4 rounded">
            <summary className="font-semibold cursor-pointer">{faq.question}</summary>
            <p className="mt-2">{faq.answer}</p>
          </details>
        ))}
      </div>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

Explanation:

About Us Page:

  • Mission Statement: Describes the website's purpose and goals.
  • Team Profiles: Displays team members with their photos, roles, and bios.
  • History: Shares the background of how the website started and evolved.
  • Contact Information: Provides email and social media links for users to connect with the team.

Contact Us Page:

  • Contact Form: Allows users to submit their name, email, subject, and message.
  • Contact Information: Displays email, phone number, and address.
  • Social Media Links: Links to the website's social media profiles.

User Profile Page:

  • Profile Information: Allows users to view and edit their profile details like name and email.
  • Activity Log: Shows a list of the user’s reviews, comments, and articles.
  • Saved Products/Articles: Section for users to access their saved or bookmarked products and articles.

FAQ Page:

  • Accordion Style FAQ: Displays a list of frequently asked questions with expandable answers.
  • Search Bar: Allows users to search for specific questions or topics within the FAQs.

These pages are structured to be responsive, user-friendly, and easy to navigate. You can further customize and enhance them based on your project’s specific needs.

If you enjoy my content and would like to support my work, you can buy me a coffee. Your support is greatly appreciated!

Disclaimer: This content is generated by AI.

Top comments (0)