diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index a0415ce..b005a9f 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -108,5 +108,9 @@ "eventCreatedSuccess": "Event created successfully", "isPaidLabel": "Is the event paid?", "selectPointonMapLabel": "Select location on map", - "locationRequiredError": "Please enter a location" + "locationRequiredError": "Please enter a location", + "eventsCount": "Events", + "followersCount": "Followers", + "followingCount": "Following", + "yourEvents": "Your events" } diff --git a/lib/l10n/app_pl.arb b/lib/l10n/app_pl.arb index 9a90486..c0424f8 100644 --- a/lib/l10n/app_pl.arb +++ b/lib/l10n/app_pl.arb @@ -108,5 +108,9 @@ "eventCreatedSuccess": "Wydarzenie zostało utworzone pomyślnie", "isPaidLabel": "Czy wydarzenie jest płatne?", "selectPointonMapLabel": "Wybierz lokalizacje na mapie", - "locationRequiredError": "Podaj lokalizację" + "locationRequiredError": "Podaj lokalizację", + "eventsCount": "Wydarzenia", + "followersCount": "Obserwujący", + "followingCount": "Obserwowani", + "yourEvents": "Twoje wydarzenia" } diff --git a/lib/src/features/auth/presentation/controllers/auth_controller.dart b/lib/src/features/auth/presentation/controllers/auth_controller.dart index dca856f..0b4e03b 100644 --- a/lib/src/features/auth/presentation/controllers/auth_controller.dart +++ b/lib/src/features/auth/presentation/controllers/auth_controller.dart @@ -17,6 +17,7 @@ import 'package:interns2025b_mobile/src/features/auth/domain/usecases/login_usec import 'package:interns2025b_mobile/src/features/auth/domain/usecases/logout_usecase.dart'; import 'package:interns2025b_mobile/src/features/auth/domain/usecases/register_usecase.dart'; import 'package:interns2025b_mobile/src/shared/domain/models/user_model.dart'; +import 'package:interns2025b_mobile/src/shared/presentation/theme/app_colors.dart'; import 'package:shared_preferences/shared_preferences.dart'; class AuthController extends AsyncNotifier { @@ -160,7 +161,7 @@ class AuthController extends AsyncNotifier { void _showError(BuildContext context, String message) { ScaffoldMessenger.of(context).showSnackBar( - SnackBar(content: Text(message), backgroundColor: Colors.red), + SnackBar(content: Text(message), backgroundColor: AppColors.red), ); } diff --git a/lib/src/features/event/presentation/pages/event_creation_page.dart b/lib/src/features/event/presentation/pages/event_creation_page.dart index 27d47e4..1adc03b 100644 --- a/lib/src/features/event/presentation/pages/event_creation_page.dart +++ b/lib/src/features/event/presentation/pages/event_creation_page.dart @@ -1,5 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:interns2025b_mobile/l10n/generated/app_localizations.dart'; import 'package:interns2025b_mobile/src/shared/presentation/widgets/navigation_bar.dart'; import 'package:interns2025b_mobile/src/features/event/presentation/widgets/event_creation_form.dart'; @@ -43,9 +44,10 @@ class _EventCreationPageState extends ConsumerState { @override Widget build(BuildContext context) { + final localizations = AppLocalizations.of(context)!; return Scaffold( appBar: AppBar( - title: const Text('Create Event'), + title: Text(localizations.createEventButton), ), body: SingleChildScrollView( padding: const EdgeInsets.all(16), diff --git a/lib/src/features/event/presentation/widgets/event_card.dart b/lib/src/features/event/presentation/widgets/event_card.dart index 918c0ca..bdd4b51 100644 --- a/lib/src/features/event/presentation/widgets/event_card.dart +++ b/lib/src/features/event/presentation/widgets/event_card.dart @@ -3,6 +3,7 @@ import 'package:interns2025b_mobile/l10n/generated/app_localizations.dart'; import 'package:interns2025b_mobile/src/features/event/presentation/widgets/event_data_time_row.dart'; import 'package:interns2025b_mobile/src/features/event/presentation/widgets/event_image.dart'; import 'package:interns2025b_mobile/src/features/event/presentation/widgets/event_price_tag.dart'; +import 'package:interns2025b_mobile/src/shared/domain/models/age_category.dart'; import 'package:interns2025b_mobile/src/shared/domain/models/event_model.dart'; import 'package:interns2025b_mobile/src/shared/presentation/theme/app_colors.dart'; @@ -61,7 +62,7 @@ class EventCard extends StatelessWidget { Padding( padding: const EdgeInsets.only(top: 4.0), child: Text( - event.ageCategory!, + AgeCategory.fromString(event.ageCategory).localized(context), style: Theme.of(context).textTheme.bodySmall?.copyWith( color: AppColors.shadeGrey700, ), diff --git a/lib/src/features/event/presentation/widgets/event_form/age_category_dropdown.dart b/lib/src/features/event/presentation/widgets/event_form/age_category_dropdown.dart index e11cb70..b6d550c 100644 --- a/lib/src/features/event/presentation/widgets/event_form/age_category_dropdown.dart +++ b/lib/src/features/event/presentation/widgets/event_form/age_category_dropdown.dart @@ -27,7 +27,7 @@ class AgeCategoryDropdown extends StatelessWidget { items: AgeCategory.values.map((category) { return DropdownMenuItem( value: category, - child: Text(category.label(context)), + child: Text(category.localized(context)), ); }).toList(), onChanged: onChanged, diff --git a/lib/src/features/event/presentation/widgets/event_form/is_paid_checkbox.dart b/lib/src/features/event/presentation/widgets/event_form/is_paid_checkbox.dart new file mode 100644 index 0000000..2eb50d7 --- /dev/null +++ b/lib/src/features/event/presentation/widgets/event_form/is_paid_checkbox.dart @@ -0,0 +1,34 @@ +import 'package:flutter/material.dart'; +import 'package:interns2025b_mobile/l10n/generated/app_localizations.dart'; +import 'package:interns2025b_mobile/src/shared/presentation/widgets/labeled_text.dart'; + +class IsPaidCheckbox extends StatelessWidget { + final bool value; + final ValueChanged onChanged; + + const IsPaidCheckbox({ + super.key, + required this.value, + required this.onChanged, + }); + + @override + Widget build(BuildContext context) { + final localizations = AppLocalizations.of(context)!; + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox(height: 20), + LabeledText(localizations.isPaidLabel), + CheckboxListTile( + value: value, + onChanged: onChanged, + controlAffinity: ListTileControlAffinity.leading, + contentPadding: EdgeInsets.zero, + ), + const SizedBox(height: 8), + ], + ); + } +} diff --git a/lib/src/features/profile/domain/utils/event_sorter.dart b/lib/src/features/profile/domain/utils/event_sorter.dart new file mode 100644 index 0000000..9b03b50 --- /dev/null +++ b/lib/src/features/profile/domain/utils/event_sorter.dart @@ -0,0 +1,21 @@ +import 'package:interns2025b_mobile/src/shared/domain/models/event_model.dart'; +import 'package:interns2025b_mobile/src/shared/domain/models/event_status.dart'; + +void sortEvents(List events) { + events.sort((a, b) { + bool aIsEndedOrCanceled = + a.status == EventStatus.canceled || a.status == EventStatus.ended; + bool bIsEndedOrCanceled = + b.status == EventStatus.canceled || b.status == EventStatus.ended; + + if (aIsEndedOrCanceled && !bIsEndedOrCanceled) { + return 1; + } else if (!aIsEndedOrCanceled && bIsEndedOrCanceled) { + return -1; + } + + final aDate = a.createdAt ?? DateTime.fromMillisecondsSinceEpoch(0); + final bDate = b.createdAt ?? DateTime.fromMillisecondsSinceEpoch(0); + return bDate.compareTo(aDate); + }); +} diff --git a/lib/src/features/profile/presentation/widgets/delete_user_request_button.dart b/lib/src/features/profile/presentation/widgets/delete_user_request_button.dart index 65c767b..9241c8c 100644 --- a/lib/src/features/profile/presentation/widgets/delete_user_request_button.dart +++ b/lib/src/features/profile/presentation/widgets/delete_user_request_button.dart @@ -41,7 +41,7 @@ class DeleteUserRequestButton extends ConsumerWidget { }, child: Text( localizations.confirm, - style: const TextStyle(color: Colors.red), + style: TextStyle(color: AppColors.red), ), ), ], diff --git a/lib/src/features/profile/presentation/widgets/event_status_badge.dart b/lib/src/features/profile/presentation/widgets/event_status_badge.dart new file mode 100644 index 0000000..d351e4e --- /dev/null +++ b/lib/src/features/profile/presentation/widgets/event_status_badge.dart @@ -0,0 +1,43 @@ +import 'package:flutter/material.dart'; +import 'package:interns2025b_mobile/src/shared/domain/models/event_status.dart'; +import 'package:interns2025b_mobile/src/shared/presentation/theme/app_colors.dart'; + +class EventStatusBadge extends StatelessWidget { + final EventStatus status; + + const EventStatusBadge({super.key, required this.status}); + + Color get _backgroundColor { + switch (status) { + case EventStatus.draft: + return AppColors.draftEventBadge.withValues(alpha: 0.5); + case EventStatus.published: + return AppColors.blueLabelBackground.withValues(alpha: 0.2); + case EventStatus.ongoing: + return AppColors.primary.withValues(alpha: 0.2); + case EventStatus.canceled: + case EventStatus.ended: + return AppColors.grey.withValues(alpha: 0.2); + } + } + + @override + Widget build(BuildContext context) { + final color = _backgroundColor; + return Container( + padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 4), + decoration: BoxDecoration( + color: color, + borderRadius: BorderRadius.circular(8), + ), + child: Text( + status.label(context), + style: const TextStyle( + color: AppColors.black, + fontSize: 12, + fontWeight: FontWeight.w500, + ), + ), + ); + } +} diff --git a/lib/src/features/profile/presentation/widgets/profile_event_card.dart b/lib/src/features/profile/presentation/widgets/profile_event_card.dart new file mode 100644 index 0000000..de9a48f --- /dev/null +++ b/lib/src/features/profile/presentation/widgets/profile_event_card.dart @@ -0,0 +1,67 @@ +import 'package:flutter/material.dart'; +import 'package:interns2025b_mobile/src/features/event/presentation/widgets/event_data_time_row.dart'; +import 'package:interns2025b_mobile/src/features/event/presentation/widgets/event_image.dart'; +import 'package:interns2025b_mobile/src/features/profile/presentation/widgets/event_status_badge.dart'; +import 'package:interns2025b_mobile/src/shared/domain/models/event_model.dart'; +import 'package:interns2025b_mobile/src/shared/presentation/theme/app_colors.dart'; + +class ProfileEventCard extends StatelessWidget { + final Event event; + + const ProfileEventCard({super.key, required this.event}); + + @override + Widget build(BuildContext context) { + return ConstrainedBox( + constraints: const BoxConstraints(maxWidth: 210), + child: Card( + shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(12)), + clipBehavior: Clip.antiAlias, + elevation: 2, + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + AspectRatio( + aspectRatio: 16 / 9, + child: EventImage( + imageUrl: event.imageUrl, + width: double.infinity, + borderRadius: BorderRadius.zero, + ), + ), + Container( + color: AppColors.backgroundLight, + padding: const EdgeInsets.fromLTRB(12, 10, 12, 12), + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + EventDateTimeRow(date: event.start), + const SizedBox(height: 6), + Row( + mainAxisAlignment: MainAxisAlignment.spaceBetween, + children: [ + Expanded( + child: Text( + event.title, + style: Theme.of(context).textTheme.bodyMedium + ?.copyWith( + fontWeight: FontWeight.bold, + color: AppColors.black, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ), + const SizedBox(width: 8), + EventStatusBadge(status: event.status), + ], + ), + ], + ), + ), + ], + ), + ), + ); + } +} diff --git a/lib/src/features/profile/presentation/widgets/profile_events_section.dart b/lib/src/features/profile/presentation/widgets/profile_events_section.dart new file mode 100644 index 0000000..0c72778 --- /dev/null +++ b/lib/src/features/profile/presentation/widgets/profile_events_section.dart @@ -0,0 +1,54 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:interns2025b_mobile/l10n/generated/app_localizations.dart'; +import 'package:interns2025b_mobile/src/core/routes/app_routes.dart'; +import 'package:interns2025b_mobile/src/features/profile/presentation/widgets/profile_event_card.dart'; +import 'package:interns2025b_mobile/src/shared/domain/models/event_model.dart'; + +class ProfileEventsSection extends ConsumerWidget { + final List events; + + const ProfileEventsSection({super.key, required this.events}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + if (events.isEmpty) { + return const SizedBox(); + } + + final localizations = AppLocalizations.of(context)!; + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + const SizedBox(height: 24), + Text( + localizations.yourEvents, + style: Theme.of(context).textTheme.titleMedium, + ), + const SizedBox(height: 12), + SizedBox( + height: 200, + child: ListView.separated( + scrollDirection: Axis.horizontal, + itemCount: events.length, + separatorBuilder: (_, _) => const SizedBox(width: 12), + itemBuilder: (context, index) { + final event = events[index]; + return GestureDetector( + onTap: () { + Navigator.pushNamed( + context, + AppRoutes.eventDetails, + arguments: event.id, + ); + }, + child: ProfileEventCard(event: event), + ); + }, + ), + ), + ], + ); + } +} diff --git a/lib/src/features/profile/presentation/widgets/profile_info_card.dart b/lib/src/features/profile/presentation/widgets/profile_info_card.dart index 4e027ae..12d42a5 100644 --- a/lib/src/features/profile/presentation/widgets/profile_info_card.dart +++ b/lib/src/features/profile/presentation/widgets/profile_info_card.dart @@ -11,22 +11,16 @@ class ProfileInfoCard extends ConsumerWidget { @override Widget build(BuildContext context, WidgetRef ref) { - final screenHeight = MediaQuery.of(context).size.height; - final topPadding = MediaQuery.of(context).padding.top; - return Stack( clipBehavior: Clip.none, children: [ Container( width: double.infinity, - constraints: BoxConstraints( - minHeight: screenHeight - topPadding - 170, - ), padding: const EdgeInsets.fromLTRB(24, 60, 24, 24), decoration: BoxDecoration( color: AppColors.backgroundLight, - borderRadius: BorderRadius.vertical(top: Radius.circular(50)), - boxShadow: [ + borderRadius: const BorderRadius.vertical(top: Radius.circular(50)), + boxShadow: const [ BoxShadow( color: Colors.black12, blurRadius: 12, @@ -34,17 +28,16 @@ class ProfileInfoCard extends ConsumerWidget { ), ], ), - child: Column( - mainAxisSize: MainAxisSize.min, - crossAxisAlignment: CrossAxisAlignment.stretch, - children: [ - const ProfileInfoContent(), - const SizedBox(height: 24), - const Padding( - padding: EdgeInsets.symmetric(horizontal: 0), - child: ProfileEditSection(), - ), - ], + child: SingleChildScrollView( + physics: const BouncingScrollPhysics(), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: const [ + ProfileInfoContent(), + SizedBox(height: 24), + ProfileEditSection(), + ], + ), ), ), Positioned( diff --git a/lib/src/features/profile/presentation/widgets/profile_info_content.dart b/lib/src/features/profile/presentation/widgets/profile_info_content.dart index 19efdcf..084a3d1 100644 --- a/lib/src/features/profile/presentation/widgets/profile_info_content.dart +++ b/lib/src/features/profile/presentation/widgets/profile_info_content.dart @@ -1,6 +1,9 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:interns2025b_mobile/src/features/profile/domain/utils/event_sorter.dart'; import 'package:interns2025b_mobile/src/features/profile/presentation/providers/profile_controller_provider.dart'; +import 'package:interns2025b_mobile/src/features/profile/presentation/widgets/profile_events_section.dart'; +import 'package:interns2025b_mobile/src/features/profile/presentation/widgets/profile_stats.dart'; class ProfileInfoContent extends ConsumerWidget { const ProfileInfoContent({super.key}); @@ -11,18 +14,31 @@ class ProfileInfoContent extends ConsumerWidget { if (user == null) return const SizedBox(); + final sortedEvents = [...user.events]; + sortEvents(sortedEvents); + return Column( + crossAxisAlignment: CrossAxisAlignment.center, children: [ const SizedBox(height: 24), Text( - user.lastName?.trim().isNotEmpty == true - ? '${user.firstName} ${user.lastName}' - : user.firstName, - style: Theme.of( - context, - ).textTheme.headlineSmall?.copyWith(fontWeight: FontWeight.bold), + user.firstName, + style: Theme.of(context).textTheme.headlineSmall?.copyWith( + fontWeight: FontWeight.bold, + ), + textAlign: TextAlign.center, ), + if ((user.lastName?.trim().isNotEmpty ?? false)) + Text( + user.lastName!, + style: Theme.of(context).textTheme.headlineSmall?.copyWith( + fontWeight: FontWeight.bold, + ), + textAlign: TextAlign.center, + ), const SizedBox(height: 24), + const ProfileStats(), + ProfileEventsSection(events: sortedEvents), ], ); } diff --git a/lib/src/features/profile/presentation/widgets/profile_stats.dart b/lib/src/features/profile/presentation/widgets/profile_stats.dart new file mode 100644 index 0000000..a81385f --- /dev/null +++ b/lib/src/features/profile/presentation/widgets/profile_stats.dart @@ -0,0 +1,34 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:interns2025b_mobile/l10n/generated/app_localizations.dart'; +import 'package:interns2025b_mobile/src/features/profile/presentation/providers/profile_controller_provider.dart'; +import 'package:interns2025b_mobile/src/features/profile/presentation/widgets/stat_tile.dart'; + +class ProfileStats extends ConsumerWidget { + const ProfileStats({super.key}); + + @override + Widget build(BuildContext context, WidgetRef ref) { + final user = ref.watch(profileControllerProvider).user; + final localizations = AppLocalizations.of(context)!; + + if (user == null) return const SizedBox(); + + return Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + StatTile(label: localizations.eventsCount, value: user.eventsCount), + const SizedBox(width: 16), + StatTile( + label: localizations.followersCount, + value: user.followersCount, + ), + const SizedBox(width: 16), + StatTile( + label: localizations.followingCount, + value: user.followingCount, + ), + ], + ); + } +} diff --git a/lib/src/features/profile/presentation/widgets/stat_tile.dart b/lib/src/features/profile/presentation/widgets/stat_tile.dart new file mode 100644 index 0000000..4e21886 --- /dev/null +++ b/lib/src/features/profile/presentation/widgets/stat_tile.dart @@ -0,0 +1,35 @@ +import 'package:flutter/material.dart'; + +class StatTile extends StatelessWidget { + final String label; + final int value; + + const StatTile({ + super.key, + required this.label, + required this.value, + }); + + @override + Widget build(BuildContext context) { + return Column( + mainAxisSize: MainAxisSize.min, + crossAxisAlignment: CrossAxisAlignment.center, + children: [ + Text( + '$value', + style: Theme.of(context) + .textTheme + .titleMedium + ?.copyWith(fontWeight: FontWeight.w600), + textAlign: TextAlign.center, + ), + Text( + label, + style: Theme.of(context).textTheme.bodySmall, + textAlign: TextAlign.center, + ), + ], + ); + } +} diff --git a/lib/src/shared/domain/models/age_category.dart b/lib/src/shared/domain/models/age_category.dart index 72fbcf4..4c69c92 100644 --- a/lib/src/shared/domain/models/age_category.dart +++ b/lib/src/shared/domain/models/age_category.dart @@ -1,10 +1,13 @@ import 'package:flutter/material.dart'; import 'package:interns2025b_mobile/l10n/generated/app_localizations.dart'; -enum AgeCategory { everyone, adults, youth, children } +enum AgeCategory { + everyone, + adults, + youth, + children; -extension AgeCategoryX on AgeCategory { - String label(BuildContext context) { + String localized(BuildContext context) { final localizations = AppLocalizations.of(context)!; switch (this) { case AgeCategory.everyone: @@ -18,9 +21,9 @@ extension AgeCategoryX on AgeCategory { } } - static AgeCategory? fromLabel(BuildContext context, String label) { + static AgeCategory fromString(String? value) { return AgeCategory.values.firstWhere( - (e) => e.label(context) == label, + (e) => e.name.toLowerCase() == value?.toLowerCase(), orElse: () => AgeCategory.everyone, ); } diff --git a/lib/src/shared/domain/models/event_model.dart b/lib/src/shared/domain/models/event_model.dart index 934bc3c..2597e90 100644 --- a/lib/src/shared/domain/models/event_model.dart +++ b/lib/src/shared/domain/models/event_model.dart @@ -49,7 +49,7 @@ class Event { factory Event.fromJson(Map json) { final rawOwnerType = json['owner_type'] as String?; - final ownerType = OwnerTypeX.fromRaw(rawOwnerType); + final ownerType = OwnerType.fromRaw(rawOwnerType); final ownerJson = json['owner']; EventOwner? parsedOwner; @@ -80,7 +80,7 @@ class Event { : null, isPaid: json['is_paid'], price: json['price'] != null ? (json['price'] as num).toDouble() : null, - status: EventStatusX.fromString(json['status']), + status: EventStatus.fromString(json['status']), imageUrl: json['image_url'], ageCategory: json['age_category'], ownerType: ownerType, diff --git a/lib/src/shared/domain/models/event_status.dart b/lib/src/shared/domain/models/event_status.dart index 45aba6c..2b84e34 100644 --- a/lib/src/shared/domain/models/event_status.dart +++ b/lib/src/shared/domain/models/event_status.dart @@ -1,9 +1,13 @@ import 'package:flutter/material.dart'; import 'package:interns2025b_mobile/l10n/generated/app_localizations.dart'; -enum EventStatus { draft, published, ongoing, ended, canceled } +enum EventStatus { + draft, + published, + ongoing, + ended, + canceled; -extension EventStatusX on EventStatus { String label(BuildContext context) { final localizations = AppLocalizations.of(context)!; switch (this) { diff --git a/lib/src/shared/domain/models/navbar_pages.dart b/lib/src/shared/domain/models/navbar_pages.dart index 7527064..ec0b604 100644 --- a/lib/src/shared/domain/models/navbar_pages.dart +++ b/lib/src/shared/domain/models/navbar_pages.dart @@ -2,9 +2,11 @@ import 'package:flutter/material.dart'; import 'package:interns2025b_mobile/l10n/generated/app_localizations.dart'; import 'package:interns2025b_mobile/src/core/routes/app_routes.dart'; -enum NavbarPages { events, addEvent, profile } +enum NavbarPages { + events, + addEvent, + profile; -extension NavbarPagesExtension on NavbarPages { String get routeName { switch (this) { case NavbarPages.events: diff --git a/lib/src/shared/domain/models/owner_type.dart b/lib/src/shared/domain/models/owner_type.dart index 5cbd96f..f6a3c7a 100644 --- a/lib/src/shared/domain/models/owner_type.dart +++ b/lib/src/shared/domain/models/owner_type.dart @@ -1,6 +1,7 @@ -enum OwnerType { user, organization } +enum OwnerType { + user, + organization; -extension OwnerTypeX on OwnerType { static OwnerType fromRaw(String? value) { switch (value?.toLowerCase()) { case 'user': diff --git a/lib/src/shared/domain/models/user_model.dart b/lib/src/shared/domain/models/user_model.dart index 288641c..ae410f8 100644 --- a/lib/src/shared/domain/models/user_model.dart +++ b/lib/src/shared/domain/models/user_model.dart @@ -1,3 +1,4 @@ +import 'package:interns2025b_mobile/src/shared/domain/models/event_model.dart'; import 'package:interns2025b_mobile/src/shared/domain/models/event_owner.dart'; class User implements EventOwner { @@ -10,6 +11,10 @@ class User implements EventOwner { final DateTime? updatedAt; @override final String? avatarUrl; + final int eventsCount; + final int followersCount; + final int followingCount; + final List events; User({ required this.id, @@ -20,6 +25,10 @@ class User implements EventOwner { required this.createdAt, required this.updatedAt, this.avatarUrl, + required this.eventsCount, + required this.followersCount, + required this.followingCount, + required this.events, }); factory User.fromJson(Map json) { @@ -38,6 +47,12 @@ class User implements EventOwner { updatedAt: json['updated_at'] != null ? DateTime.tryParse(json['updated_at']) : null, + eventsCount: json['events_count'] ?? 0, + followersCount: json['followers_count'] ?? 0, + followingCount: json['following_count'] ?? 0, + events: (json['events'] as List? ?? []) + .map((e) => Event.fromJson(e as Map)) + .toList(), ); } diff --git a/lib/src/shared/presentation/theme/app_colors.dart b/lib/src/shared/presentation/theme/app_colors.dart index aa95de3..c1beb09 100644 --- a/lib/src/shared/presentation/theme/app_colors.dart +++ b/lib/src/shared/presentation/theme/app_colors.dart @@ -28,4 +28,5 @@ class AppColors { static const borderEnable = Color(0x29687182); static const borderFill = Color(0xFFF5F5F5); static const red = Color(0xFFF44336); + static const draftEventBadge = Color(0xFFFFEB3B); } diff --git a/lib/src/shared/presentation/theme/app_theme.dart b/lib/src/shared/presentation/theme/app_theme.dart index 92ac637..3a39cfe 100644 --- a/lib/src/shared/presentation/theme/app_theme.dart +++ b/lib/src/shared/presentation/theme/app_theme.dart @@ -42,7 +42,7 @@ class AppTheme { timePickerTheme: TimePickerThemeData( hourMinuteColor: WidgetStateColor.resolveWith((Set states) { if (states.contains(WidgetState.selected)) { - return Colors.green.withValues(alpha: 0.3); + return AppColors.greenLightLabelBackground.withValues(alpha: 0.3); } return Colors.transparent; }), diff --git a/lib/src/shared/presentation/widgets/button.dart b/lib/src/shared/presentation/widgets/button.dart index 0a838bb..b0eea65 100644 --- a/lib/src/shared/presentation/widgets/button.dart +++ b/lib/src/shared/presentation/widgets/button.dart @@ -19,8 +19,8 @@ class Button extends StatelessWidget { this.fullWidth = false, this.icon, this.isLoading = false, - this.backgroundColor = Colors.black, - this.foregroundColor = Colors.white, + this.backgroundColor = AppColors.black, + this.foregroundColor = AppColors.backgroundLight, this.iconColor, this.isOutlined = false, });