prodycrts
This commit is contained in:
@@ -0,0 +1,294 @@
|
||||
/// Data Source: Products Local Data Source
|
||||
///
|
||||
/// Provides mock product and category data for development.
|
||||
/// In production, this would be replaced with actual API calls.
|
||||
library;
|
||||
|
||||
import 'package:worker/features/products/data/models/category_model.dart';
|
||||
import 'package:worker/features/products/data/models/product_model.dart';
|
||||
|
||||
/// Products Local Data Source Interface
|
||||
abstract class ProductsLocalDataSource {
|
||||
Future<List<ProductModel>> getAllProducts();
|
||||
Future<List<ProductModel>> searchProducts(String query);
|
||||
Future<List<ProductModel>> getProductsByCategory(String categoryId);
|
||||
Future<ProductModel> getProductById(String id);
|
||||
Future<List<CategoryModel>> getCategories();
|
||||
}
|
||||
|
||||
/// Products Local Data Source Implementation
|
||||
///
|
||||
/// Provides mock data for products and categories.
|
||||
/// Simulates async operations with delays.
|
||||
class ProductsLocalDataSourceImpl implements ProductsLocalDataSource {
|
||||
const ProductsLocalDataSourceImpl();
|
||||
|
||||
/// Mock categories data
|
||||
static final List<Map<String, dynamic>> _categoriesJson = [
|
||||
{
|
||||
'id': 'all',
|
||||
'name': 'Tất cả',
|
||||
'description': 'Tất cả sản phẩm',
|
||||
'icon': '📦',
|
||||
'order': 0,
|
||||
},
|
||||
{
|
||||
'id': 'floor_tiles',
|
||||
'name': 'Gạch lát nền',
|
||||
'description': 'Gạch lát nền cao cấp',
|
||||
'icon': '🏠',
|
||||
'order': 1,
|
||||
},
|
||||
{
|
||||
'id': 'wall_tiles',
|
||||
'name': 'Gạch ốp tường',
|
||||
'description': 'Gạch ốp tường chất lượng',
|
||||
'icon': '🧱',
|
||||
'order': 2,
|
||||
},
|
||||
{
|
||||
'id': 'decorative_tiles',
|
||||
'name': 'Gạch trang trí',
|
||||
'description': 'Gạch trang trí nghệ thuật',
|
||||
'icon': '✨',
|
||||
'order': 3,
|
||||
},
|
||||
{
|
||||
'id': 'outdoor_tiles',
|
||||
'name': 'Gạch ngoài trời',
|
||||
'description': 'Gạch chống trượt ngoài trời',
|
||||
'icon': '🌳',
|
||||
'order': 4,
|
||||
},
|
||||
{
|
||||
'id': 'accessories',
|
||||
'name': 'Phụ kiện',
|
||||
'description': 'Phụ kiện xây dựng',
|
||||
'icon': '🔧',
|
||||
'order': 5,
|
||||
},
|
||||
];
|
||||
|
||||
/// Mock products data
|
||||
static final List<Map<String, dynamic>> _productsJson = [
|
||||
{
|
||||
'id': 'prod_001',
|
||||
'name': 'Gạch men cao cấp 60x60',
|
||||
'sku': 'GM-60-001',
|
||||
'description': 'Gạch men bóng kiếng cao cấp, chống trượt, độ bền cao. Phù hợp cho phòng khách, phòng ngủ.',
|
||||
'price': 450000.0,
|
||||
'unit': 'm²',
|
||||
'imageUrl': 'https://images.unsplash.com/photo-1615971677499-5467cbfe1f10?w=400',
|
||||
'categoryId': 'floor_tiles',
|
||||
'inStock': true,
|
||||
'stockQuantity': 150,
|
||||
'createdAt': '2024-01-15T08:00:00Z',
|
||||
'salePrice': null,
|
||||
'brand': 'Eurotile',
|
||||
},
|
||||
{
|
||||
'id': 'prod_002',
|
||||
'name': 'Gạch granite nhập khẩu',
|
||||
'sku': 'GR-80-002',
|
||||
'description': 'Gạch granite nhập khẩu Tây Ban Nha, vân đá tự nhiên, sang trọng. Kích thước 80x80cm.',
|
||||
'price': 680000.0,
|
||||
'unit': 'm²',
|
||||
'imageUrl': 'https://images.unsplash.com/photo-1565183928294-7d22e855a326?w=400',
|
||||
'categoryId': 'floor_tiles',
|
||||
'inStock': true,
|
||||
'stockQuantity': 80,
|
||||
'createdAt': '2024-01-20T10:30:00Z',
|
||||
'salePrice': 620000.0,
|
||||
'brand': 'Vasta Stone',
|
||||
},
|
||||
{
|
||||
'id': 'prod_003',
|
||||
'name': 'Gạch mosaic trang trí',
|
||||
'sku': 'MS-30-003',
|
||||
'description': 'Gạch mosaic thủy tinh màu sắc đa dạng, tạo điểm nhấn cho không gian. Kích thước 30x30cm.',
|
||||
'price': 320000.0,
|
||||
'unit': 'm²',
|
||||
'imageUrl': 'https://images.unsplash.com/photo-1604709177225-055f99402ea3?w=400',
|
||||
'categoryId': 'decorative_tiles',
|
||||
'inStock': true,
|
||||
'stockQuantity': 45,
|
||||
'createdAt': '2024-02-01T14:15:00Z',
|
||||
'salePrice': null,
|
||||
'brand': 'Eurotile',
|
||||
},
|
||||
{
|
||||
'id': 'prod_004',
|
||||
'name': 'Gạch 3D họa tiết',
|
||||
'sku': '3D-60-004',
|
||||
'description': 'Gạch 3D với họa tiết nổi độc đáo, tạo hiệu ứng thị giác ấn tượng cho tường phòng khách.',
|
||||
'price': 750000.0,
|
||||
'unit': 'm²',
|
||||
'imageUrl': 'https://images.unsplash.com/photo-1600585152220-90363fe7e115?w=400',
|
||||
'categoryId': 'wall_tiles',
|
||||
'inStock': true,
|
||||
'stockQuantity': 30,
|
||||
'createdAt': '2024-02-10T09:00:00Z',
|
||||
'salePrice': 680000.0,
|
||||
'brand': 'Vasta Stone',
|
||||
},
|
||||
{
|
||||
'id': 'prod_005',
|
||||
'name': 'Gạch ceramic chống trượt',
|
||||
'sku': 'CR-40-005',
|
||||
'description': 'Gạch ceramic chống trượt cấp độ R11, an toàn cho phòng tắm và ban công. Kích thước 40x40cm.',
|
||||
'price': 380000.0,
|
||||
'unit': 'm²',
|
||||
'imageUrl': 'https://images.unsplash.com/photo-1600607687939-ce8a6c25118c?w=400',
|
||||
'categoryId': 'outdoor_tiles',
|
||||
'inStock': true,
|
||||
'stockQuantity': 8,
|
||||
'createdAt': '2024-02-15T11:20:00Z',
|
||||
'salePrice': null,
|
||||
'brand': 'Eurotile',
|
||||
},
|
||||
{
|
||||
'id': 'prod_006',
|
||||
'name': 'Gạch terrazzo đá mài',
|
||||
'sku': 'TZ-60-006',
|
||||
'description': 'Gạch terrazzo phong cách retro, đá mài hạt màu, độc đáo và bền đẹp theo thời gian.',
|
||||
'price': 890000.0,
|
||||
'unit': 'm²',
|
||||
'imageUrl': 'https://images.unsplash.com/photo-1600566753190-17f0baa2a6c3?w=400',
|
||||
'categoryId': 'decorative_tiles',
|
||||
'inStock': true,
|
||||
'stockQuantity': 25,
|
||||
'createdAt': '2024-02-20T15:45:00Z',
|
||||
'salePrice': 820000.0,
|
||||
'brand': 'Vasta Stone',
|
||||
},
|
||||
{
|
||||
'id': 'prod_007',
|
||||
'name': 'Gạch ốp tường bếp',
|
||||
'sku': 'OT-30-007',
|
||||
'description': 'Gạch ốp tường nhà bếp, dễ lau chùi, chống thấm tốt. Kích thước 30x60cm.',
|
||||
'price': 280000.0,
|
||||
'unit': 'm²',
|
||||
'imageUrl': 'https://images.unsplash.com/photo-1600047509807-ba8f99d2cdde?w=400',
|
||||
'categoryId': 'wall_tiles',
|
||||
'inStock': true,
|
||||
'stockQuantity': 120,
|
||||
'createdAt': '2024-03-01T08:30:00Z',
|
||||
'salePrice': null,
|
||||
'brand': 'Eurotile',
|
||||
},
|
||||
{
|
||||
'id': 'prod_008',
|
||||
'name': 'Gạch sân vườn chống rêu',
|
||||
'sku': 'SV-50-008',
|
||||
'description': 'Gạch lát sân vườn chống rêu mốc, bền với thời tiết. Kích thước 50x50cm.',
|
||||
'price': 420000.0,
|
||||
'unit': 'm²',
|
||||
'imageUrl': 'https://images.unsplash.com/photo-1600566752355-35792bedcfea?w=400',
|
||||
'categoryId': 'outdoor_tiles',
|
||||
'inStock': true,
|
||||
'stockQuantity': 65,
|
||||
'createdAt': '2024-03-05T10:00:00Z',
|
||||
'salePrice': 380000.0,
|
||||
'brand': 'Vasta Stone',
|
||||
},
|
||||
{
|
||||
'id': 'prod_009',
|
||||
'name': 'Keo dán gạch chuyên dụng',
|
||||
'sku': 'ACC-KD-009',
|
||||
'description': 'Keo dán gạch chất lượng cao, độ bám dính mạnh, chống thấm. Bao 25kg.',
|
||||
'price': 180000.0,
|
||||
'unit': 'bao',
|
||||
'imageUrl': 'https://images.unsplash.com/photo-1581094794329-c8112a89af12?w=400',
|
||||
'categoryId': 'accessories',
|
||||
'inStock': true,
|
||||
'stockQuantity': 200,
|
||||
'createdAt': '2024-03-10T13:15:00Z',
|
||||
'salePrice': null,
|
||||
'brand': 'Eurotile',
|
||||
},
|
||||
{
|
||||
'id': 'prod_010',
|
||||
'name': 'Keo chà ron màu',
|
||||
'sku': 'ACC-KCR-010',
|
||||
'description': 'Keo chà ron gạch nhiều màu sắc, chống thấm, chống nấm mốc. Bao 5kg.',
|
||||
'price': 120000.0,
|
||||
'unit': 'bao',
|
||||
'imageUrl': 'https://images.unsplash.com/photo-1621905251918-48416bd8575a?w=400',
|
||||
'categoryId': 'accessories',
|
||||
'inStock': true,
|
||||
'stockQuantity': 150,
|
||||
'createdAt': '2024-03-15T09:45:00Z',
|
||||
'salePrice': 99000.0,
|
||||
'brand': 'Vasta Stone',
|
||||
},
|
||||
];
|
||||
|
||||
@override
|
||||
Future<List<ProductModel>> getAllProducts() async {
|
||||
// Simulate network delay
|
||||
await Future.delayed(const Duration(milliseconds: 500));
|
||||
|
||||
return _productsJson
|
||||
.map((json) => ProductModel.fromJson(json))
|
||||
.toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<ProductModel>> searchProducts(String query) async {
|
||||
// Simulate network delay
|
||||
await Future.delayed(const Duration(milliseconds: 300));
|
||||
|
||||
final lowercaseQuery = query.toLowerCase();
|
||||
|
||||
final filtered = _productsJson.where((product) {
|
||||
final name = (product['name'] as String).toLowerCase();
|
||||
final sku = (product['sku'] as String).toLowerCase();
|
||||
final description = (product['description'] as String).toLowerCase();
|
||||
|
||||
return name.contains(lowercaseQuery) ||
|
||||
sku.contains(lowercaseQuery) ||
|
||||
description.contains(lowercaseQuery);
|
||||
}).toList();
|
||||
|
||||
return filtered.map((json) => ProductModel.fromJson(json)).toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<ProductModel>> getProductsByCategory(String categoryId) async {
|
||||
// Simulate network delay
|
||||
await Future.delayed(const Duration(milliseconds: 400));
|
||||
|
||||
if (categoryId == 'all') {
|
||||
return getAllProducts();
|
||||
}
|
||||
|
||||
final filtered = _productsJson
|
||||
.where((product) => product['categoryId'] == categoryId)
|
||||
.toList();
|
||||
|
||||
return filtered.map((json) => ProductModel.fromJson(json)).toList();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<ProductModel> getProductById(String id) async {
|
||||
// Simulate network delay
|
||||
await Future.delayed(const Duration(milliseconds: 200));
|
||||
|
||||
final productJson = _productsJson.firstWhere(
|
||||
(product) => product['id'] == id,
|
||||
orElse: () => throw Exception('Product not found: $id'),
|
||||
);
|
||||
|
||||
return ProductModel.fromJson(productJson);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<CategoryModel>> getCategories() async {
|
||||
// Simulate network delay
|
||||
await Future.delayed(const Duration(milliseconds: 300));
|
||||
|
||||
return _categoriesJson
|
||||
.map((json) => CategoryModel.fromJson(json))
|
||||
.toList();
|
||||
}
|
||||
}
|
||||
131
lib/features/products/data/models/category_model.dart
Normal file
131
lib/features/products/data/models/category_model.dart
Normal file
@@ -0,0 +1,131 @@
|
||||
/// Data Model: Category
|
||||
///
|
||||
/// Data Transfer Object for category information.
|
||||
library;
|
||||
|
||||
import 'package:hive_ce/hive.dart';
|
||||
import 'package:worker/features/products/domain/entities/category.dart';
|
||||
|
||||
part 'category_model.g.dart';
|
||||
|
||||
/// Category Model
|
||||
///
|
||||
/// Used for:
|
||||
/// - JSON serialization/deserialization
|
||||
/// - Hive local database storage
|
||||
/// - Converting to/from domain entity
|
||||
///
|
||||
/// Hive Type ID: 12
|
||||
@HiveType(typeId: 12)
|
||||
class CategoryModel extends HiveObject {
|
||||
/// Unique identifier
|
||||
@HiveField(0)
|
||||
final String id;
|
||||
|
||||
/// Category name
|
||||
@HiveField(1)
|
||||
final String name;
|
||||
|
||||
/// Category description
|
||||
@HiveField(2)
|
||||
final String description;
|
||||
|
||||
/// Icon name or emoji
|
||||
@HiveField(3)
|
||||
final String icon;
|
||||
|
||||
/// Display order
|
||||
@HiveField(4)
|
||||
final int order;
|
||||
|
||||
CategoryModel({
|
||||
required this.id,
|
||||
required this.name,
|
||||
required this.description,
|
||||
required this.icon,
|
||||
required this.order,
|
||||
});
|
||||
|
||||
/// From JSON constructor
|
||||
factory CategoryModel.fromJson(Map<String, dynamic> json) {
|
||||
return CategoryModel(
|
||||
id: json['id'] as String,
|
||||
name: json['name'] as String,
|
||||
description: json['description'] as String,
|
||||
icon: json['icon'] as String,
|
||||
order: json['order'] as int,
|
||||
);
|
||||
}
|
||||
|
||||
/// To JSON method
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'id': id,
|
||||
'name': name,
|
||||
'description': description,
|
||||
'icon': icon,
|
||||
'order': order,
|
||||
};
|
||||
}
|
||||
|
||||
/// Convert to domain entity
|
||||
Category toEntity() {
|
||||
return Category(
|
||||
id: id,
|
||||
name: name,
|
||||
description: description,
|
||||
icon: icon,
|
||||
order: order,
|
||||
);
|
||||
}
|
||||
|
||||
/// Create from domain entity
|
||||
factory CategoryModel.fromEntity(Category entity) {
|
||||
return CategoryModel(
|
||||
id: entity.id,
|
||||
name: entity.name,
|
||||
description: entity.description,
|
||||
icon: entity.icon,
|
||||
order: entity.order,
|
||||
);
|
||||
}
|
||||
|
||||
/// Copy with method
|
||||
CategoryModel copyWith({
|
||||
String? id,
|
||||
String? name,
|
||||
String? description,
|
||||
String? icon,
|
||||
int? order,
|
||||
}) {
|
||||
return CategoryModel(
|
||||
id: id ?? this.id,
|
||||
name: name ?? this.name,
|
||||
description: description ?? this.description,
|
||||
icon: icon ?? this.icon,
|
||||
order: order ?? this.order,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
return 'CategoryModel(id: $id, name: $name, order: $order)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
|
||||
return other is CategoryModel &&
|
||||
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);
|
||||
}
|
||||
}
|
||||
53
lib/features/products/data/models/category_model.g.dart
Normal file
53
lib/features/products/data/models/category_model.g.dart
Normal file
@@ -0,0 +1,53 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'category_model.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// TypeAdapterGenerator
|
||||
// **************************************************************************
|
||||
|
||||
class CategoryModelAdapter extends TypeAdapter<CategoryModel> {
|
||||
@override
|
||||
final typeId = 12;
|
||||
|
||||
@override
|
||||
CategoryModel read(BinaryReader reader) {
|
||||
final numOfFields = reader.readByte();
|
||||
final fields = <int, dynamic>{
|
||||
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
|
||||
};
|
||||
return CategoryModel(
|
||||
id: fields[0] as String,
|
||||
name: fields[1] as String,
|
||||
description: fields[2] as String,
|
||||
icon: fields[3] as String,
|
||||
order: (fields[4] as num).toInt(),
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void write(BinaryWriter writer, CategoryModel obj) {
|
||||
writer
|
||||
..writeByte(5)
|
||||
..writeByte(0)
|
||||
..write(obj.id)
|
||||
..writeByte(1)
|
||||
..write(obj.name)
|
||||
..writeByte(2)
|
||||
..write(obj.description)
|
||||
..writeByte(3)
|
||||
..write(obj.icon)
|
||||
..writeByte(4)
|
||||
..write(obj.order);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => typeId.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is CategoryModelAdapter &&
|
||||
runtimeType == other.runtimeType &&
|
||||
typeId == other.typeId;
|
||||
}
|
||||
242
lib/features/products/data/models/product_model.dart
Normal file
242
lib/features/products/data/models/product_model.dart
Normal file
@@ -0,0 +1,242 @@
|
||||
/// Data Model: Product
|
||||
///
|
||||
/// Data Transfer Object for product information.
|
||||
/// Handles JSON and Hive serialization/deserialization.
|
||||
library;
|
||||
|
||||
import 'package:hive_ce/hive.dart';
|
||||
import 'package:worker/features/products/domain/entities/product.dart';
|
||||
|
||||
part 'product_model.g.dart';
|
||||
|
||||
/// Product Model
|
||||
///
|
||||
/// Used for:
|
||||
/// - JSON serialization/deserialization
|
||||
/// - Hive local database storage
|
||||
/// - Converting to/from domain entity
|
||||
///
|
||||
/// Hive Type ID: 1
|
||||
@HiveType(typeId: 1)
|
||||
class ProductModel extends HiveObject {
|
||||
/// Unique identifier
|
||||
@HiveField(0)
|
||||
final String id;
|
||||
|
||||
/// Product name
|
||||
@HiveField(1)
|
||||
final String name;
|
||||
|
||||
/// Product SKU
|
||||
@HiveField(2)
|
||||
final String sku;
|
||||
|
||||
/// Product description
|
||||
@HiveField(3)
|
||||
final String description;
|
||||
|
||||
/// Price per unit (VND)
|
||||
@HiveField(4)
|
||||
final double price;
|
||||
|
||||
/// Unit of measurement
|
||||
@HiveField(5)
|
||||
final String unit;
|
||||
|
||||
/// Product image URL
|
||||
@HiveField(6)
|
||||
final String imageUrl;
|
||||
|
||||
/// Category ID
|
||||
@HiveField(7)
|
||||
final String categoryId;
|
||||
|
||||
/// Stock availability
|
||||
@HiveField(8)
|
||||
final bool inStock;
|
||||
|
||||
/// Stock quantity
|
||||
@HiveField(9)
|
||||
final int stockQuantity;
|
||||
|
||||
/// Created date (ISO8601 string)
|
||||
@HiveField(10)
|
||||
final String createdAt;
|
||||
|
||||
/// Sale price (optional)
|
||||
@HiveField(11)
|
||||
final double? salePrice;
|
||||
|
||||
/// Brand name (optional)
|
||||
@HiveField(12)
|
||||
final String? brand;
|
||||
|
||||
ProductModel({
|
||||
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,
|
||||
});
|
||||
|
||||
/// From JSON constructor
|
||||
factory ProductModel.fromJson(Map<String, dynamic> json) {
|
||||
return ProductModel(
|
||||
id: json['id'] as String,
|
||||
name: json['name'] as String,
|
||||
sku: json['sku'] as String,
|
||||
description: json['description'] as String,
|
||||
price: (json['price'] as num).toDouble(),
|
||||
unit: json['unit'] as String,
|
||||
imageUrl: json['imageUrl'] as String,
|
||||
categoryId: json['categoryId'] as String,
|
||||
inStock: json['inStock'] as bool,
|
||||
stockQuantity: json['stockQuantity'] as int,
|
||||
createdAt: json['createdAt'] as String,
|
||||
salePrice: json['salePrice'] != null ? (json['salePrice'] as num).toDouble() : null,
|
||||
brand: json['brand'] as String?,
|
||||
);
|
||||
}
|
||||
|
||||
/// To JSON method
|
||||
Map<String, dynamic> toJson() {
|
||||
return {
|
||||
'id': id,
|
||||
'name': name,
|
||||
'sku': sku,
|
||||
'description': description,
|
||||
'price': price,
|
||||
'unit': unit,
|
||||
'imageUrl': imageUrl,
|
||||
'categoryId': categoryId,
|
||||
'inStock': inStock,
|
||||
'stockQuantity': stockQuantity,
|
||||
'createdAt': createdAt,
|
||||
'salePrice': salePrice,
|
||||
'brand': brand,
|
||||
};
|
||||
}
|
||||
|
||||
/// Convert to domain entity
|
||||
Product toEntity() {
|
||||
return Product(
|
||||
id: id,
|
||||
name: name,
|
||||
sku: sku,
|
||||
description: description,
|
||||
price: price,
|
||||
unit: unit,
|
||||
imageUrl: imageUrl,
|
||||
categoryId: categoryId,
|
||||
inStock: inStock,
|
||||
stockQuantity: stockQuantity,
|
||||
createdAt: DateTime.parse(createdAt),
|
||||
salePrice: salePrice,
|
||||
brand: brand,
|
||||
);
|
||||
}
|
||||
|
||||
/// Create from domain entity
|
||||
factory ProductModel.fromEntity(Product entity) {
|
||||
return ProductModel(
|
||||
id: entity.id,
|
||||
name: entity.name,
|
||||
sku: entity.sku,
|
||||
description: entity.description,
|
||||
price: entity.price,
|
||||
unit: entity.unit,
|
||||
imageUrl: entity.imageUrl,
|
||||
categoryId: entity.categoryId,
|
||||
inStock: entity.inStock,
|
||||
stockQuantity: entity.stockQuantity,
|
||||
createdAt: entity.createdAt.toIso8601String(),
|
||||
salePrice: entity.salePrice,
|
||||
brand: entity.brand,
|
||||
);
|
||||
}
|
||||
|
||||
/// Copy with method
|
||||
ProductModel copyWith({
|
||||
String? id,
|
||||
String? name,
|
||||
String? sku,
|
||||
String? description,
|
||||
double? price,
|
||||
String? unit,
|
||||
String? imageUrl,
|
||||
String? categoryId,
|
||||
bool? inStock,
|
||||
int? stockQuantity,
|
||||
String? createdAt,
|
||||
double? salePrice,
|
||||
String? brand,
|
||||
}) {
|
||||
return ProductModel(
|
||||
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 'ProductModel(id: $id, name: $name, sku: $sku, price: $price, unit: $unit)';
|
||||
}
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) {
|
||||
if (identical(this, other)) return true;
|
||||
|
||||
return other is ProductModel &&
|
||||
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.createdAt == createdAt &&
|
||||
other.salePrice == salePrice &&
|
||||
other.brand == brand;
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode {
|
||||
return Object.hash(
|
||||
id,
|
||||
name,
|
||||
sku,
|
||||
description,
|
||||
price,
|
||||
unit,
|
||||
imageUrl,
|
||||
categoryId,
|
||||
inStock,
|
||||
stockQuantity,
|
||||
createdAt,
|
||||
salePrice,
|
||||
brand,
|
||||
);
|
||||
}
|
||||
}
|
||||
77
lib/features/products/data/models/product_model.g.dart
Normal file
77
lib/features/products/data/models/product_model.g.dart
Normal file
@@ -0,0 +1,77 @@
|
||||
// GENERATED CODE - DO NOT MODIFY BY HAND
|
||||
|
||||
part of 'product_model.dart';
|
||||
|
||||
// **************************************************************************
|
||||
// TypeAdapterGenerator
|
||||
// **************************************************************************
|
||||
|
||||
class ProductModelAdapter extends TypeAdapter<ProductModel> {
|
||||
@override
|
||||
final typeId = 1;
|
||||
|
||||
@override
|
||||
ProductModel read(BinaryReader reader) {
|
||||
final numOfFields = reader.readByte();
|
||||
final fields = <int, dynamic>{
|
||||
for (int i = 0; i < numOfFields; i++) reader.readByte(): reader.read(),
|
||||
};
|
||||
return ProductModel(
|
||||
id: fields[0] as String,
|
||||
name: fields[1] as String,
|
||||
sku: fields[2] as String,
|
||||
description: fields[3] as String,
|
||||
price: (fields[4] as num).toDouble(),
|
||||
unit: fields[5] as String,
|
||||
imageUrl: fields[6] as String,
|
||||
categoryId: fields[7] as String,
|
||||
inStock: fields[8] as bool,
|
||||
stockQuantity: (fields[9] as num).toInt(),
|
||||
createdAt: fields[10] as String,
|
||||
salePrice: (fields[11] as num?)?.toDouble(),
|
||||
brand: fields[12] as String?,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
void write(BinaryWriter writer, ProductModel obj) {
|
||||
writer
|
||||
..writeByte(13)
|
||||
..writeByte(0)
|
||||
..write(obj.id)
|
||||
..writeByte(1)
|
||||
..write(obj.name)
|
||||
..writeByte(2)
|
||||
..write(obj.sku)
|
||||
..writeByte(3)
|
||||
..write(obj.description)
|
||||
..writeByte(4)
|
||||
..write(obj.price)
|
||||
..writeByte(5)
|
||||
..write(obj.unit)
|
||||
..writeByte(6)
|
||||
..write(obj.imageUrl)
|
||||
..writeByte(7)
|
||||
..write(obj.categoryId)
|
||||
..writeByte(8)
|
||||
..write(obj.inStock)
|
||||
..writeByte(9)
|
||||
..write(obj.stockQuantity)
|
||||
..writeByte(10)
|
||||
..write(obj.createdAt)
|
||||
..writeByte(11)
|
||||
..write(obj.salePrice)
|
||||
..writeByte(12)
|
||||
..write(obj.brand);
|
||||
}
|
||||
|
||||
@override
|
||||
int get hashCode => typeId.hashCode;
|
||||
|
||||
@override
|
||||
bool operator ==(Object other) =>
|
||||
identical(this, other) ||
|
||||
other is ProductModelAdapter &&
|
||||
runtimeType == other.runtimeType &&
|
||||
typeId == other.typeId;
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
/// Repository Implementation: Products Repository
|
||||
///
|
||||
/// Concrete implementation of the products repository interface.
|
||||
/// Handles data from local datasource and converts to domain entities.
|
||||
library;
|
||||
|
||||
import 'package:worker/features/products/data/datasources/products_local_datasource.dart';
|
||||
import 'package:worker/features/products/domain/entities/category.dart';
|
||||
import 'package:worker/features/products/domain/entities/product.dart';
|
||||
import 'package:worker/features/products/domain/repositories/products_repository.dart';
|
||||
|
||||
/// Products Repository Implementation
|
||||
///
|
||||
/// Implements the repository interface defined in the domain layer.
|
||||
/// Coordinates data from local datasource and converts models to entities.
|
||||
class ProductsRepositoryImpl implements ProductsRepository {
|
||||
final ProductsLocalDataSource localDataSource;
|
||||
|
||||
const ProductsRepositoryImpl({
|
||||
required this.localDataSource,
|
||||
});
|
||||
|
||||
@override
|
||||
Future<List<Product>> getAllProducts() async {
|
||||
try {
|
||||
final productModels = await localDataSource.getAllProducts();
|
||||
return productModels.map((model) => model.toEntity()).toList();
|
||||
} catch (e) {
|
||||
throw Exception('Failed to get products: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<Product>> searchProducts(String query) async {
|
||||
try {
|
||||
final productModels = await localDataSource.searchProducts(query);
|
||||
return productModels.map((model) => model.toEntity()).toList();
|
||||
} catch (e) {
|
||||
throw Exception('Failed to search products: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<Product>> getProductsByCategory(String categoryId) async {
|
||||
try {
|
||||
final productModels = await localDataSource.getProductsByCategory(categoryId);
|
||||
return productModels.map((model) => model.toEntity()).toList();
|
||||
} catch (e) {
|
||||
throw Exception('Failed to get products by category: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Product> getProductById(String id) async {
|
||||
try {
|
||||
final productModel = await localDataSource.getProductById(id);
|
||||
return productModel.toEntity();
|
||||
} catch (e) {
|
||||
throw Exception('Failed to get product: $e');
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future<List<Category>> getCategories() async {
|
||||
try {
|
||||
final categoryModels = await localDataSource.getCategories();
|
||||
return categoryModels.map((model) => model.toEntity()).toList();
|
||||
} catch (e) {
|
||||
throw Exception('Failed to get categories: $e');
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user