Integrate Google Maps Places Autocomplete for hub address validation and enhance UI button styles
This commit is contained in:
@@ -132,10 +132,14 @@ class UiButton extends StatelessWidget {
|
||||
@override
|
||||
/// Builds the button UI.
|
||||
Widget build(BuildContext context) {
|
||||
final ButtonStyle? mergedStyle = style != null
|
||||
? _getSizeStyle().merge(style)
|
||||
: _getSizeStyle();
|
||||
|
||||
final Widget button = buttonBuilder(
|
||||
context,
|
||||
onPressed,
|
||||
style,
|
||||
mergedStyle,
|
||||
_buildButtonContent(),
|
||||
);
|
||||
|
||||
@@ -146,6 +150,65 @@ class UiButton extends StatelessWidget {
|
||||
return button;
|
||||
}
|
||||
|
||||
/// Gets the style based on the button size.
|
||||
ButtonStyle _getSizeStyle() {
|
||||
switch (size) {
|
||||
case UiButtonSize.extraSmall:
|
||||
return ButtonStyle(
|
||||
padding: WidgetStateProperty.all(
|
||||
const EdgeInsets.symmetric(
|
||||
horizontal: UiConstants.space2,
|
||||
vertical: UiConstants.space1,
|
||||
),
|
||||
),
|
||||
minimumSize: WidgetStateProperty.all(const Size(0, 28)),
|
||||
maximumSize: WidgetStateProperty.all(const Size(double.infinity, 28)),
|
||||
textStyle: WidgetStateProperty.all(
|
||||
const TextStyle(fontSize: 12, fontWeight: FontWeight.w500),
|
||||
),
|
||||
);
|
||||
case UiButtonSize.small:
|
||||
return ButtonStyle(
|
||||
padding: WidgetStateProperty.all(
|
||||
const EdgeInsets.symmetric(
|
||||
horizontal: UiConstants.space3,
|
||||
vertical: UiConstants.space2,
|
||||
),
|
||||
),
|
||||
minimumSize: WidgetStateProperty.all(const Size(0, 36)),
|
||||
maximumSize: WidgetStateProperty.all(const Size(double.infinity, 36)),
|
||||
textStyle: WidgetStateProperty.all(
|
||||
const TextStyle(fontSize: 13, fontWeight: FontWeight.w500),
|
||||
),
|
||||
);
|
||||
case UiButtonSize.medium:
|
||||
return ButtonStyle(
|
||||
padding: WidgetStateProperty.all(
|
||||
const EdgeInsets.symmetric(
|
||||
horizontal: UiConstants.space4,
|
||||
vertical: UiConstants.space3,
|
||||
),
|
||||
),
|
||||
minimumSize: WidgetStateProperty.all(const Size(0, 44)),
|
||||
maximumSize: WidgetStateProperty.all(const Size(double.infinity, 44)),
|
||||
);
|
||||
case UiButtonSize.large:
|
||||
return ButtonStyle(
|
||||
padding: WidgetStateProperty.all(
|
||||
const EdgeInsets.symmetric(
|
||||
horizontal: UiConstants.space6,
|
||||
vertical: UiConstants.space4,
|
||||
),
|
||||
),
|
||||
minimumSize: WidgetStateProperty.all(const Size(0, 52)),
|
||||
maximumSize: WidgetStateProperty.all(const Size(double.infinity, 52)),
|
||||
textStyle: WidgetStateProperty.all(
|
||||
const TextStyle(fontSize: 16, fontWeight: FontWeight.w600),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// Builds the button content with optional leading and trailing icons.
|
||||
Widget _buildButtonContent() {
|
||||
if (child != null) {
|
||||
@@ -229,6 +292,9 @@ class UiButton extends StatelessWidget {
|
||||
|
||||
/// Defines the size of a [UiButton].
|
||||
enum UiButtonSize {
|
||||
/// Extra small button (very compact)
|
||||
extraSmall,
|
||||
|
||||
/// Small button (compact)
|
||||
small,
|
||||
|
||||
|
||||
@@ -43,14 +43,11 @@ class ReorderWidget extends StatelessWidget {
|
||||
),
|
||||
if (subtitle != null) ...<Widget>[
|
||||
const SizedBox(height: UiConstants.space1),
|
||||
Text(
|
||||
subtitle!,
|
||||
style: UiTypography.body2r.textSecondary,
|
||||
),
|
||||
Text(subtitle!, style: UiTypography.body2r.textSecondary),
|
||||
],
|
||||
const SizedBox(height: UiConstants.space2),
|
||||
SizedBox(
|
||||
height: 140,
|
||||
height: 164,
|
||||
child: ListView.separated(
|
||||
scrollDirection: Axis.horizontal,
|
||||
itemCount: recentOrders.length,
|
||||
@@ -67,13 +64,7 @@ class ReorderWidget extends StatelessWidget {
|
||||
decoration: BoxDecoration(
|
||||
color: UiColors.white,
|
||||
borderRadius: UiConstants.radiusLg,
|
||||
border: Border.all(color: UiColors.border),
|
||||
boxShadow: <BoxShadow>[
|
||||
BoxShadow(
|
||||
color: UiColors.black.withValues(alpha: 0.02),
|
||||
blurRadius: 4,
|
||||
),
|
||||
],
|
||||
border: Border.all(color: UiColors.border, width: 0.6),
|
||||
),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
@@ -129,10 +120,7 @@ class ReorderWidget extends StatelessWidget {
|
||||
style: UiTypography.body1b,
|
||||
),
|
||||
Text(
|
||||
i18n.per_hr(
|
||||
amount: order.hourlyRate.toString(),
|
||||
) +
|
||||
' · ${order.hours}h',
|
||||
'${i18n.per_hr(amount: order.hourlyRate.toString())} · ${order.hours}h',
|
||||
style: UiTypography.footnote2r.textSecondary,
|
||||
),
|
||||
],
|
||||
@@ -145,25 +133,28 @@ class ReorderWidget extends StatelessWidget {
|
||||
_Badge(
|
||||
icon: UiIcons.success,
|
||||
text: order.type,
|
||||
color: const Color(0xFF2563EB),
|
||||
bg: const Color(0xFF2563EB),
|
||||
textColor: UiColors.white,
|
||||
color: UiColors.primary,
|
||||
bg: UiColors.buttonSecondaryStill,
|
||||
textColor: UiColors.primary,
|
||||
),
|
||||
const SizedBox(width: UiConstants.space2),
|
||||
_Badge(
|
||||
icon: UiIcons.building,
|
||||
text: '${order.workers}',
|
||||
color: const Color(0xFF334155),
|
||||
bg: const Color(0xFFF1F5F9),
|
||||
textColor: const Color(0xFF334155),
|
||||
color: UiColors.textSecondary,
|
||||
bg: UiColors.buttonSecondaryStill,
|
||||
textColor: UiColors.textSecondary,
|
||||
),
|
||||
],
|
||||
),
|
||||
const Spacer(),
|
||||
SizedBox(
|
||||
height: 28,
|
||||
width: double.infinity,
|
||||
child: ElevatedButton.icon(
|
||||
|
||||
UiButton.secondary(
|
||||
size: UiButtonSize.small,
|
||||
text: i18n.reorder_button,
|
||||
leadingIcon: UiIcons.zap,
|
||||
iconSize: 12,
|
||||
fullWidth: true,
|
||||
onPressed: () => onReorderPressed(<String, dynamic>{
|
||||
'orderId': order.orderId,
|
||||
'title': order.title,
|
||||
@@ -173,21 +164,6 @@ class ReorderWidget extends StatelessWidget {
|
||||
'workers': order.workers,
|
||||
'type': order.type,
|
||||
}),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: UiColors.primary,
|
||||
foregroundColor: UiColors.white,
|
||||
padding: EdgeInsets.zero,
|
||||
shape: RoundedRectangleBorder(
|
||||
borderRadius: UiConstants.radiusMd,
|
||||
),
|
||||
elevation: 0,
|
||||
),
|
||||
icon: const Icon(UiIcons.zap, size: 12),
|
||||
label: Text(
|
||||
i18n.reorder_button,
|
||||
style: UiTypography.footnote1m,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -34,9 +34,6 @@
|
||||
**Happy Path Test Cases:**
|
||||
- [ ] Sign in with valid email and password displays home dashboard
|
||||
- [ ] Sign up with business details creates account and navigates to home
|
||||
- [ ] Sign in with Google OAuth completes authentication flow
|
||||
- [ ] Sign in with Apple OAuth completes authentication flow
|
||||
- [ ] Session persists after app restart
|
||||
|
||||
**Validation & Error States:**
|
||||
- [ ] Invalid email format shows validation error
|
||||
@@ -44,24 +41,10 @@
|
||||
- [ ] Weak password in sign-up shows strength requirements
|
||||
- [ ] Duplicate email in sign-up shows "already registered" error
|
||||
- [ ] Empty fields show required field errors
|
||||
- [ ] Network error displays retry option
|
||||
|
||||
**Loading & Empty States:**
|
||||
- [ ] Loading spinner displays during authentication
|
||||
- [ ] OAuth redirect shows appropriate loading state
|
||||
- [ ] Backend timeout shows error message
|
||||
|
||||
**State Persistence:**
|
||||
- [ ] Authenticated session persists after app background → foreground
|
||||
- [ ] Session expires appropriately after logout
|
||||
- [ ] Device restart maintains logged-in state
|
||||
|
||||
**Backend Dependency Validation:**
|
||||
- [ ] `getUserById` returns user data for authenticated UID
|
||||
- [ ] `createBusiness` successfully creates business entity
|
||||
- [ ] `createUser` links user to business
|
||||
- [ ] `getBusinessesByUserId` retrieves business profile
|
||||
- [ ] Failed business creation triggers `deleteBusiness` rollback
|
||||
|
||||
---
|
||||
|
||||
@@ -77,28 +60,14 @@
|
||||
- [ ] Recent reorders display completed shift roles
|
||||
- [ ] Quick action buttons navigate to correct features
|
||||
- [ ] Drag-and-drop widget reordering works correctly
|
||||
- [ ] Dashboard refreshes on pull-to-refresh gesture
|
||||
|
||||
**Validation & Error States:**
|
||||
- [ ] Empty state shows "No data available" when no orders exist
|
||||
- [ ] API error shows retry option
|
||||
- [ ] Negative spending values display correctly
|
||||
|
||||
**Loading & Empty States:**
|
||||
- [ ] Skeleton loaders display while fetching data
|
||||
- [ ] Empty coverage shows "No shifts today"
|
||||
- [ ] Empty reorders shows "No recent orders"
|
||||
|
||||
**State Persistence:**
|
||||
- [ ] Widget order persists after app restart
|
||||
- [ ] Dashboard data refreshes after returning from background
|
||||
|
||||
**Backend Dependency Validation:**
|
||||
- [ ] `getCompletedShiftsByBusinessId` returns spending data for date ranges
|
||||
- [ ] `listShiftRolesByBusinessAndDateRange` returns coverage stats
|
||||
- [ ] `listShiftRolesByBusinessDateRangeCompletedOrders` returns reorder suggestions
|
||||
- [ ] Business ID correctly retrieved from session
|
||||
|
||||
---
|
||||
|
||||
#### 📱 CLIENT-003: Create Order
|
||||
@@ -107,12 +76,11 @@
|
||||
**Entry Points:**
|
||||
- Home → Create Order button
|
||||
- Orders tab → + FAB button
|
||||
- Order type → Rapid / One-Time / Recurring / Permanent
|
||||
- Order type → One-Time
|
||||
|
||||
**Happy Path Test Cases:**
|
||||
- [ ] Order type selection displays all available types
|
||||
- [ ] Order type selection displays.
|
||||
- [ ] Hub selection shows list of business hubs
|
||||
- [ ] Google Places autocomplete suggests valid addresses
|
||||
- [ ] Role selection displays vendor roles
|
||||
- [ ] Position quantity can be incremented/decremented (min 1)
|
||||
- [ ] Date picker displays correct calendar
|
||||
@@ -130,7 +98,6 @@
|
||||
- [ ] Invalid date (past) shows validation error
|
||||
- [ ] Start time after end time shows validation error
|
||||
- [ ] Missing required fields prevent submission
|
||||
- [ ] Network error during submission shows retry option
|
||||
- [ ] Backend validation errors display appropriately
|
||||
|
||||
**Loading & Empty States:**
|
||||
@@ -139,17 +106,6 @@
|
||||
- [ ] Loading spinner displays during submission
|
||||
- [ ] Submission progress indicator updates
|
||||
|
||||
**State Persistence:**
|
||||
- [ ] Form data persists when navigating away and back
|
||||
- [ ] Draft order data clears after successful submission
|
||||
|
||||
**Backend Dependency Validation:**
|
||||
- [ ] `createOrder` creates order with ONE_TIME type
|
||||
- [ ] `createShift` creates shift with location and time details
|
||||
- [ ] `createShiftRole` creates positions with correct rates
|
||||
- [ ] `updateOrder` links shift to order
|
||||
- [ ] All operations complete or rollback on failure
|
||||
|
||||
---
|
||||
|
||||
#### 📱 CLIENT-004: View Orders
|
||||
@@ -171,23 +127,13 @@
|
||||
|
||||
**Validation & Error States:**
|
||||
- [ ] Invalid date selection shows error
|
||||
- [ ] Network error shows retry option
|
||||
|
||||
- [ ] Missing staff data shows placeholder
|
||||
|
||||
**Loading & Empty States:**
|
||||
- [ ] Skeleton loaders display while fetching orders
|
||||
- [ ] Empty date shows "No orders for this date"
|
||||
- [ ] Empty accepted applications shows "No confirmed staff"
|
||||
|
||||
**State Persistence:**
|
||||
- [ ] Selected date persists after navigating away
|
||||
- [ ] Order list refreshes after returning from background
|
||||
|
||||
**Backend Dependency Validation:**
|
||||
- [ ] `listShiftRolesByBusinessAndDateRange` returns orders for date range
|
||||
- [ ] `listAcceptedApplicationsByBusinessForDay` returns confirmed staff
|
||||
- [ ] Business ID correctly filtered in queries
|
||||
|
||||
---
|
||||
|
||||
#### 📱 CLIENT-005: Coverage Monitoring
|
||||
@@ -207,7 +153,6 @@
|
||||
|
||||
**Validation & Error States:**
|
||||
- [ ] Missing worker photo shows default avatar
|
||||
- [ ] Network error shows retry option
|
||||
|
||||
**Loading & Empty States:**
|
||||
- [ ] Skeleton loaders display while fetching data
|
||||
@@ -245,7 +190,6 @@
|
||||
- [ ] Zero billing shows $0.00 (not error)
|
||||
- [ ] Negative savings shows correctly
|
||||
- [ ] Missing invoice data shows placeholder
|
||||
- [ ] Network error shows retry option
|
||||
|
||||
**Loading & Empty States:**
|
||||
- [ ] Skeleton loaders display while fetching data
|
||||
@@ -289,7 +233,7 @@
|
||||
- [ ] Invalid address format shows error
|
||||
- [ ] Duplicate hub name shows warning
|
||||
- [ ] Hub with active orders prevents deletion (validation error)
|
||||
- [ ] Network error shows retry option
|
||||
|
||||
|
||||
**Loading & Empty States:**
|
||||
- [ ] Skeleton loaders display while fetching hubs
|
||||
@@ -430,7 +374,7 @@
|
||||
|
||||
**Validation & Error States:**
|
||||
- [ ] Missing shift data shows placeholder
|
||||
- [ ] Network error shows retry option
|
||||
|
||||
|
||||
**Loading & Empty States:**
|
||||
- [ ] Skeleton loaders display while fetching data
|
||||
@@ -502,7 +446,7 @@
|
||||
|
||||
**Validation & Error States:**
|
||||
- [ ] Empty tabs show appropriate empty state messages
|
||||
- [ ] Network error shows retry option
|
||||
|
||||
- [ ] Already applied shift prevents duplicate application
|
||||
- [ ] Past shifts cannot be applied to
|
||||
- [ ] Cancelled shifts show cancellation reason
|
||||
@@ -546,7 +490,7 @@
|
||||
- [ ] Changes save automatically
|
||||
|
||||
**Validation & Error States:**
|
||||
- [ ] Network error shows retry option
|
||||
|
||||
- [ ] Save failure shows error message
|
||||
|
||||
**Loading & Empty States:**
|
||||
@@ -584,7 +528,7 @@
|
||||
**Validation & Error States:**
|
||||
- [ ] No shift today shows "No shifts to clock in"
|
||||
- [ ] Already clocked in prevents duplicate clock in
|
||||
- [ ] Network error shows retry option
|
||||
|
||||
- [ ] Clock in outside shift time shows warning
|
||||
|
||||
**Loading & Empty States:**
|
||||
@@ -620,7 +564,7 @@
|
||||
**Validation & Error States:**
|
||||
- [ ] Zero earnings show $0.00 (not error)
|
||||
- [ ] Missing payment data shows placeholder
|
||||
- [ ] Network error shows retry option
|
||||
|
||||
|
||||
**Loading & Empty States:**
|
||||
- [ ] Skeleton loaders display while fetching payments
|
||||
@@ -657,7 +601,7 @@
|
||||
- [ ] Empty required fields show validation errors
|
||||
- [ ] Invalid email format shows error
|
||||
- [ ] Invalid phone format shows error
|
||||
- [ ] Network error shows retry option
|
||||
|
||||
- [ ] Photo upload failure shows error
|
||||
|
||||
**Loading & Empty States:**
|
||||
@@ -695,7 +639,7 @@
|
||||
- [ ] Empty name shows validation error
|
||||
- [ ] Invalid phone format shows error
|
||||
- [ ] At least one contact required (if applicable)
|
||||
- [ ] Network error shows retry option
|
||||
|
||||
|
||||
**Loading & Empty States:**
|
||||
- [ ] Loading spinner displays while fetching contacts
|
||||
@@ -731,7 +675,7 @@
|
||||
**Validation & Error States:**
|
||||
- [ ] At least one industry required (if applicable)
|
||||
- [ ] At least one skill required (if applicable)
|
||||
- [ ] Network error shows retry option
|
||||
|
||||
|
||||
**Loading & Empty States:**
|
||||
- [ ] Loading spinner displays while fetching data
|
||||
@@ -764,7 +708,7 @@
|
||||
**Validation & Error States:**
|
||||
- [ ] At least one attire item required (if applicable)
|
||||
- [ ] Photo upload failure shows error
|
||||
- [ ] Network error shows retry option
|
||||
|
||||
|
||||
**Loading & Empty States:**
|
||||
- [ ] Loading spinner displays while fetching options
|
||||
@@ -802,7 +746,7 @@
|
||||
- [ ] Empty account number shows validation error
|
||||
- [ ] Invalid account number format shows error
|
||||
- [ ] Duplicate account shows warning
|
||||
- [ ] Network error shows retry option
|
||||
|
||||
|
||||
**Loading & Empty States:**
|
||||
- [ ] Loading spinner displays while fetching accounts
|
||||
@@ -835,7 +779,7 @@
|
||||
|
||||
**Validation & Error States:**
|
||||
- [ ] Missing attendance data shows "Not recorded"
|
||||
- [ ] Network error shows retry option
|
||||
|
||||
|
||||
**Loading & Empty States:**
|
||||
- [ ] Skeleton loaders display while fetching records
|
||||
@@ -872,7 +816,7 @@
|
||||
- [ ] Invalid SSN format shows error
|
||||
- [ ] Invalid date format shows error
|
||||
- [ ] Signature required validation
|
||||
- [ ] Network error shows retry option
|
||||
|
||||
|
||||
**Loading & Empty States:**
|
||||
- [ ] Loading spinner displays while fetching forms
|
||||
@@ -906,7 +850,7 @@
|
||||
|
||||
**Validation & Error States:**
|
||||
- [ ] Missing documents show incomplete status
|
||||
- [ ] Network error shows retry option
|
||||
|
||||
|
||||
**Loading & Empty States:**
|
||||
- [ ] Skeleton loaders display while fetching documents
|
||||
@@ -936,7 +880,7 @@
|
||||
|
||||
**Validation & Error States:**
|
||||
- [ ] Missing certificates show placeholder
|
||||
- [ ] Network error shows retry option
|
||||
|
||||
|
||||
**Loading & Empty States:**
|
||||
- [ ] Skeleton loaders display while fetching certificates
|
||||
|
||||
Reference in New Issue
Block a user