prodycrts

This commit is contained in:
Phuoc Nguyen
2025-10-20 15:56:34 +07:00
parent e321e9a419
commit f95fa9d0a6
40 changed files with 3123 additions and 447 deletions

View File

@@ -0,0 +1,71 @@
/// Domain Entity: Category
///
/// Pure business entity representing a product category.
library;
/// Category Entity
///
/// Represents a product category for filtering and organization.
class Category {
/// Unique identifier
final String id;
/// Category name (Vietnamese)
final String name;
/// Category description
final String description;
/// Icon name or emoji
final String icon;
/// Display order
final int order;
const Category({
required this.id,
required this.name,
required this.description,
required this.icon,
required this.order,
});
/// Copy with method
Category copyWith({
String? id,
String? name,
String? description,
String? icon,
int? order,
}) {
return Category(
id: id ?? this.id,
name: name ?? this.name,
description: description ?? this.description,
icon: icon ?? this.icon,
order: order ?? this.order,
);
}
@override
String toString() {
return 'Category(id: $id, name: $name, order: $order)';
}
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
return other is Category &&
other.id == id &&
other.name == name &&
other.description == description &&
other.icon == icon &&
other.order == order;
}
@override
int get hashCode {
return Object.hash(id, name, description, icon, order);
}
}

View File

@@ -0,0 +1,156 @@
/// Domain Entity: Product
///
/// Pure business entity representing a product in the catalog.
/// This entity is framework-independent and contains only business logic.
library;
/// Product Entity
///
/// Represents a tile/construction product in the application.
/// Used across all layers but originates in the domain layer.
class Product {
/// Unique identifier
final String id;
/// Product name (Vietnamese)
final String name;
/// Product SKU (Stock Keeping Unit)
final String sku;
/// Product description
final String description;
/// Price per unit (VND)
final double price;
/// Unit of measurement (e.g., "m²", "viên", "hộp")
final String unit;
/// Product image URL
final String imageUrl;
/// Category ID
final String categoryId;
/// Stock availability
final bool inStock;
/// Stock quantity
final int stockQuantity;
/// Created date
final DateTime createdAt;
/// Optional sale price
final double? salePrice;
/// Optional brand name
final String? brand;
const Product({
required this.id,
required this.name,
required this.sku,
required this.description,
required this.price,
required this.unit,
required this.imageUrl,
required this.categoryId,
required this.inStock,
required this.stockQuantity,
required this.createdAt,
this.salePrice,
this.brand,
});
/// Get effective price (sale price if available, otherwise regular price)
double get effectivePrice => salePrice ?? price;
/// Check if product is on sale
bool get isOnSale => salePrice != null && salePrice! < price;
/// Get discount percentage
int get discountPercentage {
if (!isOnSale) return 0;
return (((price - salePrice!) / price) * 100).round();
}
/// Check if stock is low (less than 10 items)
bool get isLowStock => inStock && stockQuantity < 10;
/// Copy with method for creating modified copies
Product copyWith({
String? id,
String? name,
String? sku,
String? description,
double? price,
String? unit,
String? imageUrl,
String? categoryId,
bool? inStock,
int? stockQuantity,
DateTime? createdAt,
double? salePrice,
String? brand,
}) {
return Product(
id: id ?? this.id,
name: name ?? this.name,
sku: sku ?? this.sku,
description: description ?? this.description,
price: price ?? this.price,
unit: unit ?? this.unit,
imageUrl: imageUrl ?? this.imageUrl,
categoryId: categoryId ?? this.categoryId,
inStock: inStock ?? this.inStock,
stockQuantity: stockQuantity ?? this.stockQuantity,
createdAt: createdAt ?? this.createdAt,
salePrice: salePrice ?? this.salePrice,
brand: brand ?? this.brand,
);
}
@override
String toString() {
return 'Product(id: $id, name: $name, sku: $sku, price: $price, unit: $unit, inStock: $inStock)';
}
@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
return other is Product &&
other.id == id &&
other.name == name &&
other.sku == sku &&
other.description == description &&
other.price == price &&
other.unit == unit &&
other.imageUrl == imageUrl &&
other.categoryId == categoryId &&
other.inStock == inStock &&
other.stockQuantity == stockQuantity &&
other.salePrice == salePrice &&
other.brand == brand;
}
@override
int get hashCode {
return Object.hash(
id,
name,
sku,
description,
price,
unit,
imageUrl,
categoryId,
inStock,
stockQuantity,
salePrice,
brand,
);
}
}

