fix: show personal diet name and consultant notifications

This commit is contained in:
2026-11-06 14:22:55 +03:00
parent ad70d0988e
commit 6c4572af55
26 changed files with 323 additions and 34 deletions

View File

@@ -194,6 +194,7 @@ class _ChatScreenState extends State<ChatScreen> {
: mapConsultationProgress(
state: chosenPlan.chosenPlanState,
hasPersonalDiet: chosenOption?.hasPersonalDiet ?? false,
personalDietName: chosenPlan.personalDietName,
);
final bannerType = resolveChatBanner(
progress: progress,

View File

@@ -24,6 +24,7 @@ abstract class ConsultationModel with _$ConsultationModel {
@JsonKey(name: 'consultant_id') required int consultantId,
required List<DiaryModel> diaries,
@JsonKey(name: 'personal_diet_id') required int? personalDietId,
@JsonKey(name: 'personal_diet_name') String? personalDietName,
}) = _ConsultationModel;
factory ConsultationModel.fromJson(Map<String, dynamic> json) => _$ConsultationModelFromJson(json);

View File

@@ -10,12 +10,14 @@ class ConsultationProgress {
required this.showProgressPlank,
required this.dietStatus,
required this.phase,
this.personalDietName,
});
final Set<ConsultationStep> completedSteps;
final bool showProgressPlank;
final PersonalDietStatus dietStatus;
final ConsultationPhase phase;
final String? personalDietName;
bool isStepCompleted(ConsultationStep step) => completedSteps.contains(step);

View File

