diff --git a/ios/Flutter/AppFrameworkInfo.plist b/ios/Flutter/AppFrameworkInfo.plist
index 1dc6cf7..391a902 100644
--- a/ios/Flutter/AppFrameworkInfo.plist
+++ b/ios/Flutter/AppFrameworkInfo.plist
@@ -20,7 +20,5 @@
????
CFBundleVersion
1.0
- MinimumOSVersion
- 13.0
diff --git a/ios/Podfile.lock b/ios/Podfile.lock
index 6d0c378..6d7d9f1 100644
--- a/ios/Podfile.lock
+++ b/ios/Podfile.lock
@@ -103,7 +103,7 @@ PODS:
- permission_handler_apple (9.3.0):
- Flutter
- Polyline (5.1.0)
- - PostHog (3.36.0)
+ - PostHog (3.48.3)
- posthog_flutter (0.0.1):
- Flutter
- FlutterMacOS
@@ -211,7 +211,7 @@ SPEC CHECKSUMS:
path_provider_foundation: 0b743cbb62d8e47eab856f09262bb8c1ddcfe6ba
permission_handler_apple: 9878588469a2b0d0fc1e048d9f43605f92e6cec2
Polyline: 2a1f29f87f8d9b7de868940f4f76deb8c678a5b1
- PostHog: 8e04df01d59971f1fd85d0273e18ba61076fef72
+ PostHog: 38e00e9376b90f0d92de958105029e4736ffe7f9
posthog_flutter: c7888a7df4a4eb0a6473c50da2e12520c33408c4
PromisesObjC: f5707f49cb48b9636751c5b2e7d227e43fba9f47
share_plus: 8b6f8b3447e494cca5317c8c3073de39b3600d1f
diff --git a/ios/Runner/AppDelegate.swift b/ios/Runner/AppDelegate.swift
index 6266644..c30b367 100644
--- a/ios/Runner/AppDelegate.swift
+++ b/ios/Runner/AppDelegate.swift
@@ -2,12 +2,15 @@ import Flutter
import UIKit
@main
-@objc class AppDelegate: FlutterAppDelegate {
+@objc class AppDelegate: FlutterAppDelegate, FlutterImplicitEngineDelegate {
override func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
- GeneratedPluginRegistrant.register(with: self)
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
}
+
+ func didInitializeImplicitFlutterEngine(_ engineBridge: FlutterImplicitEngineBridge) {
+ GeneratedPluginRegistrant.register(with: engineBridge.pluginRegistry)
+ }
}
diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist
index d485836..b86db19 100644
--- a/ios/Runner/Info.plist
+++ b/ios/Runner/Info.plist
@@ -42,6 +42,27 @@
NSPhotoLibraryUsageDescription
This app may need photo access to upload images from gallery.
+ UIApplicationSceneManifest
+
+ UIApplicationSupportsMultipleScenes
+
+ UISceneConfigurations
+
+ UIWindowSceneSessionRoleApplication
+
+
+ UISceneClassName
+ UIWindowScene
+ UISceneConfigurationName
+ flutter
+ UISceneDelegateClassName
+ FlutterSceneDelegate
+ UISceneStoryboardFile
+ Main
+
+
+
+
UIApplicationSupportsIndirectInputEvents
UIBackgroundModes
diff --git a/lib/core/common/utils/name_validation.dart b/lib/core/common/utils/name_validation.dart
index ba34e75..8b75f8b 100644
--- a/lib/core/common/utils/name_validation.dart
+++ b/lib/core/common/utils/name_validation.dart
@@ -1,20 +1,16 @@
import 'package:flutter/material.dart';
import 'package:form_builder_validators/form_builder_validators.dart';
-final RegExp _latinNamePattern =
- RegExp(r"^[A-Za-zÀ-ÖØ-öø-ÿ][A-Za-zÀ-ÖØ-öø-ÿ .'\-]*$");
+final RegExp _latinNamePattern = RegExp(r"^[A-Za-zÀ-ÖØ-öø-ÿ][A-Za-zÀ-ÖØ-öø-ÿ .'\-]*$");
-FormFieldValidator nameValidator({
- required String errorText,
- bool required = false,
-}) {
+FormFieldValidator nameValidator({required String errorText, bool required = false}) {
final validators = >[];
if (required) {
validators.add(FormBuilderValidators.required());
}
validators.add(
FormBuilderValidators.match(
- _latinNamePattern.pattern,
+ _latinNamePattern.pattern as RegExp,
errorText: errorText,
checkNullOrEmpty: false,
),
diff --git a/lib/core/config/theme/theme.dart b/lib/core/config/theme/theme.dart
index 07feda8..cd7004e 100644
--- a/lib/core/config/theme/theme.dart
+++ b/lib/core/config/theme/theme.dart
@@ -32,6 +32,7 @@ class AnyStepTheme {
primary: AnyStepColors.blueBright,
onPrimaryContainer: AnyStepColors.navyDark,
secondary: AnyStepColors.blueDeep,
+ tertiary: AnyStepColors.purple,
secondaryContainer: AnyStepColors.blueBright20,
onSecondary: AnyStepColors.white,
surface: AnyStepColors.white,
@@ -92,6 +93,7 @@ class AnyStepTheme {
primary: AnyStepColors.blueBright,
onPrimaryContainer: AnyStepColors.navyDark,
secondary: AnyStepColors.blueDeep,
+ tertiary: AnyStepColors.purple,
secondaryContainer: AnyStepColors.blueBright20,
onSecondary: AnyStepColors.pureWhite,
surface: AnyStepColors.pureWhite,
diff --git a/lib/core/features/dashboard/presentation/widgets/dashboard_metrics_card.dart b/lib/core/features/dashboard/presentation/widgets/dashboard_metrics_card.dart
index 64c03d4..c71fbde 100644
--- a/lib/core/features/dashboard/presentation/widgets/dashboard_metrics_card.dart
+++ b/lib/core/features/dashboard/presentation/widgets/dashboard_metrics_card.dart
@@ -335,21 +335,27 @@ class _MonthlyHoursChart extends StatelessWidget {
.map((p) => p.hours)
.fold(0, (max, v) => v > max ? v : max)
.clamp(1, double.infinity);
+ final minX = 0.0;
+ final maxX = (points.length - 1).toDouble().clamp(1, double.infinity);
return Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
label,
- style: Theme.of(
- context,
- ).textTheme.labelMedium?.copyWith(color: foreground ?? theme.colorScheme.onSurface),
+ style: Theme.of(context).textTheme.labelMedium?.copyWith(
+ color: foreground ?? theme.colorScheme.onSurface,
+ fontWeight: FontWeight.w600,
+ ),
),
const SizedBox(height: AnyStepSpacing.sm8),
- SizedBox(
+ Container(
height: 120,
+ padding: const .symmetric(horizontal: AnyStepSpacing.md16),
child: LineChart(
LineChartData(
+ minX: minX,
+ maxX: maxX.toDouble(),
minY: 0,
maxY: maxY * 1.2,
gridData: const FlGridData(show: false),
@@ -388,19 +394,14 @@ class _MonthlyHoursChart extends StatelessWidget {
FlSpot(i.toDouble(), points[i].hours),
],
isCurved: true,
- barWidth: 2.5,
- color: foreground ?? theme.colorScheme.primary,
+ curveSmoothness: 0.25,
+ preventCurveOverShooting: true,
+ barWidth: 3,
+ color: theme.colorScheme.primary,
dotData: const FlDotData(show: false),
belowBarData: BarAreaData(
show: true,
- gradient: LinearGradient(
- colors: [
- (foreground ?? theme.colorScheme.primary).withAlpha(80),
- Colors.transparent,
- ],
- begin: Alignment.topCenter,
- end: Alignment.bottomCenter,
- ),
+ color: theme.colorScheme.primary.withAlpha(40),
),
),
],
diff --git a/lib/core/features/events/presentation/event_detail/event_detail_screen.dart b/lib/core/features/events/presentation/event_detail/event_detail_screen.dart
index 233a13f..9b70d3e 100644
--- a/lib/core/features/events/presentation/event_detail/event_detail_screen.dart
+++ b/lib/core/features/events/presentation/event_detail/event_detail_screen.dart
@@ -14,7 +14,6 @@ import 'package:anystep/core/features/events/utils/launch_calendar.dart';
import 'package:anystep/core/features/profile/data/current_user.dart';
import 'package:anystep/core/features/profile/domain/user_role.dart';
import 'package:anystep/core/features/screens.dart';
-import 'package:anystep/core/features/user_events/presentation/add_attendee_screen.dart';
import 'package:anystep/core/features/user_events/presentation/sign_up_button.dart';
import 'package:anystep/core/common/widgets/share_button.dart';
import 'package:anystep/l10n/generated/app_localizations.dart';
diff --git a/lib/core/features/events/presentation/widgets/attendance_list.dart b/lib/core/features/events/presentation/widgets/attendance_list.dart
index f7dc4c0..8ceaa1d 100644
--- a/lib/core/features/events/presentation/widgets/attendance_list.dart
+++ b/lib/core/features/events/presentation/widgets/attendance_list.dart
@@ -152,8 +152,8 @@ class AttendanceList extends ConsumerWidget {
trailing: AnyStepBadge(
color: switch (user.role) {
UserRole.admin => Theme.of(context).colorScheme.tertiary,
- UserRole.board => Theme.of(context).colorScheme.primary,
- UserRole.volunteer => Theme.of(context).colorScheme.secondary,
+ UserRole.board => Theme.of(context).colorScheme.secondary,
+ UserRole.volunteer => Theme.of(context).colorScheme.primary,
},
child: Text(
user.role.displayName,
diff --git a/lib/core/features/events/presentation/widgets/sign_up_list.dart b/lib/core/features/events/presentation/widgets/sign_up_list.dart
index afdfe12..0046985 100644
--- a/lib/core/features/events/presentation/widgets/sign_up_list.dart
+++ b/lib/core/features/events/presentation/widgets/sign_up_list.dart
@@ -73,8 +73,8 @@ class SignUpList extends ConsumerWidget {
trailing: AnyStepBadge(
color: switch (user.role) {
UserRole.admin => Theme.of(context).colorScheme.tertiary,
- UserRole.board => Theme.of(context).colorScheme.primary,
- UserRole.volunteer => Theme.of(context).colorScheme.secondary,
+ UserRole.board => Theme.of(context).colorScheme.secondary,
+ UserRole.volunteer => Theme.of(context).colorScheme.primary,
},
child: Text(
user.role.displayName,
diff --git a/lib/core/features/profile/presentation/user_feed.dart b/lib/core/features/profile/presentation/user_feed.dart
index bfe0705..10c100f 100644
--- a/lib/core/features/profile/presentation/user_feed.dart
+++ b/lib/core/features/profile/presentation/user_feed.dart
@@ -33,8 +33,8 @@ class UserFeed extends StatelessWidget {
trailing: AnyStepBadge(
color: switch (user.role) {
UserRole.admin => Theme.of(context).colorScheme.tertiary,
- UserRole.board => Theme.of(context).colorScheme.primary,
- UserRole.volunteer => Theme.of(context).colorScheme.secondary,
+ UserRole.board => Theme.of(context).colorScheme.secondary,
+ UserRole.volunteer => Theme.of(context).colorScheme.primary,
},
child: Text(
user.role.displayName,
diff --git a/lib/core/features/reports/data/volunteer_hours_providers.dart b/lib/core/features/reports/data/volunteer_hours_providers.dart
index 515c2e8..49af7a0 100644
--- a/lib/core/features/reports/data/volunteer_hours_providers.dart
+++ b/lib/core/features/reports/data/volunteer_hours_providers.dart
@@ -196,8 +196,29 @@ List _aggregateMonthlyHours(List report
}
List _takeLastMonths(List points, {int maxMonths = 6}) {
- if (points.length <= maxMonths) return points;
- return points.sublist(points.length - maxMonths);
+ final now = DateTime.now();
+ final baseMonths = [
+ for (var i = maxMonths - 1; i >= 0; i--) DateTime(now.year, now.month - i),
+ ];
+ if (points.isEmpty) {
+ return baseMonths.map((month) => MonthlyHoursPoint(month: month, hours: 0)).toList();
+ }
+
+ final Map byMonth = {
+ for (final p in points)
+ "${p.month.year.toString().padLeft(4, '0')}-${p.month.month.toString().padLeft(2, '0')}"
+ : p.hours,
+ };
+ return baseMonths
+ .map(
+ (month) => MonthlyHoursPoint(
+ month: month,
+ hours:
+ byMonth["${month.year.toString().padLeft(4, '0')}-${month.month.toString().padLeft(2, '0')}"] ??
+ 0,
+ ),
+ )
+ .toList();
}
@riverpod
diff --git a/lib/core/features/user_events/presentation/add_attendee_screen.dart b/lib/core/features/user_events/presentation/add_attendee_screen.dart
index 7eb3486..3053fec 100644
--- a/lib/core/features/user_events/presentation/add_attendee_screen.dart
+++ b/lib/core/features/user_events/presentation/add_attendee_screen.dart
@@ -1,5 +1,4 @@
import 'package:anystep/core/common/constants/spacing.dart';
-import 'package:anystep/core/common/widgets/inputs/inputs.dart';
import 'package:anystep/core/common/widgets/widgets.dart';
import 'package:anystep/core/features/events/data/event_repository.dart';
import 'package:anystep/core/features/profile/domain/user_model.dart';
@@ -129,8 +128,7 @@ class _AddAttendeeScreenState extends ConsumerState {
labelText: loc.checkInLabel,
initialValue: initialCheckIn,
enabled: _attended,
- validator:
- _attended ? FormBuilderValidators.required() : null,
+ validator: _attended ? FormBuilderValidators.required() : null,
),
),
const SizedBox(width: AnyStepSpacing.sm2),
@@ -140,21 +138,22 @@ class _AddAttendeeScreenState extends ConsumerState {
labelText: loc.checkOutLabel,
initialValue: initialCheckOut,
enabled: _attended,
- validator:
- _attended
- ? FormBuilderValidators.compose([
- FormBuilderValidators.required(),
- (val) {
- final checkIn =
- formKey.currentState?.fields['checkInAt']?.value
- as DateTime?;
- if (val != null && checkIn != null && val.isBefore(checkIn)) {
- return 'Check out must be after check in';
- }
- return null;
- },
- ])
- : null,
+ validator: _attended
+ ? FormBuilderValidators.compose([
+ FormBuilderValidators.required(),
+ (val) {
+ final checkIn =
+ formKey.currentState?.fields['checkInAt']?.value
+ as DateTime?;
+ if (val != null &&
+ checkIn != null &&
+ val.isBefore(checkIn)) {
+ return 'Check out must be after check in';
+ }
+ return null;
+ },
+ ])
+ : null,
),
),
],
diff --git a/lib/core/features/user_events/presentation/attendee_search_form.dart b/lib/core/features/user_events/presentation/attendee_search_form.dart
index 476498d..19db1bd 100644
--- a/lib/core/features/user_events/presentation/attendee_search_form.dart
+++ b/lib/core/features/user_events/presentation/attendee_search_form.dart
@@ -11,11 +11,7 @@ import 'package:go_router/go_router.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart';
class AttendeeSearchForm extends ConsumerStatefulWidget {
- const AttendeeSearchForm({
- super.key,
- required this.eventId,
- required this.onUserSelected,
- });
+ const AttendeeSearchForm({super.key, required this.eventId, required this.onUserSelected});
final int eventId;
final ValueChanged onUserSelected;
@@ -56,7 +52,7 @@ class _AttendeeSearchFormState extends ConsumerState {
if (!mounted || userId == null) return;
final user = await ref.read(userRepositoryProvider).get(documentId: userId);
widget.onUserSelected(user);
- if (mounted && context.canPop()) {
+ if (mounted && context.mounted && context.canPop()) {
context.pop();
}
},
diff --git a/pubspec.lock b/pubspec.lock
index f371ee6..2a88001 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -165,10 +165,10 @@ packages:
dependency: transitive
description:
name: characters
- sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803
+ sha256: faf38497bda5ead2a8c7615f4f7939df04333478bf32e4173fcb06d428b5716b
url: "https://pub.dev"
source: hosted
- version: "1.4.0"
+ version: "1.4.1"
checked_yaml:
dependency: transitive
description:
@@ -757,14 +757,6 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.5"
- js:
- dependency: transitive
- description:
- name: js
- sha256: "53385261521cc4a0c4658fd0ad07a7d14591cf8fc33abbceae306ddb974888dc"
- url: "https://pub.dev"
- source: hosted
- version: "0.7.2"
json_annotation:
dependency: "direct main"
description:
@@ -841,18 +833,18 @@ packages:
dependency: transitive
description:
name: matcher
- sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2
+ sha256: dc0b7dc7651697ea4ff3e69ef44b0407ea32c487a39fff6a4004fa585e901861
url: "https://pub.dev"
source: hosted
- version: "0.12.17"
+ version: "0.12.19"
material_color_utilities:
dependency: transitive
description:
name: material_color_utilities
- sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
+ sha256: "9c337007e82b1889149c82ed242ed1cb24a66044e30979c44912381e9be4c48b"
url: "https://pub.dev"
source: hosted
- version: "0.11.1"
+ version: "0.13.0"
meta:
dependency: transitive
description:
@@ -1406,26 +1398,26 @@ packages:
dependency: transitive
description:
name: test
- sha256: "75906bf273541b676716d1ca7627a17e4c4070a3a16272b7a3dc7da3b9f3f6b7"
+ sha256: "280d6d890011ca966ad08df7e8a4ddfab0fb3aa49f96ed6de56e3521347a9ae7"
url: "https://pub.dev"
source: hosted
- version: "1.26.3"
+ version: "1.30.0"
test_api:
dependency: transitive
description:
name: test_api
- sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55
+ sha256: "8161c84903fd860b26bfdefb7963b3f0b68fee7adea0f59ef805ecca346f0c7a"
url: "https://pub.dev"
source: hosted
- version: "0.7.7"
+ version: "0.7.10"
test_core:
dependency: transitive
description:
name: test_core
- sha256: "0cc24b5ff94b38d2ae73e1eb43cc302b77964fbf67abad1e296025b78deb53d0"
+ sha256: "0381bd1585d1a924763c308100f2138205252fb90c9d4eeaf28489ee65ccde51"
url: "https://pub.dev"
source: hosted
- version: "0.6.12"
+ version: "0.6.16"
typed_data:
dependency: transitive
description: