/// Domain Entity: Promotion /// /// Represents a promotional offer or campaign displayed on the home screen. /// This entity contains all information needed to display promotion banners. /// /// This is a pure domain entity with no external dependencies. library; /// Promotion status enum enum PromotionStatus { /// Currently active promotion active, /// Promotion starting in the future upcoming, /// Expired promotion expired; } /// Promotion Entity /// /// Contains all information needed to display a promotion: /// - Basic info (title, description) /// - Visual assets (image URL) /// - Validity period /// - Optional discount information class Promotion { /// Unique promotion ID final String id; /// Promotion title final String title; /// Detailed description final String description; /// Banner/cover image URL final String imageUrl; /// Promotion start date final DateTime startDate; /// Promotion end date final DateTime endDate; /// Optional discount percentage (e.g., 30 for 30%) final int? discountPercentage; /// Optional discount amount final double? discountAmount; /// Optional terms and conditions final String? terms; /// Optional link to detailed page final String? detailsUrl; /// Constructor const Promotion({ required this.id, required this.title, required this.description, required this.imageUrl, required this.startDate, required this.endDate, this.discountPercentage, this.discountAmount, this.terms, this.detailsUrl, }); /// Get current promotion status PromotionStatus get status { final now = DateTime.now(); if (now.isBefore(startDate)) { return PromotionStatus.upcoming; } else if (now.isAfter(endDate)) { return PromotionStatus.expired; } else { return PromotionStatus.active; } } /// Check if promotion is currently active bool get isActive => status == PromotionStatus.active; /// Get days remaining until expiry (null if expired or upcoming) int? get daysRemaining { if (status != PromotionStatus.active) return null; return endDate.difference(DateTime.now()).inDays; } /// Format discount display text String? get discountText { if (discountPercentage != null) { return 'Giảm $discountPercentage%'; } else if (discountAmount != null) { return 'Giảm ${discountAmount?.toStringAsFixed(0) ?? '0'}₫'; } return null; } /// Copy with method for immutability Promotion copyWith({ String? id, String? title, String? description, String? imageUrl, DateTime? startDate, DateTime? endDate, int? discountPercentage, double? discountAmount, String? terms, String? detailsUrl, }) { return Promotion( id: id ?? this.id, title: title ?? this.title, description: description ?? this.description, imageUrl: imageUrl ?? this.imageUrl, startDate: startDate ?? this.startDate, endDate: endDate ?? this.endDate, discountPercentage: discountPercentage ?? this.discountPercentage, discountAmount: discountAmount ?? this.discountAmount, terms: terms ?? this.terms, detailsUrl: detailsUrl ?? this.detailsUrl, ); } /// Equality operator @override bool operator ==(Object other) { if (identical(this, other)) return true; return other is Promotion && other.id == id && other.title == title && other.description == description && other.imageUrl == imageUrl && other.startDate == startDate && other.endDate == endDate && other.discountPercentage == discountPercentage && other.discountAmount == discountAmount && other.terms == terms && other.detailsUrl == detailsUrl; } /// Hash code @override int get hashCode { return Object.hash( id, title, description, imageUrl, startDate, endDate, discountPercentage, discountAmount, terms, detailsUrl, ); } /// String representation @override String toString() { return 'Promotion(id: $id, title: $title, description: $description, ' 'imageUrl: $imageUrl, startDate: $startDate, endDate: $endDate, ' 'discountPercentage: $discountPercentage, discountAmount: $discountAmount, ' 'terms: $terms, detailsUrl: $detailsUrl)'; } }