@@ -97,7 +97,12 @@ class _ConsultationCardWidgetState extends State<ConsultationCardWidget> {
borderRadius: BorderRadius.circular(100),
),
child: Text(
s.personal_diet_proposed,
widget.consultationsModel.personalDietName == null ||
widget.consultationsModel.personalDietName!.isEmpty
? s.personal_diet_proposed
: s.personal_diet_proposed_named(
widget.consultationsModel.personalDietName!,
),
style: TextStyles.textXSSemibold.copyWith(
color: appTheme.greenA700,
),

View File

@@ -42,8 +42,12 @@ class PersonalDietStatusChip extends StatelessWidget {
return (s.payment_failed, appTheme.lightRed, appTheme.red);
}
if (progress.dietStatus == PersonalDietStatus.proposed) {
final dietName = progress.personalDietName;
final label = dietName == null || dietName.isEmpty
? s.personal_diet_proposed
: s.personal_diet_proposed_named(dietName);
return (
s.personal_diet_proposed,
label,
appTheme.transparentGreen,
appTheme.greenA700,
);

View File

@@ -4,12 +4,14 @@ import 'package:eatvio_app/features/plans/enums/chosen_plan_state_enum.dart';
ConsultationProgress mapConsultationProgress({
required ChosenPlanStateEnum state,
required bool hasPersonalDiet,
String? personalDietName,
}) {
return ConsultationProgress(
completedSteps: _completedSteps(state),
showProgressPlank: _showProgressPlank(state),
dietStatus: _dietStatus(state, hasPersonalDiet),
phase: _phase(state),
personalDietName: personalDietName,
);
}

View File

@@ -2174,6 +2174,56 @@ class PersonalDietIsDefinedToCustomerNotification implements BaseNotificationMod
}
}
class PersonalDietIsDefinedToAuthorNotification implements BaseNotificationModel {
@override
final NotificationType type;
@override
final bool isRead;
@override
final DateTime createdAt;
final int chosenPlanId;
final int dietId;
final String planName;
final String userName;
final String consultantName;
final String dietName;
final String role;
final String peerSlug;
final String peerImagePath;
PersonalDietIsDefinedToAuthorNotification({
required this.type,
required this.isRead,
required this.createdAt,
required this.chosenPlanId,
required this.dietId,
required this.planName,
required this.userName,
required this.consultantName,
required this.dietName,
required this.role,
required this.peerSlug,
required this.peerImagePath,
});
factory PersonalDietIsDefinedToAuthorNotification.fromJson(Map<String, dynamic> json, NotificationType type, {bool? isRead, String? createdAt}) {
return PersonalDietIsDefinedToAuthorNotification(
type: type,
isRead: isRead ?? false,
createdAt: parseDate(createdAt ?? json['created_at'] ?? DateTime.now().toIso8601String()),
chosenPlanId: json['chosen_plan_id'] ?? 0,
dietId: json['diet_id'] ?? 0,
planName: json['plan_name'] ?? '',
userName: json['user_name'] ?? '',
consultantName: json['consultant_name'] ?? '',
dietName: json['diet_name'] ?? '',
role: json['role']?.toString() ?? '',
peerSlug: json['peer_slug'] ?? '',
peerImagePath: json['peer_image_path'] ?? '',
);
}
}
// New Model Classes for Comprehensive Support
class ConsultantEducationUpdatedNotification implements BaseNotificationModel {
@@ -2480,6 +2530,7 @@ enum NotificationType {
chosenPlanFinishedToConsultant,
stayOnTrackToCustomer,
personalDietIsDefinedToCustomer,
personalDietIsDefinedToAuthor,
// Comments
entityCommented,
@@ -2602,6 +2653,7 @@ enum NotificationType {
'stay-on-track-to-customer' => NotificationType.stayOnTrackToCustomer,
'diary-reminder-for-users' => NotificationType.diaryReminderForUsers,
'personal-diet-is-defined-to-customer' => NotificationType.personalDietIsDefinedToCustomer,
'personal-diet-is-defined-to-author' => NotificationType.personalDietIsDefinedToAuthor,
// New additions
'consultant-education-updated' => NotificationType.consultantEducationUpdated,

View File

@@ -277,6 +277,12 @@ class NotificationsCubit extends Cubit<NotificationsState> {
isRead: isRead,
createdAt: createdAt,
),
NotificationType.personalDietIsDefinedToAuthor => PersonalDietIsDefinedToAuthorNotification.fromJson(
notificationJson,
type,
isRead: isRead,
createdAt: createdAt,
),
NotificationType.consultantEducationUpdated => ConsultantEducationUpdatedNotification.fromJson(
notificationJson,
type,

View File

@@ -122,6 +122,9 @@ class NotificationWidget extends StatelessWidget {
PersonalDietIsDefinedToCustomerNotification() => _PersonalDietIsDefinedToCustomerWidget(
notification: notification as PersonalDietIsDefinedToCustomerNotification,
),
PersonalDietIsDefinedToAuthorNotification() => _PersonalDietIsDefinedToAuthorWidget(
notification: notification as PersonalDietIsDefinedToAuthorNotification,
),
// New Widgets
ConsultantEducationUpdatedNotification() => _ConsultantEducationUpdatedWidget(notification: notification as ConsultantEducationUpdatedNotification),
ComplaintRegisteredNotification() => _ComplaintRegisteredWidget(notification: notification as ComplaintRegisteredNotification),
@@ -1273,6 +1276,27 @@ class _PersonalDietIsDefinedToCustomerWidget extends StatelessWidget {
}
}
class _PersonalDietIsDefinedToAuthorWidget extends StatelessWidget {
const _PersonalDietIsDefinedToAuthorWidget({required this.notification});
final PersonalDietIsDefinedToAuthorNotification notification;
@override
Widget build(BuildContext context) {
final userName = notification.userName.isNotEmpty ? notification.userName : 'Пользователь';
final dietName = notification.dietName.isNotEmpty ? ', выбранная диета: «${notification.dietName}»' : '';
return _BaseNotificationWidget(
isRead: notification.isRead,
notificationsText: 'Вы предложили персональную диету пользователю $userName$dietName',
createdAt: notification.createdAt,
onTap: notification.peerSlug.isEmpty
? null
: () {
context.push(RouteConstants.profileScreen, extra: notification.peerSlug);
},
);
}
}
class _ConsultantEducationUpdatedWidget extends StatelessWidget {
const _ConsultantEducationUpdatedWidget({required this.notification});
final ConsultantEducationUpdatedNotification notification;

View File

@@ -413,6 +413,12 @@ class NotificationsService {
isRead: readDate != null,
createdAt: createdAt,
),
NotificationType.personalDietIsDefinedToAuthor => PersonalDietIsDefinedToAuthorNotification.fromJson(
notificationJson['data'],
type,
isRead: readDate != null,
createdAt: createdAt,
),
NotificationType.consultantEducationUpdated => ConsultantEducationUpdatedNotification.fromJson(
notificationJson['data'],
type,

View File

@@ -15,6 +15,7 @@ abstract class ChosenPlanModel with _$ChosenPlanModel {
@JsonKey(name: 'last_state') required ChosenPlanStateEnum chosenPlanState,
@JsonKey(name: 'chat_id') required String chatId,
@JsonKey(name: 'consultant_id') required int consultantId,
@JsonKey(name: 'personal_diet_name') String? personalDietName,
@JsonKey(name: 'plan') required PlanModel plan,
}) = _ChosenPlanModel;