From 3485e1fe5d1424450c74e7cad148a06da1a6458e Mon Sep 17 00:00:00 2001 From: Username1548 Date: Thu, 22 May 2025 22:00:01 +0700 Subject: [PATCH] added notifications layoute --- .../models/notification_model.dart | 2 +- .../notifications_cubit.dart | 30 --- .../presentation/notifications_screen.dart | 41 ++- .../widgets/notification_widget.dart | 254 +++++++++++++++++- lib/generated/intl/messages_en.dart | 40 +++ lib/generated/l10n.dart | 110 ++++++++ lib/l10n/intl_en.arb | 13 +- 7 files changed, 441 insertions(+), 49 deletions(-) diff --git a/lib/features/notifications/models/notification_model.dart b/lib/features/notifications/models/notification_model.dart index f0b7b8b3..5bcdf18c 100644 --- a/lib/features/notifications/models/notification_model.dart +++ b/lib/features/notifications/models/notification_model.dart @@ -1,4 +1,4 @@ -abstract class BaseNotificationModel { +sealed class BaseNotificationModel { abstract final NotificationType type; } diff --git a/lib/features/notifications/presentation/notifications_cubit/notifications_cubit.dart b/lib/features/notifications/presentation/notifications_cubit/notifications_cubit.dart index 6bb8c2c0..091d1f9d 100644 --- a/lib/features/notifications/presentation/notifications_cubit/notifications_cubit.dart +++ b/lib/features/notifications/presentation/notifications_cubit/notifications_cubit.dart @@ -74,33 +74,3 @@ class NotificationsCubit extends Cubit { } } } - -final qwe = { - 'event': 'IlluminateNotificationsEventsBroadcastNotificationCreated', - 'channel': 'private-message-events.36', - 'data': { - "plan_name": "Little Cheeseburger", - "plan_id": 18, - "user_name": "\u041b\u044f\u043f\u0438\u0441 \u041b\u0430\u043f\u0448\u0430", - "user_id": 53, - "user_photo_path": "https://dev2.eatvio.com/storage/profile-photos/X2aHwD6IOIFxEK8HM2d2oLuqio5BmocDLeWoH3Gs.jpg", - "chat_id": "3d14615d-1388-4833-8165-b40daa78f96a", - "chat_url": "https://dev2.eatvio.com/chat/3d14615d-1388-4833-8165-b40daa78f96a", - "notification_class_name": "App\\Notifications\\Plan\\PlanSubscriptionStartedToAuthor", - "database_type": "client-start-plan-subscription-to-author", - "id": "65cbea6d-1734-406e-b5da-79db270aed90", - "type": "client-start-plan-subscription-to-author" - } -}; -final qwe2 = { - "plan_name": "Little Cheeseburger", - "plan_id": 18, - "user_name": "\u041b\u044f\u043f\u0438\u0441 \u041b\u0430\u043f\u0448\u0430", - "user_id": 53, - "user_photo_path": "https://dev2.eatvio.com/storage/profile-photos/X2aHwD6IOIFxEK8HM2d2oLuqio5BmocDLeWoH3Gs.jpg", - "chat_url": "https://dev2.eatvio.com/chat/3d14615d-1388-4833-8165-b40daa78f96a", - "notification_class_name": "App\\Notifications\\Plan\\PlanSubscriptionCanceledToAuthor", - "database_type": "client-cancel-plan-subscription-to-author", - "id": "cf86d0e7-8ebb-4360-9b8a-b6df97e12aed", - "type": "client-cancel-plan-subscription-to-author" -}; diff --git a/lib/features/notifications/presentation/notifications_screen.dart b/lib/features/notifications/presentation/notifications_screen.dart index 73c244f1..7c3e4581 100644 --- a/lib/features/notifications/presentation/notifications_screen.dart +++ b/lib/features/notifications/presentation/notifications_screen.dart @@ -1,3 +1,5 @@ +import 'package:eatvio_app/core/theme/theme_helper.dart'; +import 'package:eatvio_app/core/widgets/custom_divider.dart'; import 'package:eatvio_app/features/notifications/presentation/notifications_cubit/notifications_cubit.dart'; import 'package:eatvio_app/features/notifications/presentation/notifications_cubit/notifications_state.dart'; import 'package:eatvio_app/features/notifications/presentation/widgets/notification_widget.dart'; @@ -12,19 +14,42 @@ class NotificationsScreen extends StatelessWidget { return BlocBuilder( builder: (BuildContext context, NotificationsState state) { return Scaffold( + backgroundColor: appTheme.gray100, appBar: AppBar( title: const Text('Уведомления'), ), body: state is NotificationsStateLoaded - ? CustomScrollView( - slivers: [ - SliverList.builder( - itemBuilder: (context, index) => NotificationWidget( - notification: state.notifications[index], + ? Padding( + padding: const EdgeInsets.only(top: 8.0), + child: Container( + padding: const EdgeInsets.only(top: 16), + decoration: BoxDecoration( + color: appTheme.whiteA700, + borderRadius: const BorderRadius.only( + topLeft: Radius.circular(12), + topRight: Radius.circular(12), ), - itemCount: state.notifications.length, - ) - ], + ), + child: CustomScrollView( + slivers: [ + SliverList.separated( + separatorBuilder: (context, index) => const Padding( + padding: EdgeInsets.symmetric(vertical: 12, horizontal: 16), + child: CustomDivider(), + ), + itemBuilder: (context, index) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 16), + child: NotificationWidget( + notification: state.notifications[index], + ), + ); + }, + itemCount: state.notifications.length, + ) + ], + ), + ), ) : const Placeholder(), ); diff --git a/lib/features/notifications/presentation/widgets/notification_widget.dart b/lib/features/notifications/presentation/widgets/notification_widget.dart index 4cff1067..21534b0b 100644 --- a/lib/features/notifications/presentation/widgets/notification_widget.dart +++ b/lib/features/notifications/presentation/widgets/notification_widget.dart @@ -1,21 +1,257 @@ +import 'package:eatvio_app/core/theme/custom_text_style.dart'; import 'package:eatvio_app/core/theme/theme_helper.dart'; +import 'package:eatvio_app/core/widgets/custom_outlined_button.dart'; +import 'package:eatvio_app/features/my_finance/presentation/pages/card_settings.dart'; import 'package:eatvio_app/features/notifications/models/notification_model.dart'; +import 'package:eatvio_app/generated/l10n.dart'; import 'package:flutter/material.dart'; +import 'package:go_router/go_router.dart'; class NotificationWidget extends StatelessWidget { const NotificationWidget({super.key, required this.notification}); final BaseNotificationModel notification; @override Widget build(BuildContext context) { - return Container( - width: MediaQuery.sizeOf(context).width, - height: 60, - margin: const EdgeInsets.symmetric(horizontal: 16, vertical: 12), - decoration: BoxDecoration( - color: appTheme.green, - borderRadius: BorderRadius.circular(18), - ), - child: Center(child: Text(notification.type.name)), + return switch (notification) { + PlanSubscriptionStartedToAuthorNotification() => _PlanSubscriptionStartedToAuthorWidget( + notification: notification as PlanSubscriptionStartedToAuthorNotification, + ), + PlanSubscriptionCanceledToAuthorNotification() => _PlanSubscriptionCanceledToAuthorWidget( + notification: notification as PlanSubscriptionCanceledToAuthorNotification, + ), + ChosenPlanStartedToClientNotification() => _ChosenPlanStartedToClientWidget( + notification: notification as ChosenPlanStartedToClientNotification, + ), + ChosenPlanTomorrowReminderToClientNotification() => _ChosenPlanTomorrowReminderToClientWidget( + notification: notification as ChosenPlanTomorrowReminderToClientNotification, + ), + DiaryReminderNotification() => _DiaryReminderWidget( + notification: notification as DiaryReminderNotification, + ), + ConsultantLeftCommentNotification() => _ConsultantLeftCommentWidget( + notification: notification as ConsultantLeftCommentNotification, + ), + PersonalizedDietIsDefinedNotification() => _PersonalizedDietIsDefinedWidget( + notification: notification as PersonalizedDietIsDefinedNotification, + ), + ChosenPlanFinishedToClientNotification() => _ChosenPlanFinishedToClientWidget( + notification: notification as ChosenPlanFinishedToClientNotification, + ), + PlanSubscriptionCanceledToCustomerNotification() => _PlanSubscriptionCanceledToCustomerWidget( + notification: notification as PlanSubscriptionCanceledToCustomerNotification, + ), + ChosenPlanStartedToConsultantNotification() => _ChosenPlanStartedToConsultantWidget( + notification: notification as ChosenPlanStartedToConsultantNotification, + ), + ChosenPlanTomorrowReminderToConsultantNotification() => _ChosenPlanTomorrowReminderToConsultantWidget( + notification: notification as ChosenPlanTomorrowReminderToConsultantNotification, + ), + PlanSubscriptionPaidToAuthorNotification() => throw _PlanSubscriptionPaidToAuthorWidget( + notification: notification as PlanSubscriptionPaidToAuthorNotification, + ), + ChosenPlanFinishedToConsultantNotification() => _ChosenPlanFinishedToConsultantWidget( + notification: notification as ChosenPlanFinishedToConsultantNotification, + ), + UnknownNotification() => const SizedBox.shrink(), + }; + } +} + +class _PlanSubscriptionStartedToAuthorWidget extends StatelessWidget { + const _PlanSubscriptionStartedToAuthorWidget({required this.notification}); + final PlanSubscriptionStartedToAuthorNotification notification; + @override + Widget build(BuildContext context) { + final s = S.of(context); + + return _BaseNotificationWidget( + notificationsText: s.userSubscribedToPlan(notification.customerName, notification.planName), + ); + } +} + +class _PlanSubscriptionCanceledToAuthorWidget extends StatelessWidget { + const _PlanSubscriptionCanceledToAuthorWidget({required this.notification}); + final PlanSubscriptionCanceledToAuthorNotification notification; + @override + Widget build(BuildContext context) { + final s = S.of(context); + + return _BaseNotificationWidget( + notificationsText: s.userCanceldThePlanSubscription(notification.customerName, notification.planName), + ); + } +} + +class _ChosenPlanStartedToClientWidget extends StatelessWidget { + const _ChosenPlanStartedToClientWidget({required this.notification}); + final ChosenPlanStartedToClientNotification notification; + @override + Widget build(BuildContext context) { + final s = S.of(context); + + return _BaseNotificationWidget( + notificationsText: s.planStarted(notification.planName), + ); + } +} + +class _ChosenPlanTomorrowReminderToClientWidget extends StatelessWidget { + const _ChosenPlanTomorrowReminderToClientWidget({required this.notification}); + final ChosenPlanTomorrowReminderToClientNotification notification; + @override + Widget build(BuildContext context) { + final s = S.of(context); + + return _BaseNotificationWidget( + notificationsText: s.tomorrowYourPlanWillStart(notification.planName), + ); + } +} + +class _DiaryReminderWidget extends StatelessWidget { + const _DiaryReminderWidget({required this.notification}); + final DiaryReminderNotification notification; + @override + Widget build(BuildContext context) { + final s = S.of(context); + return _BaseNotificationWidget( + notificationsText: s.fillYasterday, + ); + } +} + +class _ConsultantLeftCommentWidget extends StatelessWidget { + const _ConsultantLeftCommentWidget({required this.notification}); + final ConsultantLeftCommentNotification notification; + @override + Widget build(BuildContext context) { + final s = S.of(context); + + return _BaseNotificationWidget( + notificationsText: s.consultantLeftComment(notification.date), + ); + } +} + +class _PersonalizedDietIsDefinedWidget extends StatelessWidget { + const _PersonalizedDietIsDefinedWidget({required this.notification}); + final PersonalizedDietIsDefinedNotification notification; + @override + Widget build(BuildContext context) { + return Container(); + } +} + +class _ChosenPlanFinishedToClientWidget extends StatelessWidget { + const _ChosenPlanFinishedToClientWidget({required this.notification}); + final ChosenPlanFinishedToClientNotification notification; + @override + Widget build(BuildContext context) { + final s = S.of(context); + + return _BaseNotificationWidget( + notificationsText: '${s.congratsYouHaveFinishedPlan} «${notification.planName}»', + ); + } +} + +class _PlanSubscriptionCanceledToCustomerWidget extends StatelessWidget { + const _PlanSubscriptionCanceledToCustomerWidget({required this.notification}); + final PlanSubscriptionCanceledToCustomerNotification notification; + @override + Widget build(BuildContext context) { + final s = S.of(context); + + return _BaseNotificationWidget( + notificationsText: s.youCanceledSubscriptionToPlan(notification.planName), + ); + } +} + +class _ChosenPlanStartedToConsultantWidget extends StatelessWidget { + const _ChosenPlanStartedToConsultantWidget({required this.notification}); + final ChosenPlanStartedToConsultantNotification notification; + @override + Widget build(BuildContext context) { + final s = S.of(context); + + return _BaseNotificationWidget( + notificationsText: s.planWithUserStarted(notification.planName, notification.customerName), + ); + } +} + +class _ChosenPlanTomorrowReminderToConsultantWidget extends StatelessWidget { + const _ChosenPlanTomorrowReminderToConsultantWidget({required this.notification}); + final ChosenPlanTomorrowReminderToConsultantNotification notification; + @override + Widget build(BuildContext context) { + final s = S.of(context); + + return _BaseNotificationWidget( + notificationsText: s.tommorowPlanWithUserWillStart(notification.planName, notification.customerName), + ); + } +} + +class _PlanSubscriptionPaidToAuthorWidget extends StatelessWidget { + const _PlanSubscriptionPaidToAuthorWidget({required this.notification}); + final PlanSubscriptionPaidToAuthorNotification notification; + @override + Widget build(BuildContext context) { + final s = S.of(context); + + return _BaseNotificationWidget( + notificationsText: s.userPaidThePlan(notification.customerName, notification.planName), + ); + } +} + +class _ChosenPlanFinishedToConsultantWidget extends StatelessWidget { + const _ChosenPlanFinishedToConsultantWidget({required this.notification}); + final ChosenPlanFinishedToConsultantNotification notification; + @override + Widget build(BuildContext context) { + final s = S.of(context); + return Container( + child: Column(children: [ + Text( + '${s.congratsYouHaveFinishedPlan} «${notification.planName}»', + style: TextStyles.textMSemibold.copyWith(color: appTheme.black), + ), + const SizedBox(height: 12), + Text( + s.userConfirmedPlanCompletionWithoutSum, + style: TextStyles.textSMedium.copyWith(color: appTheme.black), + ), + const SizedBox(height: 12), + CustomOutlinedButton( + text: s.collectCharge, + buttonTextStyle: TextStyles.textMSemibold.copyWith(color: appTheme.whiteA700), + buttonStyle: ButtonStyle(side: const WidgetStatePropertyAll(BorderSide.none), backgroundColor: WidgetStatePropertyAll(appTheme.green)), + onPressed: () { + GoRouter.of(context).pushNamed(CardSettingsScreen.name, extra: notification.planId); + }, + ), + const SizedBox(height: 12), + ]), + ); + } +} + +// CardSettingsScreen +class _BaseNotificationWidget extends StatelessWidget { + const _BaseNotificationWidget({required this.notificationsText}); + final String notificationsText; + @override + Widget build(BuildContext context) { + return SizedBox( + width: MediaQuery.sizeOf(context).width * 0.6, + child: Text( + notificationsText, + style: TextStyles.textMMedium.copyWith(color: appTheme.black), + ), ); } } diff --git a/lib/generated/intl/messages_en.dart b/lib/generated/intl/messages_en.dart index dc980241..5b68cf0c 100644 --- a/lib/generated/intl/messages_en.dart +++ b/lib/generated/intl/messages_en.dart @@ -20,6 +20,30 @@ typedef String MessageIfAbsent(String messageStr, List args); class MessageLookup extends MessageLookupByLibrary { String get localeName => 'en'; + static String m0(date) => "Консультант прокомментировал день ${date}"; + + static String m1(planName) => "План «${planName}» начался"; + + static String m2(planName, name) => + "План «${planName}» с пользователем ${name} начался"; + + static String m3(planName, name) => + "Завтра начинается план «${planName}» с пользователем ${name}"; + + static String m4(planName) => + "Завтра начнется прохождение плана «${planName}»"; + + static String m5(name, planName) => + "Пользователь ${name} отменил подписку на план «${planName}»"; + + static String m6(name, planName) => + "Пользователь ${name} оплатил план «${planName}»"; + + static String m7(name, planName) => + "Пользователь ${name} подписался на план «${planName}»"; + + static String m8(planName) => "Вы отменили подписку на план «${planName}»"; + final messages = _notInlinedMessages(_notInlinedMessages); static Map _notInlinedMessages(_) => { "Moscow": MessageLookupByLibrary.simpleMessage("Москва"), @@ -153,6 +177,7 @@ class MessageLookup extends MessageLookupByLibrary { "congratsYouHaveFinishedPlan": MessageLookupByLibrary.simpleMessage( "Поздравляем, Вы закончили план", ), + "consultantLeftComment": m0, "consultations_count": MessageLookupByLibrary.simpleMessage( "Кол-во проведенных консулдьтаций", ), @@ -301,6 +326,9 @@ class MessageLookup extends MessageLookupByLibrary { "Ваши любимые/нелюбимые продукты питания?", ), "female": MessageLookupByLibrary.simpleMessage("Женский"), + "fillYasterday": MessageLookupByLibrary.simpleMessage( + "Заполните вчерашний день", + ), "fill_in_information": MessageLookupByLibrary.simpleMessage( "Заполните информацию:", ), @@ -504,9 +532,11 @@ class MessageLookup extends MessageLookupByLibrary { "Физическая форма", ), "place_of_living": MessageLookupByLibrary.simpleMessage("Место проживания"), + "planStarted": m1, "planWillBeConfirmedAfterThreeDays": MessageLookupByLibrary.simpleMessage( "Если вы не подтвердите, то план через три дня подтвердится автоматически", ), + "planWithUserStarted": m2, "plan_author": MessageLookupByLibrary.simpleMessage("Автор плана"), "plan_desc": MessageLookupByLibrary.simpleMessage("Описание плана"), "plan_desc_example": MessageLookupByLibrary.simpleMessage( @@ -640,6 +670,8 @@ class MessageLookup extends MessageLookupByLibrary { "toOffer": MessageLookupByLibrary.simpleMessage("Предложить"), "to_draft": MessageLookupByLibrary.simpleMessage("В черновик"), "to_order": MessageLookupByLibrary.simpleMessage("Заказать"), + "tommorowPlanWithUserWillStart": m3, + "tomorrowYourPlanWillStart": m4, "try_again": MessageLookupByLibrary.simpleMessage("Попробовать снова"), "try_again_2": MessageLookupByLibrary.simpleMessage("Попробуйте ещё раз"), "typeAmoutOfReturn": MessageLookupByLibrary.simpleMessage( @@ -664,9 +696,16 @@ class MessageLookup extends MessageLookupByLibrary { "Верхние показатели давления:", ), "upper": MessageLookupByLibrary.simpleMessage("Верхнее"), + "userCanceldThePlanSubscription": m5, "userConfirmedPlanCompletion": MessageLookupByLibrary.simpleMessage( "Пользователь подтвердил прохождения плана. В ближайшее время Вы получите", ), + "userConfirmedPlanCompletionWithoutSum": + MessageLookupByLibrary.simpleMessage( + "Пользователь подтвердил прохождения плана.", + ), + "userPaidThePlan": m6, + "userSubscribedToPlan": m7, "user_authorization": MessageLookupByLibrary.simpleMessage( "Аутентификация", ), @@ -716,6 +755,7 @@ class MessageLookup extends MessageLookupByLibrary { "youCanComplain": MessageLookupByLibrary.simpleMessage( "Если вас что-то не устроило, вы можете подать жалобу", ), + "youCanceledSubscriptionToPlan": m8, "youShureYouWantToCancelOrder": MessageLookupByLibrary.simpleMessage( "Вы уверены, что хотите отменить заказ?", ), diff --git a/lib/generated/l10n.dart b/lib/generated/l10n.dart index 8da944a3..1e319c1d 100644 --- a/lib/generated/l10n.dart +++ b/lib/generated/l10n.dart @@ -3095,6 +3095,16 @@ class S { ); } + /// `Пользователь подтвердил прохождения плана.` + String get userConfirmedPlanCompletionWithoutSum { + return Intl.message( + 'Пользователь подтвердил прохождения плана.', + name: 'userConfirmedPlanCompletionWithoutSum', + desc: '', + args: [], + ); + } + /// `Получить выплату` String get collectCharge { return Intl.message( @@ -3224,6 +3234,106 @@ class S { args: [], ); } + + /// `Пользователь {name} оплатил план «{planName}»` + String userPaidThePlan(Object name, Object planName) { + return Intl.message( + 'Пользователь $name оплатил план «$planName»', + name: 'userPaidThePlan', + desc: '', + args: [name, planName], + ); + } + + /// `Завтра начинается план «{planName}» с пользователем {name}` + String tommorowPlanWithUserWillStart(Object planName, Object name) { + return Intl.message( + 'Завтра начинается план «$planName» с пользователем $name', + name: 'tommorowPlanWithUserWillStart', + desc: '', + args: [planName, name], + ); + } + + /// `План «{planName}» с пользователем {name} начался` + String planWithUserStarted(Object planName, Object name) { + return Intl.message( + 'План «$planName» с пользователем $name начался', + name: 'planWithUserStarted', + desc: '', + args: [planName, name], + ); + } + + /// `Вы отменили подписку на план «{planName}»` + String youCanceledSubscriptionToPlan(Object planName) { + return Intl.message( + 'Вы отменили подписку на план «$planName»', + name: 'youCanceledSubscriptionToPlan', + desc: '', + args: [planName], + ); + } + + /// `Консультант прокомментировал день {date}` + String consultantLeftComment(Object date) { + return Intl.message( + 'Консультант прокомментировал день $date', + name: 'consultantLeftComment', + desc: '', + args: [date], + ); + } + + /// `Заполните вчерашний день` + String get fillYasterday { + return Intl.message( + 'Заполните вчерашний день', + name: 'fillYasterday', + desc: '', + args: [], + ); + } + + /// `Завтра начнется прохождение плана «{planName}»` + String tomorrowYourPlanWillStart(Object planName) { + return Intl.message( + 'Завтра начнется прохождение плана «$planName»', + name: 'tomorrowYourPlanWillStart', + desc: '', + args: [planName], + ); + } + + /// `План «{planName}» начался` + String planStarted(Object planName) { + return Intl.message( + 'План «$planName» начался', + name: 'planStarted', + desc: '', + args: [planName], + ); + } + + /// `Пользователь {name} отменил подписку на план «{planName}»` + String userCanceldThePlanSubscription(Object name, Object planName) { + return Intl.message( + 'Пользователь $name отменил подписку на план «$planName»', + name: 'userCanceldThePlanSubscription', + desc: '', + args: [name, planName], + ); + } + + /// `Пользователь {name} подписался на план «{planName}»` + String userSubscribedToPlan(Object name, Object planName) { + return Intl.message( + 'Пользователь $name подписался на план «$planName»', + name: 'userSubscribedToPlan', + desc: '', + args: [name, planName], + ); + } } class AppLocalizationDelegate extends LocalizationsDelegate { diff --git a/lib/l10n/intl_en.arb b/lib/l10n/intl_en.arb index d6038cb7..08de0607 100644 --- a/lib/l10n/intl_en.arb +++ b/lib/l10n/intl_en.arb @@ -373,6 +373,7 @@ "mealComment": "Комментарий к приему пищи", "congratsYouHaveFinishedPlan": "Поздравляем, Вы закончили план", "userConfirmedPlanCompletion": "Пользователь подтвердил прохождения плана. В ближайшее время Вы получите", + "userConfirmedPlanCompletionWithoutSum": "Пользователь подтвердил прохождения плана.", "collectCharge": "Получить выплату", "chargeApproved": "Выплата одобрена", "moneySentToCard": "Средства отправлены на карту", @@ -385,6 +386,16 @@ "pleaseConfirmPlan": "Просим вас подтвердить прохождение плана", "planWillBeConfirmedAfterThreeDays": "Если вы не подтвердите, то план через три дня подтвердится автоматически", "youCanComplain": "Если вас что-то не устроило, вы можете подать жалобу", - "leaveComplainRequest": "Подать жалобу" + "leaveComplainRequest": "Подать жалобу", + "userPaidThePlan": "Пользователь {name} оплатил план «{planName}»", + "tommorowPlanWithUserWillStart": "Завтра начинается план «{planName}» с пользователем {name}", + "planWithUserStarted": "План «{planName}» с пользователем {name} начался", + "youCanceledSubscriptionToPlan": "Вы отменили подписку на план «{planName}»", + "consultantLeftComment": "Консультант прокомментировал день {date}", + "fillYasterday": "Заполните вчерашний день", + "tomorrowYourPlanWillStart": "Завтра начнется прохождение плана «{planName}»", + "planStarted": "План «{planName}» начался", + "userCanceldThePlanSubscription": "Пользователь {name} отменил подписку на план «{planName}»", + "userSubscribedToPlan": "Пользователь {name} подписался на план «{planName}»" }