View File

@@ -0,0 +1,44 @@
/// Domain Repository Interface: Products Repository
///
/// Defines the contract for product data operations.
/// The data layer will implement this interface.
library;
import 'package:worker/features/products/domain/entities/category.dart';
import 'package:worker/features/products/domain/entities/product.dart';
/// Products Repository Interface
///
/// Abstract repository defining product data operations.
/// Implemented by the data layer to provide actual data access logic.
abstract class ProductsRepository {
/// Get all products
///
/// Returns a list of all available products.
/// Throws an exception if the operation fails.
Future<List<Product>> getAllProducts();
/// Search products by query
///
/// [query] - Search term to filter products
/// Returns filtered list of products matching the query.
Future<List<Product>> searchProducts(String query);
/// Get products by category
///
/// [categoryId] - Category ID to filter by
/// Returns list of products in the specified category.
Future<List<Product>> getProductsByCategory(String categoryId);
/// Get product by ID
///
/// [id] - Product ID
/// Returns the product with the specified ID.
/// Throws an exception if product not found.
Future<Product> getProductById(String id);
/// Get all categories
///
/// Returns a list of all product categories.
Future<List<Category>> getCategories();
}

View File

@@ -0,0 +1,28 @@
/// Use Case: Get Categories
///
/// Business logic for retrieving product categories.
library;
import 'package:worker/features/products/domain/entities/category.dart';
import 'package:worker/features/products/domain/repositories/products_repository.dart';
/// Get Categories Use Case
///
/// Retrieves all product categories from the repository.
class GetCategories {
final ProductsRepository repository;
const GetCategories(this.repository);
/// Execute the use case
///
/// Returns list of all categories sorted by order
Future<List<Category>> call() async {
final categories = await repository.getCategories();
// Sort by display order
categories.sort((a, b) => a.order.compareTo(b.order));
return categories;
}
}

View File

@@ -0,0 +1,28 @@
/// Use Case: Get Products
///
/// Business logic for retrieving products with optional filtering.
library;
import 'package:worker/features/products/domain/entities/product.dart';
import 'package:worker/features/products/domain/repositories/products_repository.dart';
/// Get Products Use Case
///
/// Retrieves products from the repository with optional category filtering.
class GetProducts {
final ProductsRepository repository;
const GetProducts(this.repository);
/// Execute the use case
///
/// [categoryId] - Optional category ID to filter products
/// Returns list of products (all or filtered by category)
Future<List<Product>> call({String? categoryId}) async {
if (categoryId == null || categoryId == 'all') {
return await repository.getAllProducts();
} else {
return await repository.getProductsByCategory(categoryId);
}
}
}

View File

@@ -0,0 +1,29 @@
/// Use Case: Search Products
///
/// Business logic for searching products by query string.
library;
import 'package:worker/features/products/domain/entities/product.dart';
import 'package:worker/features/products/domain/repositories/products_repository.dart';
/// Search Products Use Case
///
/// Searches for products matching the given query string.
class SearchProducts {
final ProductsRepository repository;
const SearchProducts(this.repository);
/// Execute the use case
///
/// [query] - Search query string
/// Returns list of products matching the query
Future<List<Product>> call(String query) async {
// Return all products if query is empty
if (query.trim().isEmpty) {
return await repository.getAllProducts();
}
return await repository.searchProducts(query);
}
}