270 lines
6.8 KiB
Dart
270 lines
6.8 KiB
Dart
import 'package:hive/hive.dart';
|
|
import 'menu_model.dart';
|
|
|
|
part 'recommendation_model.g.dart';
|
|
|
|
@HiveType(typeId: 3)
|
|
class RecommendationModel extends HiveObject {
|
|
@HiveField(0)
|
|
String id;
|
|
|
|
@HiveField(1)
|
|
String userId;
|
|
|
|
@HiveField(2)
|
|
List<MenuRecommendation> recommendations;
|
|
|
|
@HiveField(3)
|
|
String originalMenuText;
|
|
|
|
@HiveField(4)
|
|
String? menuImageUrl;
|
|
|
|
@HiveField(5)
|
|
String prompt;
|
|
|
|
@HiveField(6)
|
|
String aiResponse;
|
|
|
|
@HiveField(7)
|
|
double confidence;
|
|
|
|
@HiveField(8)
|
|
DateTime createdAt;
|
|
|
|
@HiveField(9)
|
|
bool isFavorite;
|
|
|
|
@HiveField(10)
|
|
String sessionId;
|
|
|
|
@HiveField(11)
|
|
String? selectedMenuName;
|
|
|
|
@HiveField(12)
|
|
List<String>? menuImagePaths;
|
|
|
|
RecommendationModel({
|
|
required this.id,
|
|
required this.userId,
|
|
required this.recommendations,
|
|
required this.originalMenuText,
|
|
this.menuImageUrl,
|
|
this.menuImagePaths,
|
|
required this.prompt,
|
|
required this.aiResponse,
|
|
this.confidence = 0.0,
|
|
required this.createdAt,
|
|
this.isFavorite = false,
|
|
required this.sessionId,
|
|
this.selectedMenuName,
|
|
});
|
|
|
|
factory RecommendationModel.fromJson(Map<String, dynamic> json) {
|
|
return RecommendationModel(
|
|
id: json['id'] as String,
|
|
userId: json['userId'] as String,
|
|
recommendations: (json['recommendations'] as List)
|
|
.map((e) => MenuRecommendation.fromJson(e as Map<String, dynamic>))
|
|
.toList(),
|
|
originalMenuText: json['originalMenuText'] as String,
|
|
menuImageUrl: json['menuImageUrl'] as String?,
|
|
menuImagePaths: (json['menuImagePaths'] as List<dynamic>?)
|
|
?.map((e) => e as String)
|
|
.toList(),
|
|
prompt: json['prompt'] as String,
|
|
aiResponse: json['aiResponse'] as String,
|
|
confidence: (json['confidence'] as num?)?.toDouble() ?? 0.0,
|
|
createdAt: DateTime.parse(json['createdAt'] as String),
|
|
isFavorite: json['isFavorite'] as bool? ?? false,
|
|
sessionId: json['sessionId'] as String,
|
|
selectedMenuName: json['selectedMenuName'] as String?,
|
|
);
|
|
}
|
|
|
|
Map<String, dynamic> toJson() {
|
|
return {
|
|
'id': id,
|
|
'userId': userId,
|
|
'recommendations': recommendations.map((e) => e.toJson()).toList(),
|
|
'originalMenuText': originalMenuText,
|
|
'menuImageUrl': menuImageUrl,
|
|
'menuImagePaths': menuImagePaths,
|
|
'prompt': prompt,
|
|
'aiResponse': aiResponse,
|
|
'confidence': confidence,
|
|
'createdAt': createdAt.toIso8601String(),
|
|
'isFavorite': isFavorite,
|
|
'sessionId': sessionId,
|
|
'selectedMenuName': selectedMenuName,
|
|
};
|
|
}
|
|
|
|
RecommendationModel copyWith({
|
|
String? id,
|
|
String? userId,
|
|
List<MenuRecommendation>? recommendations,
|
|
String? originalMenuText,
|
|
String? menuImageUrl,
|
|
List<String>? menuImagePaths,
|
|
String? prompt,
|
|
String? aiResponse,
|
|
double? confidence,
|
|
DateTime? createdAt,
|
|
bool? isFavorite,
|
|
String? sessionId,
|
|
String? selectedMenuName,
|
|
}) {
|
|
return RecommendationModel(
|
|
id: id ?? this.id,
|
|
userId: userId ?? this.userId,
|
|
recommendations: recommendations ?? this.recommendations,
|
|
originalMenuText: originalMenuText ?? this.originalMenuText,
|
|
menuImageUrl: menuImageUrl ?? this.menuImageUrl,
|
|
menuImagePaths: menuImagePaths ?? this.menuImagePaths,
|
|
prompt: prompt ?? this.prompt,
|
|
aiResponse: aiResponse ?? this.aiResponse,
|
|
confidence: confidence ?? this.confidence,
|
|
createdAt: createdAt ?? this.createdAt,
|
|
isFavorite: isFavorite ?? this.isFavorite,
|
|
sessionId: sessionId ?? this.sessionId,
|
|
selectedMenuName: selectedMenuName ?? this.selectedMenuName,
|
|
);
|
|
}
|
|
|
|
List<MenuRecommendation> get topRecommendations {
|
|
final sorted = List<MenuRecommendation>.from(recommendations);
|
|
sorted.sort((a, b) => b.score.compareTo(a.score));
|
|
return sorted.take(3).toList();
|
|
}
|
|
|
|
String get formattedDate {
|
|
final now = DateTime.now();
|
|
final difference = now.difference(createdAt);
|
|
|
|
if (difference.inDays > 0) {
|
|
return '${difference.inDays} hari yang lalu';
|
|
} else if (difference.inHours > 0) {
|
|
return '${difference.inHours} jam yang lalu';
|
|
} else if (difference.inMinutes > 0) {
|
|
return '${difference.inMinutes} menit yang lalu';
|
|
} else {
|
|
return 'Baru saja';
|
|
}
|
|
}
|
|
}
|
|
|
|
@HiveType(typeId: 4)
|
|
class MenuRecommendation extends HiveObject {
|
|
@HiveField(0)
|
|
String menuName;
|
|
|
|
@HiveField(1)
|
|
String description;
|
|
|
|
@HiveField(2)
|
|
double price;
|
|
|
|
@HiveField(3)
|
|
String reason;
|
|
|
|
@HiveField(4)
|
|
double score;
|
|
|
|
@HiveField(5)
|
|
List<String> tags;
|
|
|
|
@HiveField(6)
|
|
String category;
|
|
|
|
@HiveField(7)
|
|
String? imageUrl;
|
|
|
|
@HiveField(8)
|
|
bool isRecommended;
|
|
|
|
@HiveField(9)
|
|
String? slotType;
|
|
|
|
MenuRecommendation({
|
|
required this.menuName,
|
|
required this.description,
|
|
required this.price,
|
|
required this.reason,
|
|
this.score = 0.0,
|
|
this.tags = const [],
|
|
required this.category,
|
|
this.imageUrl,
|
|
this.isRecommended = true,
|
|
this.slotType,
|
|
});
|
|
|
|
factory MenuRecommendation.fromJson(Map<String, dynamic> json) {
|
|
return MenuRecommendation(
|
|
menuName: json['menuName'] as String,
|
|
description: json['description'] as String,
|
|
price: (json['price'] as num).toDouble(),
|
|
reason: json['reason'] as String,
|
|
score: (json['score'] as num?)?.toDouble() ?? 0.0,
|
|
tags: List<String>.from(json['tags'] ?? []),
|
|
category: json['category'] as String,
|
|
imageUrl: json['imageUrl'] as String?,
|
|
isRecommended: json['isRecommended'] as bool? ?? true,
|
|
slotType: json['slotType'] as String?,
|
|
);
|
|
}
|
|
|
|
Map<String, dynamic> toJson() {
|
|
return {
|
|
'menuName': menuName,
|
|
'description': description,
|
|
'price': price,
|
|
'reason': reason,
|
|
'score': score,
|
|
'tags': tags,
|
|
'category': category,
|
|
'imageUrl': imageUrl,
|
|
'isRecommended': isRecommended,
|
|
'slotType': slotType,
|
|
};
|
|
}
|
|
|
|
MenuRecommendation copyWith({
|
|
String? menuName,
|
|
String? description,
|
|
double? price,
|
|
String? reason,
|
|
double? score,
|
|
List<String>? tags,
|
|
String? category,
|
|
String? imageUrl,
|
|
bool? isRecommended,
|
|
String? slotType,
|
|
}) {
|
|
return MenuRecommendation(
|
|
menuName: menuName ?? this.menuName,
|
|
description: description ?? this.description,
|
|
price: price ?? this.price,
|
|
reason: reason ?? this.reason,
|
|
score: score ?? this.score,
|
|
tags: tags ?? this.tags,
|
|
category: category ?? this.category,
|
|
imageUrl: imageUrl ?? this.imageUrl,
|
|
isRecommended: isRecommended ?? this.isRecommended,
|
|
slotType: slotType ?? this.slotType,
|
|
);
|
|
}
|
|
|
|
String get formattedPrice {
|
|
return 'Rp ${price.toStringAsFixed(0).replaceAllMapped(RegExp(r'(\d{1,3})(?=(\d{3})+(?!\d))'), (Match m) => '${m[1]}.')}';
|
|
}
|
|
|
|
int get starRating {
|
|
return (score * 5).round().clamp(1, 5);
|
|
}
|
|
|
|
String get scorePercentage {
|
|
return '${(score * 100).toStringAsFixed(0)}%';
|
|
}
|
|
}
|