diff --git a/assets/images/ic_premium.svg b/assets/images/ic_premium.svg
new file mode 100644
index 000000000..de7db9011
--- /dev/null
+++ b/assets/images/ic_premium.svg
@@ -0,0 +1,24 @@
+
diff --git a/core/lib/presentation/extensions/color_extension.dart b/core/lib/presentation/extensions/color_extension.dart
index 4e72c1f4e..68b941201 100644
--- a/core/lib/presentation/extensions/color_extension.dart
+++ b/core/lib/presentation/extensions/color_extension.dart
@@ -243,6 +243,7 @@ extension AppColor on Color {
static const m3Tertiary20 = Color(0xFF71767C);
static const m3Neutral70 = Color(0xFFAEAAAE);
static const m3Neutral90 = Color(0xFFE6E1E5);
+ static const m3SysLightSecondaryBlue = Color(0xFF5C9CE6);
static const grayBackgroundColor = Color(0xFFF3F6F9);
static const m3SurfaceBackground = Color(0xFF1C1B1F);
static const warningColor = Color(0xFFFFC107);
diff --git a/core/lib/presentation/resources/image_paths.dart b/core/lib/presentation/resources/image_paths.dart
index cf39c7e4f..24f6a9d6d 100644
--- a/core/lib/presentation/resources/image_paths.dart
+++ b/core/lib/presentation/resources/image_paths.dart
@@ -239,6 +239,7 @@ class ImagePaths {
String get icTwakeWorkplace => _getIconPath('icon_twp.png');
String get animLottieTmail => _getAnimationPath('lottie-tmail.json');
String get icExpandArrows => _getImagePath('ic_expand_arrows.svg');
+ String get icPremium => _getImagePath('ic_premium.svg');
String _getImagePath(String imageName) {
return AssetsPaths.images + imageName;
diff --git a/core/lib/presentation/views/dialog/confirm_dialog_button.dart b/core/lib/presentation/views/dialog/confirm_dialog_button.dart
index dcf166f37..20b1a8e5c 100644
--- a/core/lib/presentation/views/dialog/confirm_dialog_button.dart
+++ b/core/lib/presentation/views/dialog/confirm_dialog_button.dart
@@ -1,11 +1,19 @@
+import 'package:core/presentation/extensions/color_extension.dart';
import 'package:core/presentation/utils/theme_utils.dart';
import 'package:flutter/material.dart';
+import 'package:flutter_svg/flutter_svg.dart';
class ConfirmDialogButton extends StatelessWidget {
final String label;
final Color? backgroundColor;
final Color? textColor;
final Color? borderColor;
+ final String? icon;
+ final double? iconSize;
+ final Color? iconColor;
+ final TextStyle? textStyle;
+ final double? radius;
+ final EdgeInsetsGeometry? padding;
final VoidCallback? onTapAction;
const ConfirmDialogButton({
@@ -14,6 +22,12 @@ class ConfirmDialogButton extends StatelessWidget {
this.backgroundColor,
this.textColor,
this.borderColor,
+ this.icon,
+ this.iconSize,
+ this.iconColor,
+ this.textStyle,
+ this.radius,
+ this.padding,
this.onTapAction,
});
@@ -21,11 +35,11 @@ class ConfirmDialogButton extends StatelessWidget {
Widget build(BuildContext context) {
final outlineBorder = borderColor != null
? RoundedRectangleBorder(
- borderRadius: const BorderRadius.all(Radius.circular(100)),
+ borderRadius: BorderRadius.all(Radius.circular(radius ?? 100)),
side: BorderSide(width: 1, color: borderColor!),
)
- : const RoundedRectangleBorder(
- borderRadius: BorderRadius.all(Radius.circular(100)),
+ : RoundedRectangleBorder(
+ borderRadius: BorderRadius.all(Radius.circular(radius ?? 100)),
);
return TextButton(
@@ -33,15 +47,37 @@ class ConfirmDialogButton extends StatelessWidget {
backgroundColor: backgroundColor,
overlayColor: Theme.of(context).colorScheme.outline.withValues(alpha: 0.08),
shape: outlineBorder,
- padding: const EdgeInsets.symmetric(horizontal: 10),
+ padding: padding ?? const EdgeInsets.symmetric(horizontal: 10),
),
onPressed: onTapAction,
- child: Text(
- label,
- style: ThemeUtils.textStyleM3LabelLarge(color: textColor),
- maxLines: 1,
- overflow: TextOverflow.ellipsis,
- ),
+ child: icon == null
+ ? Text(
+ label,
+ style: textStyle ?? ThemeUtils.textStyleM3LabelLarge(color: textColor),
+ maxLines: 1,
+ overflow: TextOverflow.ellipsis,
+ )
+ : Row(
+ mainAxisSize: MainAxisSize.min,
+ children: [
+ SvgPicture.asset(
+ icon!,
+ width: iconSize ?? 14,
+ height: iconSize ?? 14,
+ fit: BoxFit.fill,
+ colorFilter: iconColor?.toInt() == Colors.transparent.toInt()
+ ? null
+ : iconColor?.asFilter() ?? AppColor.primaryMain.asFilter(),
+ ),
+ const SizedBox(width: 8),
+ Text(
+ label,
+ style: textStyle ?? ThemeUtils.textStyleM3LabelLarge(color: textColor),
+ maxLines: 1,
+ overflow: TextOverflow.ellipsis,
+ )
+ ],
+ ),
);
}
}
diff --git a/lib/features/base/widget/premium/increase_space_button_widget.dart b/lib/features/base/widget/premium/increase_space_button_widget.dart
new file mode 100644
index 000000000..c356b0b6e
--- /dev/null
+++ b/lib/features/base/widget/premium/increase_space_button_widget.dart
@@ -0,0 +1,41 @@
+import 'package:core/presentation/extensions/color_extension.dart';
+import 'package:core/presentation/resources/image_paths.dart';
+import 'package:core/presentation/utils/theme_utils.dart';
+import 'package:core/presentation/views/dialog/confirm_dialog_button.dart';
+import 'package:flutter/material.dart';
+import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
+
+class IncreaseSpaceButtonWidget extends StatelessWidget {
+ final ImagePaths imagePaths;
+ final VoidCallback onTapAction;
+ final EdgeInsetsGeometry? margin;
+
+ const IncreaseSpaceButtonWidget({
+ super.key,
+ required this.imagePaths,
+ required this.onTapAction,
+ this.margin,
+ });
+
+ @override
+ Widget build(BuildContext context) {
+ return Container(
+ constraints: const BoxConstraints(minWidth: 193),
+ height: 44,
+ margin: margin,
+ child: ConfirmDialogButton(
+ label: AppLocalizations.of(context).increaseYourSpace,
+ borderColor: AppColor.m3SysLightSecondaryBlue,
+ icon: imagePaths.icPremium,
+ iconSize: 22,
+ iconColor: Colors.transparent,
+ textStyle: ThemeUtils.textStyleM3BodyMedium.copyWith(
+ color: AppColor.blue700,
+ ),
+ radius: 7,
+ padding: const EdgeInsetsDirectional.symmetric(horizontal: 8),
+ onTapAction: onTapAction,
+ ),
+ );
+ }
+}
diff --git a/lib/features/mailbox/presentation/mailbox_view_web.dart b/lib/features/mailbox/presentation/mailbox_view_web.dart
index de0226924..1824717d0 100644
--- a/lib/features/mailbox/presentation/mailbox_view_web.dart
+++ b/lib/features/mailbox/presentation/mailbox_view_web.dart
@@ -5,8 +5,10 @@ import 'package:flutter/gestures.dart';
import 'package:flutter/material.dart';
import 'package:get/get.dart';
import 'package:tmail_ui_user/features/base/widget/application_version_widget.dart';
+import 'package:tmail_ui_user/features/base/widget/premium/increase_space_button_widget.dart';
import 'package:tmail_ui_user/features/base/widget/scrollbar_list_view.dart';
import 'package:tmail_ui_user/features/mailbox/presentation/base_mailbox_view.dart';
+import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/extensions/validate_saas_premium_available_extension.dart';
import 'package:tmail_ui_user/features/quotas/presentation/quotas_view.dart';
import 'package:tmail_ui_user/main/localizations/app_localizations.dart';
@@ -35,6 +37,23 @@ class MailboxView extends BaseMailboxView {
: _buildListMailbox(context),
),
const QuotasView(),
+ Obx(() {
+ final isPremiumAvailable =
+ controller.mailboxDashBoardController.isPremiumAvailable;
+
+ if (isPremiumAvailable) {
+ return IncreaseSpaceButtonWidget(
+ imagePaths: controller.imagePaths,
+ margin: const EdgeInsetsDirectional.only(
+ start: 26,
+ bottom: 8,
+ ),
+ onTapAction: () {},
+ );
+ } else {
+ return const SizedBox.shrink();
+ }
+ }),
Container(
alignment: isDesktop
? AlignmentDirectional.center
diff --git a/lib/features/mailbox_dashboard/presentation/extensions/validate_saas_premium_available_extension.dart b/lib/features/mailbox_dashboard/presentation/extensions/validate_saas_premium_available_extension.dart
new file mode 100644
index 000000000..dbd779522
--- /dev/null
+++ b/lib/features/mailbox_dashboard/presentation/extensions/validate_saas_premium_available_extension.dart
@@ -0,0 +1,16 @@
+import 'package:tmail_ui_user/features/home/domain/extensions/session_extensions.dart';
+import 'package:tmail_ui_user/features/mailbox_dashboard/presentation/controller/mailbox_dashboard_controller.dart';
+
+extension ValidateSaasPremiumAvailableExtension on MailboxDashBoardController {
+ bool get isPremiumAvailable {
+ if (accountId.value == null || sessionCurrent == null) {
+ return false;
+ }
+
+ final saasAccount = sessionCurrent?.getSaaSAccountCapability(
+ accountId.value!,
+ );
+
+ return saasAccount?.isPremiumAvailable ?? false;
+ }
+}
diff --git a/lib/l10n/intl_messages.arb b/lib/l10n/intl_messages.arb
index 0126f7589..2a85c1168 100644
--- a/lib/l10n/intl_messages.arb
+++ b/lib/l10n/intl_messages.arb
@@ -1,5 +1,5 @@
{
- "@@last_modified": "2025-08-18T13:21:17.206262",
+ "@@last_modified": "2025-09-09T13:12:15.883838",
"initializing_data": "Initializing data...",
"@initializing_data": {
"type": "text",
@@ -4671,5 +4671,11 @@
"type": "text",
"placeholders_order": [],
"placeholders": {}
+ },
+ "increaseYourSpace": "Increase your space",
+ "@increaseYourSpace": {
+ "type": "text",
+ "placeholders_order": [],
+ "placeholders": {}
}
-}
+}
\ No newline at end of file
diff --git a/lib/main/localizations/app_localizations.dart b/lib/main/localizations/app_localizations.dart
index ab0390d5f..6f3835612 100644
--- a/lib/main/localizations/app_localizations.dart
+++ b/lib/main/localizations/app_localizations.dart
@@ -4928,4 +4928,11 @@ class AppLocalizations {
name: 'archiveMessagesFailed',
);
}
+
+ String get increaseYourSpace {
+ return Intl.message(
+ 'Increase your space',
+ name: 'increaseYourSpace',
+ );
+ }
}
diff --git a/model/lib/saas/saas_account_capability.dart b/model/lib/saas/saas_account_capability.dart
index c388438ee..15f2edccd 100644
--- a/model/lib/saas/saas_account_capability.dart
+++ b/model/lib/saas/saas_account_capability.dart
@@ -19,6 +19,8 @@ class SaaSAccountCapability extends CapabilityProperties {
return SaaSAccountCapability.fromJson(json);
}
+ bool get isPremiumAvailable => canUpgrade;
+
@override
List