Merge branch 'dev' into Issues-on-payments-timecard-availability-screens-01-02-03-04
This commit is contained in:
@@ -166,9 +166,10 @@ class BillingRepositoryImpl implements BillingRepository {
|
||||
}
|
||||
|
||||
fdc.Timestamp _toTimestamp(DateTime dateTime) {
|
||||
final int seconds = dateTime.millisecondsSinceEpoch ~/ 1000;
|
||||
final DateTime utc = dateTime.toUtc();
|
||||
final int seconds = utc.millisecondsSinceEpoch ~/ 1000;
|
||||
final int nanoseconds =
|
||||
(dateTime.millisecondsSinceEpoch % 1000) * 1000000;
|
||||
(utc.millisecondsSinceEpoch % 1000) * 1000000;
|
||||
return fdc.Timestamp(nanoseconds, seconds);
|
||||
}
|
||||
|
||||
|
||||
@@ -2,16 +2,16 @@ import 'package:design_system/design_system.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_modular/flutter_modular.dart';
|
||||
|
||||
import '../blocs/billing_bloc.dart';
|
||||
import '../blocs/billing_event.dart';
|
||||
import '../blocs/billing_state.dart';
|
||||
import '../widgets/billing_header.dart';
|
||||
import '../widgets/pending_invoices_section.dart';
|
||||
import '../widgets/payment_method_card.dart';
|
||||
import '../widgets/spending_breakdown_card.dart';
|
||||
import '../widgets/savings_card.dart';
|
||||
import '../widgets/invoice_history_section.dart';
|
||||
import '../widgets/export_invoices_button.dart';
|
||||
import '../widgets/payment_method_card.dart';
|
||||
import '../widgets/pending_invoices_section.dart';
|
||||
import '../widgets/savings_card.dart';
|
||||
import '../widgets/spending_breakdown_card.dart';
|
||||
|
||||
/// The entry point page for the client billing feature.
|
||||
///
|
||||
@@ -43,7 +43,6 @@ class BillingView extends StatelessWidget {
|
||||
return BlocBuilder<BillingBloc, BillingState>(
|
||||
builder: (BuildContext context, BillingState state) {
|
||||
return Scaffold(
|
||||
backgroundColor: UiColors.bgPrimary,
|
||||
body: Column(
|
||||
children: <Widget>[
|
||||
BillingHeader(
|
||||
@@ -89,9 +88,7 @@ class BillingView extends StatelessWidget {
|
||||
SavingsCard(savings: state.savings),
|
||||
const SizedBox(height: UiConstants.space6),
|
||||
InvoiceHistorySection(invoices: state.invoiceHistory),
|
||||
const SizedBox(height: UiConstants.space6),
|
||||
const ExportInvoicesButton(),
|
||||
const SizedBox(height: UiConstants.space6),
|
||||
const SizedBox(height: UiConstants.space24),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
name: billing
|
||||
description: Client Billing feature package
|
||||
publish_to: 'none'
|
||||
version: 1.0.0+1
|
||||
version: 0.0.1
|
||||
resolution: workspace
|
||||
|
||||
environment:
|
||||
|
||||
@@ -100,9 +100,10 @@ class CoverageRepositoryImpl implements CoverageRepository {
|
||||
}
|
||||
|
||||
fdc.Timestamp _toTimestamp(DateTime dateTime) {
|
||||
final int seconds = dateTime.millisecondsSinceEpoch ~/ 1000;
|
||||
final DateTime utc = dateTime.toUtc();
|
||||
final int seconds = utc.millisecondsSinceEpoch ~/ 1000;
|
||||
final int nanoseconds =
|
||||
(dateTime.millisecondsSinceEpoch % 1000) * 1000000;
|
||||
(utc.millisecondsSinceEpoch % 1000) * 1000000;
|
||||
return fdc.Timestamp(nanoseconds, seconds);
|
||||
}
|
||||
|
||||
@@ -195,7 +196,7 @@ class CoverageRepositoryImpl implements CoverageRepository {
|
||||
if (timestamp == null) {
|
||||
return null;
|
||||
}
|
||||
final DateTime date = timestamp.toDateTime();
|
||||
final DateTime date = timestamp.toDateTime().toLocal();
|
||||
final String hour = date.hour.toString().padLeft(2, '0');
|
||||
final String minute = date.minute.toString().padLeft(2, '0');
|
||||
return '$hour:$minute';
|
||||
|
||||
@@ -39,7 +39,7 @@ class CoverageShift extends Equatable {
|
||||
|
||||
/// Calculates the coverage percentage for this shift.
|
||||
int get coveragePercent {
|
||||
if (workersNeeded == 0) return 100;
|
||||
if (workersNeeded == 0) return 0;
|
||||
return ((workers.length / workersNeeded) * 100).round();
|
||||
}
|
||||
|
||||
@@ -118,7 +118,7 @@ class CoverageStats extends Equatable {
|
||||
|
||||
/// Calculates the overall coverage percentage.
|
||||
int get coveragePercent {
|
||||
if (totalNeeded == 0) return 100;
|
||||
if (totalNeeded == 0) return 0;
|
||||
return ((totalConfirmed / totalNeeded) * 100).round();
|
||||
}
|
||||
|
||||
|
||||
@@ -24,7 +24,6 @@ class CoveragePage extends StatelessWidget {
|
||||
create: (BuildContext context) => Modular.get<CoverageBloc>()
|
||||
..add(CoverageLoadRequested(date: DateTime.now())),
|
||||
child: Scaffold(
|
||||
backgroundColor: UiColors.background,
|
||||
body: BlocBuilder<CoverageBloc, CoverageState>(
|
||||
builder: (BuildContext context, CoverageState state) {
|
||||
return Column(
|
||||
@@ -114,13 +113,14 @@ class CoveragePage extends StatelessWidget {
|
||||
const SizedBox(height: UiConstants.space5),
|
||||
],
|
||||
Text(
|
||||
'Shifts',
|
||||
'Shifts (${state.shifts.length})',
|
||||
style: UiTypography.title2b.copyWith(
|
||||
color: UiColors.textPrimary,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: UiConstants.space3),
|
||||
CoverageShiftList(shifts: state.shifts),
|
||||
const SizedBox(height: 100),
|
||||
],
|
||||
),
|
||||
);
|
||||
|
||||
@@ -35,24 +35,23 @@ class CoverageShiftList extends StatelessWidget {
|
||||
if (shifts.isEmpty) {
|
||||
return Container(
|
||||
padding: const EdgeInsets.all(UiConstants.space8),
|
||||
width: double.infinity,
|
||||
decoration: BoxDecoration(
|
||||
color: UiColors.bgPopup,
|
||||
borderRadius: UiConstants.radiusLg,
|
||||
border: Border.all(color: UiColors.border),
|
||||
),
|
||||
child: Column(
|
||||
spacing: UiConstants.space4,
|
||||
children: <Widget>[
|
||||
const Icon(
|
||||
UiIcons.users,
|
||||
size: UiConstants.space12,
|
||||
color: UiColors.mutedForeground,
|
||||
color: UiColors.textSecondary,
|
||||
),
|
||||
const SizedBox(height: UiConstants.space3),
|
||||
Text(
|
||||
'No shifts scheduled for this day',
|
||||
style: UiTypography.body2r.copyWith(
|
||||
color: UiColors.mutedForeground,
|
||||
),
|
||||
style: UiTypography.body2r.textSecondary,
|
||||
),
|
||||
],
|
||||
),
|
||||
@@ -160,12 +159,15 @@ class _ShiftHeader extends StatelessWidget {
|
||||
),
|
||||
),
|
||||
child: Row(
|
||||
spacing: UiConstants.space4,
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
spacing: UiConstants.space2,
|
||||
children: <Widget>[
|
||||
Row(
|
||||
spacing: UiConstants.space2,
|
||||
children: <Widget>[
|
||||
Container(
|
||||
width: UiConstants.space2,
|
||||
@@ -175,42 +177,43 @@ class _ShiftHeader extends StatelessWidget {
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: UiConstants.space2),
|
||||
Text(
|
||||
title,
|
||||
style: UiTypography.body1b.copyWith(
|
||||
color: UiColors.textPrimary,
|
||||
),
|
||||
style: UiTypography.body1b.textPrimary,
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: UiConstants.space2),
|
||||
Row(
|
||||
Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: <Widget>[
|
||||
const Icon(
|
||||
UiIcons.mapPin,
|
||||
size: UiConstants.space3,
|
||||
color: UiColors.mutedForeground,
|
||||
Row(
|
||||
spacing: UiConstants.space1,
|
||||
children: <Widget>[
|
||||
const Icon(
|
||||
UiIcons.mapPin,
|
||||
size: UiConstants.space3,
|
||||
color: UiColors.iconSecondary,
|
||||
),
|
||||
Expanded(child: Text(
|
||||
location,
|
||||
style: UiTypography.body3r.textSecondary,
|
||||
overflow: TextOverflow.ellipsis,
|
||||
)),
|
||||
],
|
||||
),
|
||||
const SizedBox(width: UiConstants.space1),
|
||||
Text(
|
||||
location,
|
||||
style: UiTypography.body3r.copyWith(
|
||||
color: UiColors.mutedForeground,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: UiConstants.space3),
|
||||
const Icon(
|
||||
UiIcons.clock,
|
||||
size: UiConstants.space3,
|
||||
color: UiColors.mutedForeground,
|
||||
),
|
||||
const SizedBox(width: UiConstants.space1),
|
||||
Text(
|
||||
startTime,
|
||||
style: UiTypography.body3r.copyWith(
|
||||
color: UiColors.mutedForeground,
|
||||
),
|
||||
Row(
|
||||
spacing: UiConstants.space1,
|
||||
children: <Widget>[
|
||||
const Icon(
|
||||
UiIcons.clock,
|
||||
size: UiConstants.space3,
|
||||
color: UiColors.iconSecondary,
|
||||
),
|
||||
Text(
|
||||
startTime,
|
||||
style: UiTypography.body3r.textSecondary,
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
|
||||
@@ -1,650 +0,0 @@
|
||||
# Generated by pub
|
||||
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||
packages:
|
||||
async:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: async
|
||||
sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.13.0"
|
||||
auto_injector:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: auto_injector
|
||||
sha256: "1fc2624898e92485122eb2b1698dd42511d7ff6574f84a3a8606fc4549a1e8f8"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
bloc:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: bloc
|
||||
sha256: "106842ad6569f0b60297619e9e0b1885c2fb9bf84812935490e6c5275777804e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "8.1.4"
|
||||
boolean_selector:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: boolean_selector
|
||||
sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
characters:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: characters
|
||||
sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
clock:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: clock
|
||||
sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.2"
|
||||
code_assets:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: code_assets
|
||||
sha256: "83ccdaa064c980b5596c35dd64a8d3ecc68620174ab9b90b6343b753aa721687"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: collection
|
||||
sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.19.1"
|
||||
core_localization:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "../../../core_localization"
|
||||
relative: true
|
||||
source: path
|
||||
version: "0.0.1"
|
||||
crypto:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: crypto
|
||||
sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.7"
|
||||
csv:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: csv
|
||||
sha256: c6aa2679b2a18cb57652920f674488d89712efaf4d3fdf2e537215b35fc19d6c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.0.0"
|
||||
design_system:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "../../../design_system"
|
||||
relative: true
|
||||
source: path
|
||||
version: "0.0.1"
|
||||
equatable:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: equatable
|
||||
sha256: "3e0141505477fd8ad55d6eb4e7776d3fe8430be8e497ccb1521370c3f21a3e2b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.8"
|
||||
fake_async:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: fake_async
|
||||
sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.3"
|
||||
ffi:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: ffi
|
||||
sha256: d07d37192dbf97461359c1518788f203b0c9102cfd2c35a716b823741219542c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.5"
|
||||
file:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: file
|
||||
sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.0.1"
|
||||
fixnum:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: fixnum
|
||||
sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
flutter:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_bloc:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_bloc
|
||||
sha256: b594505eac31a0518bdcb4b5b79573b8d9117b193cc80cc12e17d639b10aa27a
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "8.1.6"
|
||||
flutter_lints:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: flutter_lints
|
||||
sha256: "5398f14efa795ffb7a33e9b6a08798b26a180edac4ad7db3f231e40f82ce11e1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.0.0"
|
||||
flutter_localizations:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_modular:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_modular
|
||||
sha256: "33a63d9fe61429d12b3dfa04795ed890f17d179d3d38e988ba7969651fcd5586"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.4.1"
|
||||
flutter_test:
|
||||
dependency: "direct dev"
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_web_plugins:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
font_awesome_flutter:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: font_awesome_flutter
|
||||
sha256: b9011df3a1fa02993630b8fb83526368cf2206a711259830325bab2f1d2a4eb0
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "10.12.0"
|
||||
glob:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: glob
|
||||
sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.3"
|
||||
google_fonts:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: google_fonts
|
||||
sha256: "6996212014b996eaa17074e02b1b925b212f5e053832d9048970dc27255a8fb3"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.1.0"
|
||||
hooks:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: hooks
|
||||
sha256: "5d309c86e7ce34cd8e37aa71cb30cb652d3829b900ab145e4d9da564b31d59f7"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
http:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http
|
||||
sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.6.0"
|
||||
http_parser:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http_parser
|
||||
sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.1.2"
|
||||
intl:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: intl
|
||||
sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.20.2"
|
||||
krow_core:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "../../../core"
|
||||
relative: true
|
||||
source: path
|
||||
version: "0.0.1"
|
||||
krow_data_connect:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "../../../data_connect"
|
||||
relative: true
|
||||
source: path
|
||||
version: "0.0.1"
|
||||
krow_domain:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "../../../domain"
|
||||
relative: true
|
||||
source: path
|
||||
version: "0.0.1"
|
||||
leak_tracker:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker
|
||||
sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "11.0.2"
|
||||
leak_tracker_flutter_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_flutter_testing
|
||||
sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.10"
|
||||
leak_tracker_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_testing
|
||||
sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.2"
|
||||
lints:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: lints
|
||||
sha256: c35bb79562d980e9a453fc715854e1ed39e24e7d0297a880ef54e17f9874a9d7
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "5.1.1"
|
||||
logging:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: logging
|
||||
sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
lucide_icons:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: lucide_icons
|
||||
sha256: ad24d0fd65707e48add30bebada7d90bff2a1bba0a72d6e9b19d44246b0e83c4
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.257.0"
|
||||
matcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: matcher
|
||||
sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.12.17"
|
||||
material_color_utilities:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: material_color_utilities
|
||||
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.11.1"
|
||||
meta:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: meta
|
||||
sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.17.0"
|
||||
modular_core:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: modular_core
|
||||
sha256: "1db0420a0dfb8a2c6dca846e7cbaa4ffeb778e247916dbcb27fb25aa566e5436"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.4.1"
|
||||
native_toolchain_c:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: native_toolchain_c
|
||||
sha256: "89e83885ba09da5fdf2cdacc8002a712ca238c28b7f717910b34bcd27b0d03ac"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.17.4"
|
||||
nested:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: nested
|
||||
sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
objective_c:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: objective_c
|
||||
sha256: "7fd0c4d8ac8980011753b9bdaed2bf15111365924cdeeeaeb596214ea2b03537"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "9.2.4"
|
||||
path:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path
|
||||
sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.9.1"
|
||||
path_provider:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider
|
||||
sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.5"
|
||||
path_provider_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_android
|
||||
sha256: f2c65e21139ce2c3dad46922be8272bb5963516045659e71bb16e151c93b580e
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.22"
|
||||
path_provider_foundation:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_foundation
|
||||
sha256: "2a376b7d6392d80cd3705782d2caa734ca4727776db0b6ec36ef3f1855197699"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.6.0"
|
||||
path_provider_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_linux
|
||||
sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.1"
|
||||
path_provider_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_platform_interface
|
||||
sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
path_provider_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_windows
|
||||
sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.0"
|
||||
platform:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: platform
|
||||
sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.6"
|
||||
plugin_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: plugin_platform_interface
|
||||
sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.8"
|
||||
provider:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: provider
|
||||
sha256: "4e82183fa20e5ca25703ead7e05de9e4cceed1fbd1eadc1ac3cb6f565a09f272"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.1.5+1"
|
||||
pub_semver:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pub_semver
|
||||
sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.0"
|
||||
result_dart:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: result_dart
|
||||
sha256: "0666b21fbdf697b3bdd9986348a380aa204b3ebe7c146d8e4cdaa7ce735e6054"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
shared_preferences:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences
|
||||
sha256: "2939ae520c9024cb197fc20dee269cd8cdbf564c8b5746374ec6cacdc5169e64"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.5.4"
|
||||
shared_preferences_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_android
|
||||
sha256: "83af5c682796c0f7719c2bbf74792d113e40ae97981b8f266fa84574573556bc"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.18"
|
||||
shared_preferences_foundation:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_foundation
|
||||
sha256: "4e7eaffc2b17ba398759f1151415869a34771ba11ebbccd1b0145472a619a64f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.5.6"
|
||||
shared_preferences_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_linux
|
||||
sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.1"
|
||||
shared_preferences_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_platform_interface
|
||||
sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.1"
|
||||
shared_preferences_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_web
|
||||
sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.3"
|
||||
shared_preferences_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_windows
|
||||
sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.1"
|
||||
sky_engine:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
slang:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: slang
|
||||
sha256: "13e3b6f07adc51ab751e7889647774d294cbce7a3382f81d9e5029acfe9c37b2"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.12.0"
|
||||
slang_flutter:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: slang_flutter
|
||||
sha256: "0a4545cca5404d6b7487cf61cf1fe56c52daeb08de56a7574ee8381fbad035a0"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.12.0"
|
||||
source_span:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_span
|
||||
sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.10.1"
|
||||
stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stack_trace
|
||||
sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.12.1"
|
||||
stream_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stream_channel
|
||||
sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
string_scanner:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: string_scanner
|
||||
sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.1"
|
||||
term_glyph:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: term_glyph
|
||||
sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.2"
|
||||
test_api:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.7"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: typed_data
|
||||
sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
uuid:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: uuid
|
||||
sha256: a11b666489b1954e01d992f3d601b1804a33937b5a8fe677bd26b8a9f96f96e8
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.5.2"
|
||||
vector_math:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vector_math
|
||||
sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.0"
|
||||
vm_service:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vm_service
|
||||
sha256: "45caa6c5917fa127b5dbcfbd1fa60b14e583afdc08bfc96dda38886ca252eb60"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "15.0.2"
|
||||
watcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: watcher
|
||||
sha256: "1398c9f081a753f9226febe8900fce8f7d0a67163334e1c94a2438339d79d635"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.1"
|
||||
web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: web
|
||||
sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
xdg_directories:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: xdg_directories
|
||||
sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
yaml:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: yaml
|
||||
sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.3"
|
||||
sdks:
|
||||
dart: ">=3.10.7 <4.0.0"
|
||||
flutter: ">=3.38.4"
|
||||
@@ -1,7 +1,8 @@
|
||||
name: client_coverage
|
||||
description: Client coverage feature for tracking daily shift coverage and worker status
|
||||
version: 1.0.0
|
||||
version: 0.0.1
|
||||
publish_to: none
|
||||
resolution: workspace
|
||||
|
||||
environment:
|
||||
sdk: ^3.6.0
|
||||
@@ -26,9 +27,10 @@ dependencies:
|
||||
flutter_modular: ^6.3.4
|
||||
flutter_bloc: ^8.1.6
|
||||
equatable: ^2.0.7
|
||||
intl: ^0.20.1
|
||||
intl: ^0.20.0
|
||||
firebase_data_connect: ^0.2.2+1
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
flutter_lints: ^5.0.0
|
||||
flutter_lints: ^6.0.0
|
||||
|
||||
@@ -30,7 +30,7 @@ class ClientMainPage extends StatelessWidget {
|
||||
BlocProvider.of<ClientMainCubit>(context).navigateToTab(index);
|
||||
},
|
||||
);
|
||||
},
|
||||
},
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
@@ -99,13 +99,6 @@ class ClientMainBottomBar extends StatelessWidget {
|
||||
activeColor: activeColor,
|
||||
inactiveColor: inactiveColor,
|
||||
),
|
||||
_buildNavItem(
|
||||
index: 4,
|
||||
icon: UiIcons.chart,
|
||||
label: t.client_main.tabs.reports,
|
||||
activeColor: activeColor,
|
||||
inactiveColor: inactiveColor,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
|
||||
@@ -27,26 +27,28 @@ class ClientCreateOrderRepositoryImpl
|
||||
@override
|
||||
Future<List<domain.OrderType>> getOrderTypes() {
|
||||
return Future.value(const <domain.OrderType>[
|
||||
domain.OrderType(
|
||||
id: 'rapid',
|
||||
titleKey: 'client_create_order.types.rapid',
|
||||
descriptionKey: 'client_create_order.types.rapid_desc',
|
||||
),
|
||||
domain.OrderType(
|
||||
id: 'one-time',
|
||||
titleKey: 'client_create_order.types.one_time',
|
||||
descriptionKey: 'client_create_order.types.one_time_desc',
|
||||
),
|
||||
domain.OrderType(
|
||||
id: 'recurring',
|
||||
titleKey: 'client_create_order.types.recurring',
|
||||
descriptionKey: 'client_create_order.types.recurring_desc',
|
||||
),
|
||||
domain.OrderType(
|
||||
id: 'permanent',
|
||||
titleKey: 'client_create_order.types.permanent',
|
||||
descriptionKey: 'client_create_order.types.permanent_desc',
|
||||
),
|
||||
|
||||
/// TODO: FEATURE_NOT_YET_IMPLEMENTED
|
||||
// domain.OrderType(
|
||||
// id: 'rapid',
|
||||
// titleKey: 'client_create_order.types.rapid',
|
||||
// descriptionKey: 'client_create_order.types.rapid_desc',
|
||||
// ),
|
||||
// domain.OrderType(
|
||||
// id: 'recurring',
|
||||
// titleKey: 'client_create_order.types.recurring',
|
||||
// descriptionKey: 'client_create_order.types.recurring_desc',
|
||||
// ),
|
||||
// domain.OrderType(
|
||||
// id: 'permanent',
|
||||
// titleKey: 'client_create_order.types.permanent',
|
||||
// descriptionKey: 'client_create_order.types.permanent_desc',
|
||||
// ),
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -61,12 +63,25 @@ class ClientCreateOrderRepositoryImpl
|
||||
if (vendorId == null || vendorId.isEmpty) {
|
||||
throw Exception('Vendor is missing.');
|
||||
}
|
||||
final domain.OneTimeOrderHubDetails? hub = order.hub;
|
||||
if (hub == null || hub.id.isEmpty) {
|
||||
throw Exception('Hub is missing.');
|
||||
}
|
||||
|
||||
final fdc.Timestamp orderTimestamp = _toTimestamp(order.date);
|
||||
final DateTime orderDateOnly = DateTime(
|
||||
order.date.year,
|
||||
order.date.month,
|
||||
order.date.day,
|
||||
);
|
||||
final fdc.Timestamp orderTimestamp = _toTimestamp(orderDateOnly);
|
||||
final fdc.OperationResult<dc.CreateOrderData, dc.CreateOrderVariables> orderResult = await _dataConnect
|
||||
.createOrder(businessId: businessId, orderType: dc.OrderType.ONE_TIME)
|
||||
.createOrder(
|
||||
businessId: businessId,
|
||||
orderType: dc.OrderType.ONE_TIME,
|
||||
teamHubId: hub.id,
|
||||
)
|
||||
.vendorId(vendorId)
|
||||
.location(order.location)
|
||||
.eventName(order.eventName)
|
||||
.status(dc.OrderStatus.POSTED)
|
||||
.date(orderTimestamp)
|
||||
.execute();
|
||||
@@ -86,8 +101,15 @@ class ClientCreateOrderRepositoryImpl
|
||||
final fdc.OperationResult<dc.CreateShiftData, dc.CreateShiftVariables> shiftResult = await _dataConnect
|
||||
.createShift(title: shiftTitle, orderId: orderId)
|
||||
.date(orderTimestamp)
|
||||
.location(order.location)
|
||||
.locationAddress(order.location)
|
||||
.location(hub.name)
|
||||
.locationAddress(hub.address)
|
||||
.latitude(hub.latitude)
|
||||
.longitude(hub.longitude)
|
||||
.placeId(hub.placeId)
|
||||
.city(hub.city)
|
||||
.state(hub.state)
|
||||
.street(hub.street)
|
||||
.country(hub.country)
|
||||
.status(dc.ShiftStatus.PENDING)
|
||||
.workersNeeded(workersNeeded)
|
||||
.filled(0)
|
||||
@@ -109,6 +131,10 @@ class ClientCreateOrderRepositoryImpl
|
||||
final double rate = order.roleRates[position.role] ?? 0;
|
||||
final double totalValue = rate * hours * position.count;
|
||||
|
||||
print(
|
||||
'CreateOneTimeOrder shiftRole: start=${start.toIso8601String()} end=${normalizedEnd.toIso8601String()}',
|
||||
);
|
||||
|
||||
await _dataConnect
|
||||
.createShiftRole(
|
||||
shiftId: shiftId,
|
||||
@@ -124,7 +150,7 @@ class ClientCreateOrderRepositoryImpl
|
||||
}
|
||||
|
||||
await _dataConnect
|
||||
.updateOrder(id: orderId)
|
||||
.updateOrder(id: orderId, teamHubId: hub.id)
|
||||
.shifts(fdc.AnyValue(<String>[shiftId]))
|
||||
.execute();
|
||||
}
|
||||
|
||||
@@ -13,14 +13,17 @@ class OneTimeOrderBloc extends Bloc<OneTimeOrderEvent, OneTimeOrderState> {
|
||||
: super(OneTimeOrderState.initial()) {
|
||||
on<OneTimeOrderVendorsLoaded>(_onVendorsLoaded);
|
||||
on<OneTimeOrderVendorChanged>(_onVendorChanged);
|
||||
on<OneTimeOrderHubsLoaded>(_onHubsLoaded);
|
||||
on<OneTimeOrderHubChanged>(_onHubChanged);
|
||||
on<OneTimeOrderEventNameChanged>(_onEventNameChanged);
|
||||
on<OneTimeOrderDateChanged>(_onDateChanged);
|
||||
on<OneTimeOrderLocationChanged>(_onLocationChanged);
|
||||
on<OneTimeOrderPositionAdded>(_onPositionAdded);
|
||||
on<OneTimeOrderPositionRemoved>(_onPositionRemoved);
|
||||
on<OneTimeOrderPositionUpdated>(_onPositionUpdated);
|
||||
on<OneTimeOrderSubmitted>(_onSubmitted);
|
||||
|
||||
_loadVendors();
|
||||
_loadHubs();
|
||||
}
|
||||
final CreateOneTimeOrderUseCase _createOneTimeOrderUseCase;
|
||||
final dc.ExampleConnector _dataConnect;
|
||||
@@ -63,6 +66,38 @@ class OneTimeOrderBloc extends Bloc<OneTimeOrderEvent, OneTimeOrderState> {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _loadHubs() async {
|
||||
try {
|
||||
final String? businessId = dc.ClientSessionStore.instance.session?.business?.id;
|
||||
if (businessId == null || businessId.isEmpty) {
|
||||
add(const OneTimeOrderHubsLoaded(<OneTimeOrderHubOption>[]));
|
||||
return;
|
||||
}
|
||||
final QueryResult<dc.ListTeamHubsByOwnerIdData, dc.ListTeamHubsByOwnerIdVariables>
|
||||
result = await _dataConnect.listTeamHubsByOwnerId(ownerId: businessId).execute();
|
||||
final List<OneTimeOrderHubOption> hubs = result.data.teamHubs
|
||||
.map(
|
||||
(dc.ListTeamHubsByOwnerIdTeamHubs hub) => OneTimeOrderHubOption(
|
||||
id: hub.id,
|
||||
name: hub.hubName,
|
||||
address: hub.address,
|
||||
placeId: hub.placeId,
|
||||
latitude: hub.latitude,
|
||||
longitude: hub.longitude,
|
||||
city: hub.city,
|
||||
state: hub.state,
|
||||
street: hub.street,
|
||||
country: hub.country,
|
||||
zipCode: hub.zipCode,
|
||||
),
|
||||
)
|
||||
.toList();
|
||||
add(OneTimeOrderHubsLoaded(hubs));
|
||||
} catch (_) {
|
||||
add(const OneTimeOrderHubsLoaded(<OneTimeOrderHubOption>[]));
|
||||
}
|
||||
}
|
||||
|
||||
void _onVendorsLoaded(
|
||||
OneTimeOrderVendorsLoaded event,
|
||||
Emitter<OneTimeOrderState> emit,
|
||||
@@ -88,6 +123,40 @@ class OneTimeOrderBloc extends Bloc<OneTimeOrderEvent, OneTimeOrderState> {
|
||||
_loadRolesForVendor(event.vendor.id);
|
||||
}
|
||||
|
||||
void _onHubsLoaded(
|
||||
OneTimeOrderHubsLoaded event,
|
||||
Emitter<OneTimeOrderState> emit,
|
||||
) {
|
||||
final OneTimeOrderHubOption? selectedHub =
|
||||
event.hubs.isNotEmpty ? event.hubs.first : null;
|
||||
emit(
|
||||
state.copyWith(
|
||||
hubs: event.hubs,
|
||||
selectedHub: selectedHub,
|
||||
location: selectedHub?.name ?? '',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _onHubChanged(
|
||||
OneTimeOrderHubChanged event,
|
||||
Emitter<OneTimeOrderState> emit,
|
||||
) {
|
||||
emit(
|
||||
state.copyWith(
|
||||
selectedHub: event.hub,
|
||||
location: event.hub.name,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
void _onEventNameChanged(
|
||||
OneTimeOrderEventNameChanged event,
|
||||
Emitter<OneTimeOrderState> emit,
|
||||
) {
|
||||
emit(state.copyWith(eventName: event.eventName));
|
||||
}
|
||||
|
||||
void _onDateChanged(
|
||||
OneTimeOrderDateChanged event,
|
||||
Emitter<OneTimeOrderState> emit,
|
||||
@@ -95,13 +164,6 @@ class OneTimeOrderBloc extends Bloc<OneTimeOrderEvent, OneTimeOrderState> {
|
||||
emit(state.copyWith(date: event.date));
|
||||
}
|
||||
|
||||
void _onLocationChanged(
|
||||
OneTimeOrderLocationChanged event,
|
||||
Emitter<OneTimeOrderState> emit,
|
||||
) {
|
||||
emit(state.copyWith(location: event.location));
|
||||
}
|
||||
|
||||
void _onPositionAdded(
|
||||
OneTimeOrderPositionAdded event,
|
||||
Emitter<OneTimeOrderState> emit,
|
||||
@@ -149,10 +211,28 @@ class OneTimeOrderBloc extends Bloc<OneTimeOrderEvent, OneTimeOrderState> {
|
||||
final Map<String, double> roleRates = <String, double>{
|
||||
for (final OneTimeOrderRoleOption role in state.roles) role.id: role.costPerHour,
|
||||
};
|
||||
final OneTimeOrderHubOption? selectedHub = state.selectedHub;
|
||||
if (selectedHub == null) {
|
||||
throw Exception('Hub is missing.');
|
||||
}
|
||||
final OneTimeOrder order = OneTimeOrder(
|
||||
date: state.date,
|
||||
location: state.location,
|
||||
location: selectedHub.name,
|
||||
positions: state.positions,
|
||||
hub: OneTimeOrderHubDetails(
|
||||
id: selectedHub.id,
|
||||
name: selectedHub.name,
|
||||
address: selectedHub.address,
|
||||
placeId: selectedHub.placeId,
|
||||
latitude: selectedHub.latitude,
|
||||
longitude: selectedHub.longitude,
|
||||
city: selectedHub.city,
|
||||
state: selectedHub.state,
|
||||
street: selectedHub.street,
|
||||
country: selectedHub.country,
|
||||
zipCode: selectedHub.zipCode,
|
||||
),
|
||||
eventName: state.eventName,
|
||||
vendorId: state.selectedVendor?.id,
|
||||
roleRates: roleRates,
|
||||
);
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:krow_domain/krow_domain.dart';
|
||||
import 'one_time_order_state.dart';
|
||||
|
||||
abstract class OneTimeOrderEvent extends Equatable {
|
||||
const OneTimeOrderEvent();
|
||||
@@ -24,6 +25,30 @@ class OneTimeOrderVendorChanged extends OneTimeOrderEvent {
|
||||
List<Object?> get props => <Object?>[vendor];
|
||||
}
|
||||
|
||||
class OneTimeOrderHubsLoaded extends OneTimeOrderEvent {
|
||||
const OneTimeOrderHubsLoaded(this.hubs);
|
||||
final List<OneTimeOrderHubOption> hubs;
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[hubs];
|
||||
}
|
||||
|
||||
class OneTimeOrderHubChanged extends OneTimeOrderEvent {
|
||||
const OneTimeOrderHubChanged(this.hub);
|
||||
final OneTimeOrderHubOption hub;
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[hub];
|
||||
}
|
||||
|
||||
class OneTimeOrderEventNameChanged extends OneTimeOrderEvent {
|
||||
const OneTimeOrderEventNameChanged(this.eventName);
|
||||
final String eventName;
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[eventName];
|
||||
}
|
||||
|
||||
class OneTimeOrderDateChanged extends OneTimeOrderEvent {
|
||||
const OneTimeOrderDateChanged(this.date);
|
||||
final DateTime date;
|
||||
@@ -32,14 +57,6 @@ class OneTimeOrderDateChanged extends OneTimeOrderEvent {
|
||||
List<Object?> get props => <Object?>[date];
|
||||
}
|
||||
|
||||
class OneTimeOrderLocationChanged extends OneTimeOrderEvent {
|
||||
const OneTimeOrderLocationChanged(this.location);
|
||||
final String location;
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[location];
|
||||
}
|
||||
|
||||
class OneTimeOrderPositionAdded extends OneTimeOrderEvent {
|
||||
const OneTimeOrderPositionAdded();
|
||||
}
|
||||
|
||||
@@ -7,11 +7,14 @@ class OneTimeOrderState extends Equatable {
|
||||
const OneTimeOrderState({
|
||||
required this.date,
|
||||
required this.location,
|
||||
required this.eventName,
|
||||
required this.positions,
|
||||
this.status = OneTimeOrderStatus.initial,
|
||||
this.errorMessage,
|
||||
this.vendors = const <Vendor>[],
|
||||
this.selectedVendor,
|
||||
this.hubs = const <OneTimeOrderHubOption>[],
|
||||
this.selectedHub,
|
||||
this.roles = const <OneTimeOrderRoleOption>[],
|
||||
});
|
||||
|
||||
@@ -19,40 +22,51 @@ class OneTimeOrderState extends Equatable {
|
||||
return OneTimeOrderState(
|
||||
date: DateTime.now(),
|
||||
location: '',
|
||||
eventName: '',
|
||||
positions: const <OneTimeOrderPosition>[
|
||||
OneTimeOrderPosition(role: '', count: 1, startTime: '', endTime: ''),
|
||||
],
|
||||
vendors: const <Vendor>[],
|
||||
hubs: const <OneTimeOrderHubOption>[],
|
||||
roles: const <OneTimeOrderRoleOption>[],
|
||||
);
|
||||
}
|
||||
final DateTime date;
|
||||
final String location;
|
||||
final String eventName;
|
||||
final List<OneTimeOrderPosition> positions;
|
||||
final OneTimeOrderStatus status;
|
||||
final String? errorMessage;
|
||||
final List<Vendor> vendors;
|
||||
final Vendor? selectedVendor;
|
||||
final List<OneTimeOrderHubOption> hubs;
|
||||
final OneTimeOrderHubOption? selectedHub;
|
||||
final List<OneTimeOrderRoleOption> roles;
|
||||
|
||||
OneTimeOrderState copyWith({
|
||||
DateTime? date,
|
||||
String? location,
|
||||
String? eventName,
|
||||
List<OneTimeOrderPosition>? positions,
|
||||
OneTimeOrderStatus? status,
|
||||
String? errorMessage,
|
||||
List<Vendor>? vendors,
|
||||
Vendor? selectedVendor,
|
||||
List<OneTimeOrderHubOption>? hubs,
|
||||
OneTimeOrderHubOption? selectedHub,
|
||||
List<OneTimeOrderRoleOption>? roles,
|
||||
}) {
|
||||
return OneTimeOrderState(
|
||||
date: date ?? this.date,
|
||||
location: location ?? this.location,
|
||||
eventName: eventName ?? this.eventName,
|
||||
positions: positions ?? this.positions,
|
||||
status: status ?? this.status,
|
||||
errorMessage: errorMessage ?? this.errorMessage,
|
||||
vendors: vendors ?? this.vendors,
|
||||
selectedVendor: selectedVendor ?? this.selectedVendor,
|
||||
hubs: hubs ?? this.hubs,
|
||||
selectedHub: selectedHub ?? this.selectedHub,
|
||||
roles: roles ?? this.roles,
|
||||
);
|
||||
}
|
||||
@@ -61,15 +75,61 @@ class OneTimeOrderState extends Equatable {
|
||||
List<Object?> get props => <Object?>[
|
||||
date,
|
||||
location,
|
||||
eventName,
|
||||
positions,
|
||||
status,
|
||||
errorMessage,
|
||||
vendors,
|
||||
selectedVendor,
|
||||
hubs,
|
||||
selectedHub,
|
||||
roles,
|
||||
];
|
||||
}
|
||||
|
||||
class OneTimeOrderHubOption extends Equatable {
|
||||
const OneTimeOrderHubOption({
|
||||
required this.id,
|
||||
required this.name,
|
||||
required this.address,
|
||||
this.placeId,
|
||||
this.latitude,
|
||||
this.longitude,
|
||||
this.city,
|
||||
this.state,
|
||||
this.street,
|
||||
this.country,
|
||||
this.zipCode,
|
||||
});
|
||||
|
||||
final String id;
|
||||
final String name;
|
||||
final String address;
|
||||
final String? placeId;
|
||||
final double? latitude;
|
||||
final double? longitude;
|
||||
final String? city;
|
||||
final String? state;
|
||||
final String? street;
|
||||
final String? country;
|
||||
final String? zipCode;
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[
|
||||
id,
|
||||
name,
|
||||
address,
|
||||
placeId,
|
||||
latitude,
|
||||
longitude,
|
||||
city,
|
||||
state,
|
||||
street,
|
||||
country,
|
||||
zipCode,
|
||||
];
|
||||
}
|
||||
|
||||
class OneTimeOrderRoleOption extends Equatable {
|
||||
const OneTimeOrderRoleOption({
|
||||
required this.id,
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
import 'package:design_system/design_system.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
|
||||
/// A text input for the order name in the one-time order form.
|
||||
class OneTimeOrderEventNameInput extends StatefulWidget {
|
||||
const OneTimeOrderEventNameInput({
|
||||
required this.label,
|
||||
required this.value,
|
||||
required this.onChanged,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final String label;
|
||||
final String value;
|
||||
final ValueChanged<String> onChanged;
|
||||
|
||||
@override
|
||||
State<OneTimeOrderEventNameInput> createState() =>
|
||||
_OneTimeOrderEventNameInputState();
|
||||
}
|
||||
|
||||
class _OneTimeOrderEventNameInputState
|
||||
extends State<OneTimeOrderEventNameInput> {
|
||||
late final TextEditingController _controller;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_controller = TextEditingController(text: widget.value);
|
||||
}
|
||||
|
||||
@override
|
||||
void didUpdateWidget(OneTimeOrderEventNameInput oldWidget) {
|
||||
super.didUpdateWidget(oldWidget);
|
||||
if (widget.value != _controller.text) {
|
||||
_controller.text = widget.value;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_controller.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return UiTextField(
|
||||
label: widget.label,
|
||||
controller: _controller,
|
||||
onChanged: widget.onChanged,
|
||||
hintText: 'Order name',
|
||||
prefixIcon: UiIcons.briefcase,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -8,8 +8,8 @@ import '../../blocs/one_time_order_bloc.dart';
|
||||
import '../../blocs/one_time_order_event.dart';
|
||||
import '../../blocs/one_time_order_state.dart';
|
||||
import 'one_time_order_date_picker.dart';
|
||||
import 'one_time_order_event_name_input.dart';
|
||||
import 'one_time_order_header.dart';
|
||||
import 'one_time_order_location_input.dart';
|
||||
import 'one_time_order_position_card.dart';
|
||||
import 'one_time_order_section_header.dart';
|
||||
import 'one_time_order_success_view.dart';
|
||||
@@ -31,7 +31,12 @@ class OneTimeOrderView extends StatelessWidget {
|
||||
title: labels.success_title,
|
||||
message: labels.success_message,
|
||||
buttonLabel: labels.back_to_orders,
|
||||
onDone: () => Modular.to.pop(),
|
||||
onDone: () => Modular.to.navigate(
|
||||
'/client-main/orders/',
|
||||
arguments: <String, dynamic>{
|
||||
'initialDate': state.date.toIso8601String(),
|
||||
},
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -129,6 +134,15 @@ class _OneTimeOrderForm extends StatelessWidget {
|
||||
),
|
||||
const SizedBox(height: UiConstants.space4),
|
||||
|
||||
OneTimeOrderEventNameInput(
|
||||
label: 'ORDER NAME',
|
||||
value: state.eventName,
|
||||
onChanged: (String value) => BlocProvider.of<OneTimeOrderBloc>(
|
||||
context,
|
||||
).add(OneTimeOrderEventNameChanged(value)),
|
||||
),
|
||||
const SizedBox(height: UiConstants.space4),
|
||||
|
||||
// Vendor Selection
|
||||
Text('SELECT VENDOR', style: UiTypography.footnote2r.textSecondary),
|
||||
const SizedBox(height: 8),
|
||||
@@ -179,12 +193,43 @@ class _OneTimeOrderForm extends StatelessWidget {
|
||||
),
|
||||
const SizedBox(height: UiConstants.space4),
|
||||
|
||||
OneTimeOrderLocationInput(
|
||||
label: labels.location_label,
|
||||
value: state.location,
|
||||
onChanged: (String location) => BlocProvider.of<OneTimeOrderBloc>(
|
||||
context,
|
||||
).add(OneTimeOrderLocationChanged(location)),
|
||||
Text('HUB', style: UiTypography.footnote2r.textSecondary),
|
||||
const SizedBox(height: 8),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: UiConstants.space3),
|
||||
height: 48,
|
||||
decoration: BoxDecoration(
|
||||
color: UiColors.white,
|
||||
borderRadius: UiConstants.radiusMd,
|
||||
border: Border.all(color: UiColors.border),
|
||||
),
|
||||
child: DropdownButtonHideUnderline(
|
||||
child: DropdownButton<OneTimeOrderHubOption>(
|
||||
isExpanded: true,
|
||||
value: state.selectedHub,
|
||||
icon: const Icon(
|
||||
UiIcons.chevronDown,
|
||||
size: 18,
|
||||
color: UiColors.iconSecondary,
|
||||
),
|
||||
onChanged: (OneTimeOrderHubOption? hub) {
|
||||
if (hub != null) {
|
||||
BlocProvider.of<OneTimeOrderBloc>(
|
||||
context,
|
||||
).add(OneTimeOrderHubChanged(hub));
|
||||
}
|
||||
},
|
||||
items: state.hubs.map((OneTimeOrderHubOption hub) {
|
||||
return DropdownMenuItem<OneTimeOrderHubOption>(
|
||||
value: hub,
|
||||
child: Text(
|
||||
hub.name,
|
||||
style: UiTypography.body2m.textPrimary,
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: UiConstants.space6),
|
||||
|
||||
|
||||
@@ -25,6 +25,7 @@ dependencies:
|
||||
krow_data_connect:
|
||||
path: ../../../data_connect
|
||||
firebase_data_connect: ^0.2.2+2
|
||||
firebase_auth: ^6.1.4
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
library client_home;
|
||||
|
||||
import 'package:flutter_modular/flutter_modular.dart';
|
||||
import 'package:krow_data_connect/krow_data_connect.dart';
|
||||
import 'src/data/repositories_impl/home_repository_impl.dart';
|
||||
|
||||
@@ -89,6 +89,14 @@ class HomeRepositoryImpl implements HomeRepositoryInterface {
|
||||
end: _toTimestamp(end),
|
||||
)
|
||||
.execute();
|
||||
print(
|
||||
'Home coverage: businessId=$businessId '
|
||||
'startLocal=${start.toIso8601String()} '
|
||||
'endLocal=${end.toIso8601String()} '
|
||||
'startUtc=${_toTimestamp(start).toJson()} '
|
||||
'endUtc=${_toTimestamp(end).toJson()} '
|
||||
'shiftRoles=${result.data.shiftRoles.length}',
|
||||
);
|
||||
|
||||
int totalNeeded = 0;
|
||||
int totalFilled = 0;
|
||||
@@ -169,7 +177,8 @@ class HomeRepositoryImpl implements HomeRepositoryInterface {
|
||||
}
|
||||
|
||||
fdc.Timestamp _toTimestamp(DateTime date) {
|
||||
final int millis = date.millisecondsSinceEpoch;
|
||||
final DateTime utc = date.toUtc();
|
||||
final int millis = utc.millisecondsSinceEpoch;
|
||||
final int seconds = millis ~/ 1000;
|
||||
final int nanos = (millis % 1000) * 1000000;
|
||||
return fdc.Timestamp(nanos, seconds);
|
||||
|
||||
@@ -26,6 +26,8 @@ class ClientHomeBloc extends Bloc<ClientHomeEvent, ClientHomeState> {
|
||||
on<ClientHomeWidgetVisibilityToggled>(_onWidgetVisibilityToggled);
|
||||
on<ClientHomeWidgetReordered>(_onWidgetReordered);
|
||||
on<ClientHomeLayoutReset>(_onLayoutReset);
|
||||
|
||||
add(ClientHomeStarted());
|
||||
}
|
||||
|
||||
Future<void> _onStarted(
|
||||
|
||||
@@ -35,12 +35,12 @@ class ClientHomeState extends Equatable {
|
||||
this.isEditMode = false,
|
||||
this.errorMessage,
|
||||
this.dashboardData = const HomeDashboardData(
|
||||
weeklySpending: 4250.0,
|
||||
next7DaysSpending: 6100.0,
|
||||
weeklyShifts: 12,
|
||||
next7DaysScheduled: 18,
|
||||
totalNeeded: 10,
|
||||
totalFilled: 8,
|
||||
weeklySpending: 0.0,
|
||||
next7DaysSpending: 0.0,
|
||||
weeklyShifts: 0,
|
||||
next7DaysScheduled: 0,
|
||||
totalNeeded: 0,
|
||||
totalFilled: 0,
|
||||
),
|
||||
this.reorderItems = const <ReorderItem>[],
|
||||
this.businessName = 'Your Company',
|
||||
|
||||
@@ -34,10 +34,7 @@ class ClientHomeSheets {
|
||||
builder: (BuildContext context) {
|
||||
return ShiftOrderFormSheet(
|
||||
initialData: initialData,
|
||||
onSubmit: (Map<String, dynamic> data) {
|
||||
Navigator.pop(context);
|
||||
onSubmit(data);
|
||||
},
|
||||
onSubmit: onSubmit,
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
@@ -24,8 +24,7 @@ class ClientHomePage extends StatelessWidget {
|
||||
final TranslationsClientHomeEn i18n = t.client_home;
|
||||
|
||||
return BlocProvider<ClientHomeBloc>(
|
||||
create: (BuildContext context) =>
|
||||
Modular.get<ClientHomeBloc>()..add(ClientHomeStarted()),
|
||||
create: (BuildContext context) => Modular.get<ClientHomeBloc>(),
|
||||
child: Scaffold(
|
||||
body: SafeArea(
|
||||
child: Column(
|
||||
@@ -59,19 +58,15 @@ class ClientHomePage extends StatelessWidget {
|
||||
100,
|
||||
),
|
||||
onReorder: (int oldIndex, int newIndex) {
|
||||
BlocProvider.of<ClientHomeBloc>(context).add(
|
||||
ClientHomeWidgetReordered(oldIndex, newIndex),
|
||||
);
|
||||
BlocProvider.of<ClientHomeBloc>(
|
||||
context,
|
||||
).add(ClientHomeWidgetReordered(oldIndex, newIndex));
|
||||
},
|
||||
children: state.widgetOrder.map((String id) {
|
||||
return Container(
|
||||
key: ValueKey(id),
|
||||
key: ValueKey<String>(id),
|
||||
margin: const EdgeInsets.only(bottom: UiConstants.space4),
|
||||
child: DashboardWidgetBuilder(
|
||||
id: id,
|
||||
state: state,
|
||||
isEditMode: true,
|
||||
),
|
||||
child: DashboardWidgetBuilder(id: id, state: state, isEditMode: true),
|
||||
);
|
||||
}).toList(),
|
||||
);
|
||||
|
||||
@@ -24,21 +24,22 @@ class ActionsWidget extends StatelessWidget {
|
||||
|
||||
return Row(
|
||||
children: <Widget>[
|
||||
Expanded(
|
||||
child: _ActionCard(
|
||||
title: i18n.rapid,
|
||||
subtitle: i18n.rapid_subtitle,
|
||||
icon: UiIcons.zap,
|
||||
color: const Color(0xFFFEF2F2),
|
||||
borderColor: const Color(0xFFFECACA),
|
||||
iconBgColor: const Color(0xFFFEE2E2),
|
||||
iconColor: const Color(0xFFDC2626),
|
||||
textColor: const Color(0xFF7F1D1D),
|
||||
subtitleColor: const Color(0xFFB91C1C),
|
||||
onTap: onRapidPressed,
|
||||
),
|
||||
),
|
||||
const SizedBox(width: UiConstants.space2),
|
||||
/// TODO: FEATURE_NOT_YET_IMPLEMENTED
|
||||
// Expanded(
|
||||
// child: _ActionCard(
|
||||
// title: i18n.rapid,
|
||||
// subtitle: i18n.rapid_subtitle,
|
||||
// icon: UiIcons.zap,
|
||||
// color: const Color(0xFFFEF2F2),
|
||||
// borderColor: const Color(0xFFFECACA),
|
||||
// iconBgColor: const Color(0xFFFEE2E2),
|
||||
// iconColor: const Color(0xFFDC2626),
|
||||
// textColor: const Color(0xFF7F1D1D),
|
||||
// subtitleColor: const Color(0xFFB91C1C),
|
||||
// onTap: onRapidPressed,
|
||||
// ),
|
||||
// ),
|
||||
// const SizedBox(width: UiConstants.space2),
|
||||
Expanded(
|
||||
child: _ActionCard(
|
||||
title: i18n.create_order,
|
||||
|
||||
@@ -25,11 +25,12 @@ class CoverageDashboard extends StatelessWidget {
|
||||
for (final s in shifts) {
|
||||
final int needed = s['workersNeeded'] as int? ?? 0;
|
||||
final int confirmed = s['filled'] as int? ?? 0;
|
||||
final double rate = s['hourlyRate'] as double? ?? 20.0;
|
||||
final double rate = s['hourlyRate'] as double? ?? 0.0;
|
||||
final double hours = s['hours'] as double? ?? 0.0;
|
||||
|
||||
totalNeeded += needed;
|
||||
totalConfirmed += confirmed;
|
||||
todayCost += rate * 8 * confirmed;
|
||||
todayCost += rate * hours;
|
||||
}
|
||||
|
||||
final int coveragePercent = totalNeeded > 0
|
||||
@@ -104,15 +105,13 @@ class CoverageDashboard extends StatelessWidget {
|
||||
icon: UiIcons.warning,
|
||||
isWarning: unfilledPositions > 0,
|
||||
),
|
||||
if (lateWorkersCount > 0) ...<Widget>[
|
||||
const SizedBox(height: UiConstants.space2),
|
||||
_StatusCard(
|
||||
label: 'Running Late',
|
||||
value: '$lateWorkersCount',
|
||||
icon: UiIcons.error,
|
||||
isError: true,
|
||||
),
|
||||
],
|
||||
const SizedBox(height: UiConstants.space2),
|
||||
_StatusCard(
|
||||
label: 'Running Late',
|
||||
value: '$lateWorkersCount',
|
||||
icon: UiIcons.error,
|
||||
isError: true,
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
@@ -122,7 +121,7 @@ class CoverageDashboard extends StatelessWidget {
|
||||
children: <Widget>[
|
||||
_StatusCard(
|
||||
label: 'Checked In',
|
||||
value: '$checkedInCount/$totalConfirmed',
|
||||
value: '$checkedInCount/$totalNeeded',
|
||||
icon: UiIcons.success,
|
||||
isInfo: true,
|
||||
),
|
||||
|
||||
@@ -15,9 +15,9 @@ class CoverageWidget extends StatelessWidget {
|
||||
/// Creates a [CoverageWidget].
|
||||
const CoverageWidget({
|
||||
super.key,
|
||||
this.totalNeeded = 10,
|
||||
this.totalConfirmed = 8,
|
||||
this.coveragePercent = 80,
|
||||
this.totalNeeded = 0,
|
||||
this.totalConfirmed = 0,
|
||||
this.coveragePercent = 0,
|
||||
});
|
||||
|
||||
@override
|
||||
|
||||
@@ -70,7 +70,21 @@ class DashboardWidgetBuilder extends StatelessWidget {
|
||||
context,
|
||||
data,
|
||||
onSubmit: (Map<String, dynamic> submittedData) {
|
||||
// Handle form submission if needed
|
||||
final String? dateStr =
|
||||
submittedData['date']?.toString();
|
||||
if (dateStr == null || dateStr.isEmpty) {
|
||||
return;
|
||||
}
|
||||
final DateTime? initialDate = DateTime.tryParse(dateStr);
|
||||
if (initialDate == null) {
|
||||
return;
|
||||
}
|
||||
Modular.to.navigate(
|
||||
'/client-main/orders/',
|
||||
arguments: <String, dynamic>{
|
||||
'initialDate': initialDate.toIso8601String(),
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
},
|
||||
|
||||
@@ -31,6 +31,15 @@ class _LiveActivityWidgetState extends State<LiveActivityWidget> {
|
||||
final DateTime now = DateTime.now();
|
||||
final DateTime start = DateTime(now.year, now.month, now.day);
|
||||
final DateTime end = DateTime(now.year, now.month, now.day, 23, 59, 59, 999);
|
||||
final fdc.QueryResult<dc.ListShiftRolesByBusinessAndDateRangeData,
|
||||
dc.ListShiftRolesByBusinessAndDateRangeVariables> shiftRolesResult =
|
||||
await dc.ExampleConnector.instance
|
||||
.listShiftRolesByBusinessAndDateRange(
|
||||
businessId: businessId,
|
||||
start: _toTimestamp(start),
|
||||
end: _toTimestamp(end),
|
||||
)
|
||||
.execute();
|
||||
final fdc.QueryResult<dc.ListStaffsApplicationsByBusinessForDayData,
|
||||
dc.ListStaffsApplicationsByBusinessForDayVariables> result =
|
||||
await dc.ExampleConnector.instance
|
||||
@@ -41,33 +50,20 @@ class _LiveActivityWidgetState extends State<LiveActivityWidget> {
|
||||
)
|
||||
.execute();
|
||||
|
||||
if (result.data.applications.isEmpty) {
|
||||
if (shiftRolesResult.data.shiftRoles.isEmpty &&
|
||||
result.data.applications.isEmpty) {
|
||||
return _LiveActivityData.empty();
|
||||
}
|
||||
|
||||
final Map<String, _LiveShiftAggregate> aggregates =
|
||||
<String, _LiveShiftAggregate>{};
|
||||
for (final dc.ListStaffsApplicationsByBusinessForDayApplications app
|
||||
in result.data.applications) {
|
||||
final String key = '${app.shiftId}:${app.roleId}';
|
||||
final _LiveShiftAggregate aggregate = aggregates[key] ??
|
||||
_LiveShiftAggregate(
|
||||
workersNeeded: app.shiftRole.count,
|
||||
assigned: app.shiftRole.assigned ?? 0,
|
||||
cost: app.shiftRole.shift.cost ?? 0,
|
||||
);
|
||||
aggregates[key] = aggregate;
|
||||
}
|
||||
|
||||
int totalNeeded = 0;
|
||||
int totalAssigned = 0;
|
||||
double totalCost = 0;
|
||||
for (final _LiveShiftAggregate aggregate in aggregates.values) {
|
||||
totalNeeded += aggregate.workersNeeded;
|
||||
totalAssigned += aggregate.assigned;
|
||||
totalCost += aggregate.cost;
|
||||
for (final dc.ListShiftRolesByBusinessAndDateRangeShiftRoles shiftRole
|
||||
in shiftRolesResult.data.shiftRoles) {
|
||||
totalNeeded += shiftRole.count;
|
||||
totalCost += shiftRole.totalValue ?? 0;
|
||||
}
|
||||
|
||||
final int totalAssigned = result.data.applications.length;
|
||||
int lateCount = 0;
|
||||
int checkedInCount = 0;
|
||||
for (final dc.ListStaffsApplicationsByBusinessForDayApplications app
|
||||
@@ -92,9 +88,10 @@ class _LiveActivityWidgetState extends State<LiveActivityWidget> {
|
||||
}
|
||||
|
||||
fdc.Timestamp _toTimestamp(DateTime dateTime) {
|
||||
final int seconds = dateTime.millisecondsSinceEpoch ~/ 1000;
|
||||
final DateTime utc = dateTime.toUtc();
|
||||
final int seconds = utc.millisecondsSinceEpoch ~/ 1000;
|
||||
final int nanoseconds =
|
||||
(dateTime.millisecondsSinceEpoch % 1000) * 1000000;
|
||||
(utc.millisecondsSinceEpoch % 1000) * 1000000;
|
||||
return fdc.Timestamp(nanoseconds, seconds);
|
||||
}
|
||||
|
||||
@@ -136,9 +133,8 @@ class _LiveActivityWidgetState extends State<LiveActivityWidget> {
|
||||
<String, Object>{
|
||||
'workersNeeded': data.totalNeeded,
|
||||
'filled': data.totalAssigned,
|
||||
'hourlyRate': data.totalAssigned == 0
|
||||
? 0.0
|
||||
: data.totalCost / data.totalAssigned,
|
||||
'hourlyRate': 1.0,
|
||||
'hours': data.totalCost,
|
||||
'status': 'OPEN',
|
||||
'date': DateTime.now().toIso8601String().split('T')[0],
|
||||
},
|
||||
@@ -192,15 +188,3 @@ class _LiveActivityData {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
class _LiveShiftAggregate {
|
||||
_LiveShiftAggregate({
|
||||
required this.workersNeeded,
|
||||
required this.assigned,
|
||||
required this.cost,
|
||||
});
|
||||
|
||||
final int workersNeeded;
|
||||
final int assigned;
|
||||
final double cost;
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'package:core_localization/core_localization.dart';
|
||||
import 'package:design_system/design_system.dart';
|
||||
import 'package:firebase_data_connect/firebase_data_connect.dart' as fdc;
|
||||
import 'package:flutter/material.dart';
|
||||
@@ -52,6 +53,7 @@ class ShiftOrderFormSheet extends StatefulWidget {
|
||||
class _ShiftOrderFormSheetState extends State<ShiftOrderFormSheet> {
|
||||
late TextEditingController _dateController;
|
||||
late TextEditingController _globalLocationController;
|
||||
late TextEditingController _orderNameController;
|
||||
|
||||
late List<Map<String, dynamic>> _positions;
|
||||
|
||||
@@ -59,6 +61,10 @@ class _ShiftOrderFormSheetState extends State<ShiftOrderFormSheet> {
|
||||
List<_VendorOption> _vendors = const <_VendorOption>[];
|
||||
List<_RoleOption> _roles = const <_RoleOption>[];
|
||||
String? _selectedVendorId;
|
||||
List<dc.ListTeamHubsByOwnerIdTeamHubs> _hubs = const <dc.ListTeamHubsByOwnerIdTeamHubs>[];
|
||||
dc.ListTeamHubsByOwnerIdTeamHubs? _selectedHub;
|
||||
bool _showSuccess = false;
|
||||
Map<String, dynamic>? _submitData;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
@@ -75,6 +81,9 @@ class _ShiftOrderFormSheetState extends State<ShiftOrderFormSheet> {
|
||||
widget.initialData?['locationAddress'] ??
|
||||
'',
|
||||
);
|
||||
_orderNameController = TextEditingController(
|
||||
text: widget.initialData?['eventName']?.toString() ?? '',
|
||||
);
|
||||
|
||||
// Initialize positions
|
||||
_positions = <Map<String, dynamic>>[
|
||||
@@ -96,6 +105,7 @@ class _ShiftOrderFormSheetState extends State<ShiftOrderFormSheet> {
|
||||
];
|
||||
|
||||
_loadVendors();
|
||||
_loadHubs();
|
||||
_loadOrderDetails();
|
||||
}
|
||||
|
||||
@@ -103,6 +113,7 @@ class _ShiftOrderFormSheetState extends State<ShiftOrderFormSheet> {
|
||||
void dispose() {
|
||||
_dateController.dispose();
|
||||
_globalLocationController.dispose();
|
||||
_orderNameController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@@ -187,9 +198,14 @@ class _ShiftOrderFormSheetState extends State<ShiftOrderFormSheet> {
|
||||
if (businessId == null || businessId.isEmpty) {
|
||||
return;
|
||||
}
|
||||
final dc.ListTeamHubsByOwnerIdTeamHubs? selectedHub = _selectedHub;
|
||||
if (selectedHub == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final DateTime date = DateTime.parse(_dateController.text);
|
||||
final fdc.Timestamp orderTimestamp = _toTimestamp(date);
|
||||
final DateTime dateOnly = DateTime.utc(date.year, date.month, date.day);
|
||||
final fdc.Timestamp orderTimestamp = _toTimestamp(dateOnly);
|
||||
final dc.OrderType orderType =
|
||||
_orderTypeFromValue(widget.initialData?['type']?.toString());
|
||||
|
||||
@@ -198,9 +214,10 @@ class _ShiftOrderFormSheetState extends State<ShiftOrderFormSheet> {
|
||||
.createOrder(
|
||||
businessId: businessId,
|
||||
orderType: orderType,
|
||||
teamHubId: selectedHub.id,
|
||||
)
|
||||
.vendorId(_selectedVendorId)
|
||||
.location(_globalLocationController.text)
|
||||
.eventName(_orderNameController.text)
|
||||
.status(dc.OrderStatus.POSTED)
|
||||
.date(orderTimestamp)
|
||||
.execute();
|
||||
@@ -222,8 +239,15 @@ class _ShiftOrderFormSheetState extends State<ShiftOrderFormSheet> {
|
||||
shiftResult = await _dataConnect
|
||||
.createShift(title: shiftTitle, orderId: orderId)
|
||||
.date(orderTimestamp)
|
||||
.location(_globalLocationController.text)
|
||||
.locationAddress(_globalLocationController.text)
|
||||
.location(selectedHub.hubName)
|
||||
.locationAddress(selectedHub.address)
|
||||
.latitude(selectedHub.latitude)
|
||||
.longitude(selectedHub.longitude)
|
||||
.placeId(selectedHub.placeId)
|
||||
.city(selectedHub.city)
|
||||
.state(selectedHub.state)
|
||||
.street(selectedHub.street)
|
||||
.country(selectedHub.country)
|
||||
.status(dc.ShiftStatus.PENDING)
|
||||
.workersNeeded(workersNeeded)
|
||||
.filled(0)
|
||||
@@ -266,12 +290,17 @@ class _ShiftOrderFormSheetState extends State<ShiftOrderFormSheet> {
|
||||
}
|
||||
|
||||
await _dataConnect
|
||||
.updateOrder(id: orderId)
|
||||
.updateOrder(id: orderId, teamHubId: selectedHub.id)
|
||||
.shifts(fdc.AnyValue(<String>[shiftId]))
|
||||
.execute();
|
||||
|
||||
widget.onSubmit(<String, dynamic>{
|
||||
'orderId': orderId,
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
_submitData = <String, dynamic>{
|
||||
'orderId': orderId,
|
||||
'date': _dateController.text,
|
||||
};
|
||||
_showSuccess = true;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -306,6 +335,35 @@ class _ShiftOrderFormSheetState extends State<ShiftOrderFormSheet> {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _loadHubs() async {
|
||||
final String? businessId = dc.ClientSessionStore.instance.session?.business?.id;
|
||||
if (businessId == null || businessId.isEmpty) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
final fdc.QueryResult<
|
||||
dc.ListTeamHubsByOwnerIdData,
|
||||
dc.ListTeamHubsByOwnerIdVariables> result =
|
||||
await _dataConnect.listTeamHubsByOwnerId(ownerId: businessId).execute();
|
||||
final List<dc.ListTeamHubsByOwnerIdTeamHubs> hubs = result.data.teamHubs;
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
_hubs = hubs;
|
||||
_selectedHub = hubs.isNotEmpty ? hubs.first : null;
|
||||
if (_selectedHub != null) {
|
||||
_globalLocationController.text = _selectedHub!.address;
|
||||
}
|
||||
});
|
||||
} catch (_) {
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
_hubs = const <dc.ListTeamHubsByOwnerIdTeamHubs>[];
|
||||
_selectedHub = null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _loadRolesForVendor(String vendorId) async {
|
||||
try {
|
||||
final fdc.QueryResult<dc.ListRolesByVendorIdData, dc.ListRolesByVendorIdVariables>
|
||||
@@ -357,10 +415,14 @@ class _ShiftOrderFormSheetState extends State<ShiftOrderFormSheet> {
|
||||
|
||||
final dc.ListShiftRolesByBusinessAndOrderShiftRolesShift firstShift =
|
||||
shiftRoles.first.shift;
|
||||
_globalLocationController.text = firstShift.order.location ??
|
||||
firstShift.locationAddress ??
|
||||
firstShift.location ??
|
||||
_globalLocationController.text;
|
||||
final dc.ListShiftRolesByBusinessAndOrderShiftRolesShiftOrderTeamHub?
|
||||
teamHub = firstShift.order.teamHub;
|
||||
await _loadHubsAndSelect(
|
||||
placeId: teamHub?.placeId,
|
||||
hubName: teamHub?.hubName,
|
||||
address: teamHub?.address,
|
||||
);
|
||||
_orderNameController.text = firstShift.order.eventName ?? '';
|
||||
|
||||
final String? vendorId = firstShift.order.vendorId;
|
||||
if (mounted) {
|
||||
@@ -394,6 +456,70 @@ class _ShiftOrderFormSheetState extends State<ShiftOrderFormSheet> {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _loadHubsAndSelect({
|
||||
String? placeId,
|
||||
String? hubName,
|
||||
String? address,
|
||||
}) async {
|
||||
final String? businessId = dc.ClientSessionStore.instance.session?.business?.id;
|
||||
if (businessId == null || businessId.isEmpty) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
final fdc.QueryResult<
|
||||
dc.ListTeamHubsByOwnerIdData,
|
||||
dc.ListTeamHubsByOwnerIdVariables> result =
|
||||
await _dataConnect.listTeamHubsByOwnerId(ownerId: businessId).execute();
|
||||
final List<dc.ListTeamHubsByOwnerIdTeamHubs> hubs = result.data.teamHubs;
|
||||
dc.ListTeamHubsByOwnerIdTeamHubs? selected;
|
||||
|
||||
if (placeId != null && placeId.isNotEmpty) {
|
||||
for (final dc.ListTeamHubsByOwnerIdTeamHubs hub in hubs) {
|
||||
if (hub.placeId == placeId) {
|
||||
selected = hub;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (selected == null && hubName != null && hubName.isNotEmpty) {
|
||||
for (final dc.ListTeamHubsByOwnerIdTeamHubs hub in hubs) {
|
||||
if (hub.hubName == hubName) {
|
||||
selected = hub;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (selected == null && address != null && address.isNotEmpty) {
|
||||
for (final dc.ListTeamHubsByOwnerIdTeamHubs hub in hubs) {
|
||||
if (hub.address == address) {
|
||||
selected = hub;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
selected ??= hubs.isNotEmpty ? hubs.first : null;
|
||||
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
_hubs = hubs;
|
||||
_selectedHub = selected;
|
||||
if (selected != null) {
|
||||
_globalLocationController.text = selected.address;
|
||||
}
|
||||
});
|
||||
} catch (_) {
|
||||
if (!mounted) return;
|
||||
setState(() {
|
||||
_hubs = const <dc.ListTeamHubsByOwnerIdTeamHubs>[];
|
||||
_selectedHub = null;
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
String _formatTimeForField(fdc.Timestamp? value) {
|
||||
if (value == null) return '';
|
||||
try {
|
||||
@@ -472,7 +598,8 @@ class _ShiftOrderFormSheetState extends State<ShiftOrderFormSheet> {
|
||||
}
|
||||
|
||||
fdc.Timestamp _toTimestamp(DateTime date) {
|
||||
final int millis = date.millisecondsSinceEpoch;
|
||||
final DateTime utc = date.toUtc();
|
||||
final int millis = utc.millisecondsSinceEpoch;
|
||||
final int seconds = millis ~/ 1000;
|
||||
final int nanos = (millis % 1000) * 1000000;
|
||||
return fdc.Timestamp(nanos, seconds);
|
||||
@@ -480,6 +607,16 @@ class _ShiftOrderFormSheetState extends State<ShiftOrderFormSheet> {
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (_showSuccess) {
|
||||
final TranslationsClientCreateOrderOneTimeEn labels =
|
||||
t.client_create_order.one_time;
|
||||
return _buildSuccessView(
|
||||
title: labels.success_title,
|
||||
message: labels.success_message,
|
||||
buttonLabel: labels.back_to_orders,
|
||||
);
|
||||
}
|
||||
|
||||
return Container(
|
||||
height: MediaQuery.of(context).size.height * 0.95,
|
||||
decoration: const BoxDecoration(
|
||||
@@ -546,12 +683,16 @@ class _ShiftOrderFormSheetState extends State<ShiftOrderFormSheet> {
|
||||
_buildVendorDropdown(),
|
||||
const SizedBox(height: UiConstants.space4),
|
||||
|
||||
_buildSectionHeader('ORDER NAME'),
|
||||
_buildOrderNameField(),
|
||||
const SizedBox(height: UiConstants.space4),
|
||||
|
||||
_buildSectionHeader('DATE'),
|
||||
_buildDateField(),
|
||||
const SizedBox(height: UiConstants.space4),
|
||||
|
||||
_buildSectionHeader('LOCATION'),
|
||||
_buildLocationField(),
|
||||
_buildSectionHeader('HUB'),
|
||||
_buildHubField(),
|
||||
const SizedBox(height: UiConstants.space5),
|
||||
|
||||
Row(
|
||||
@@ -783,7 +924,7 @@ class _ShiftOrderFormSheetState extends State<ShiftOrderFormSheet> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildLocationField() {
|
||||
Widget _buildHubField() {
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: UiConstants.space3),
|
||||
decoration: BoxDecoration(
|
||||
@@ -791,21 +932,52 @@ class _ShiftOrderFormSheetState extends State<ShiftOrderFormSheet> {
|
||||
borderRadius: UiConstants.radiusMd,
|
||||
border: Border.all(color: UiColors.border),
|
||||
),
|
||||
child: Row(
|
||||
children: <Widget>[
|
||||
const Icon(UiIcons.mapPin, size: 20, color: UiColors.iconSecondary),
|
||||
const SizedBox(width: UiConstants.space2),
|
||||
Expanded(
|
||||
child: TextField(
|
||||
controller: _globalLocationController,
|
||||
decoration: const InputDecoration(
|
||||
hintText: 'Enter location address',
|
||||
border: InputBorder.none,
|
||||
),
|
||||
style: UiTypography.body2r.textPrimary,
|
||||
),
|
||||
child: DropdownButtonHideUnderline(
|
||||
child: DropdownButton<dc.ListTeamHubsByOwnerIdTeamHubs>(
|
||||
isExpanded: true,
|
||||
value: _selectedHub,
|
||||
icon: const Icon(
|
||||
UiIcons.chevronDown,
|
||||
size: 18,
|
||||
color: UiColors.iconSecondary,
|
||||
),
|
||||
],
|
||||
onChanged: (dc.ListTeamHubsByOwnerIdTeamHubs? hub) {
|
||||
if (hub != null) {
|
||||
setState(() {
|
||||
_selectedHub = hub;
|
||||
_globalLocationController.text = hub.address;
|
||||
});
|
||||
}
|
||||
},
|
||||
items: _hubs.map((dc.ListTeamHubsByOwnerIdTeamHubs hub) {
|
||||
return DropdownMenuItem<dc.ListTeamHubsByOwnerIdTeamHubs>(
|
||||
value: hub,
|
||||
child: Text(
|
||||
hub.hubName,
|
||||
style: UiTypography.body2r.textPrimary,
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildOrderNameField() {
|
||||
return Container(
|
||||
padding: const EdgeInsets.symmetric(horizontal: UiConstants.space3),
|
||||
decoration: BoxDecoration(
|
||||
color: UiColors.white,
|
||||
borderRadius: UiConstants.radiusMd,
|
||||
border: Border.all(color: UiColors.border),
|
||||
),
|
||||
child: TextField(
|
||||
controller: _orderNameController,
|
||||
decoration: const InputDecoration(
|
||||
hintText: 'Order name',
|
||||
border: InputBorder.none,
|
||||
),
|
||||
style: UiTypography.body2r.textPrimary,
|
||||
),
|
||||
);
|
||||
}
|
||||
@@ -1109,6 +1281,90 @@ class _ShiftOrderFormSheetState extends State<ShiftOrderFormSheet> {
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildSuccessView({
|
||||
required String title,
|
||||
required String message,
|
||||
required String buttonLabel,
|
||||
}) {
|
||||
return Container(
|
||||
width: double.infinity,
|
||||
height: MediaQuery.of(context).size.height * 0.95,
|
||||
decoration: const BoxDecoration(
|
||||
gradient: LinearGradient(
|
||||
begin: Alignment.topCenter,
|
||||
end: Alignment.bottomCenter,
|
||||
colors: <Color>[UiColors.primary, UiColors.buttonPrimaryHover],
|
||||
),
|
||||
borderRadius: BorderRadius.vertical(top: Radius.circular(24)),
|
||||
),
|
||||
child: SafeArea(
|
||||
child: Center(
|
||||
child: Container(
|
||||
margin: const EdgeInsets.symmetric(horizontal: 40),
|
||||
padding: const EdgeInsets.all(UiConstants.space8),
|
||||
decoration: BoxDecoration(
|
||||
color: UiColors.white,
|
||||
borderRadius: UiConstants.radiusLg * 1.5,
|
||||
boxShadow: <BoxShadow>[
|
||||
BoxShadow(
|
||||
color: Colors.black.withValues(alpha: 0.2),
|
||||
blurRadius: 20,
|
||||
offset: const Offset(0, 10),
|
||||
),
|
||||
],
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: <Widget>[
|
||||
Container(
|
||||
width: 64,
|
||||
height: 64,
|
||||
decoration: const BoxDecoration(
|
||||
color: UiColors.accent,
|
||||
shape: BoxShape.circle,
|
||||
),
|
||||
child: const Center(
|
||||
child: Icon(
|
||||
UiIcons.check,
|
||||
color: UiColors.black,
|
||||
size: 32,
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: UiConstants.space6),
|
||||
Text(
|
||||
title,
|
||||
style: UiTypography.headline2m.textPrimary,
|
||||
textAlign: TextAlign.center,
|
||||
),
|
||||
const SizedBox(height: UiConstants.space3),
|
||||
Text(
|
||||
message,
|
||||
textAlign: TextAlign.center,
|
||||
style: UiTypography.body2r.textSecondary.copyWith(
|
||||
height: 1.5,
|
||||
),
|
||||
),
|
||||
const SizedBox(height: UiConstants.space8),
|
||||
SizedBox(
|
||||
width: double.infinity,
|
||||
child: UiButton.primary(
|
||||
text: buttonLabel,
|
||||
onPressed: () {
|
||||
widget.onSubmit(_submitData ?? <String, dynamic>{});
|
||||
Navigator.pop(context);
|
||||
},
|
||||
size: UiButtonSize.large,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Widget _buildInlineTimeInput({
|
||||
required String label,
|
||||
required String value,
|
||||
|
||||
@@ -22,6 +22,7 @@ dependencies:
|
||||
core_localization:
|
||||
path: ../../../core_localization
|
||||
krow_domain: ^0.0.1
|
||||
krow_data_connect: ^0.0.1
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
import 'dart:convert';
|
||||
|
||||
import 'package:firebase_auth/firebase_auth.dart' as firebase;
|
||||
import 'package:firebase_data_connect/firebase_data_connect.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:krow_data_connect/krow_data_connect.dart' as dc;
|
||||
import 'package:krow_domain/krow_domain.dart' as domain;
|
||||
|
||||
import '../../domain/repositories/hub_repository_interface.dart';
|
||||
import '../../util/hubs_constants.dart';
|
||||
|
||||
/// Implementation of [HubRepositoryInterface] backed by Data Connect.
|
||||
class HubRepositoryImpl implements HubRepositoryInterface {
|
||||
@@ -27,10 +31,24 @@ class HubRepositoryImpl implements HubRepositoryInterface {
|
||||
Future<domain.Hub> createHub({
|
||||
required String name,
|
||||
required String address,
|
||||
String? placeId,
|
||||
double? latitude,
|
||||
double? longitude,
|
||||
String? city,
|
||||
String? state,
|
||||
String? street,
|
||||
String? country,
|
||||
String? zipCode,
|
||||
}) async {
|
||||
final dc.GetBusinessesByUserIdBusinesses business = await _getBusinessForCurrentUser();
|
||||
final String teamId = await _getOrCreateTeamId(business);
|
||||
final String? city = business.city;
|
||||
final _PlaceAddress? placeAddress =
|
||||
placeId == null || placeId.isEmpty ? null : await _fetchPlaceAddress(placeId);
|
||||
final String? cityValue = city ?? placeAddress?.city ?? business.city;
|
||||
final String? stateValue = state ?? placeAddress?.state;
|
||||
final String? streetValue = street ?? placeAddress?.street;
|
||||
final String? countryValue = country ?? placeAddress?.country;
|
||||
final String? zipCodeValue = zipCode ?? placeAddress?.zipCode;
|
||||
|
||||
final OperationResult<dc.CreateTeamHubData, dc.CreateTeamHubVariables> result = await _dataConnect
|
||||
.createTeamHub(
|
||||
@@ -38,7 +56,14 @@ class HubRepositoryImpl implements HubRepositoryInterface {
|
||||
hubName: name,
|
||||
address: address,
|
||||
)
|
||||
.city(city?.isNotEmpty == true ? city : '')
|
||||
.placeId(placeId)
|
||||
.latitude(latitude)
|
||||
.longitude(longitude)
|
||||
.city(cityValue?.isNotEmpty == true ? cityValue : '')
|
||||
.state(stateValue)
|
||||
.street(streetValue)
|
||||
.country(countryValue)
|
||||
.zipCode(zipCodeValue)
|
||||
.execute();
|
||||
final String? createdId = result.data?.teamHub_insert.id;
|
||||
if (createdId == null) {
|
||||
@@ -69,6 +94,25 @@ class HubRepositoryImpl implements HubRepositoryInterface {
|
||||
|
||||
@override
|
||||
Future<void> deleteHub(String id) async {
|
||||
final String? businessId = dc.ClientSessionStore.instance.session?.business?.id;
|
||||
if (businessId == null || businessId.isEmpty) {
|
||||
await _firebaseAuth.signOut();
|
||||
throw Exception('Business is missing. Please sign in again.');
|
||||
}
|
||||
|
||||
final QueryResult<
|
||||
dc.ListOrdersByBusinessAndTeamHubData,
|
||||
dc.ListOrdersByBusinessAndTeamHubVariables> result = await _dataConnect
|
||||
.listOrdersByBusinessAndTeamHub(
|
||||
businessId: businessId,
|
||||
teamHubId: id,
|
||||
)
|
||||
.execute();
|
||||
|
||||
if (result.data.orders.isNotEmpty) {
|
||||
throw Exception("Sorry this hub has orders, it can't be deleted.");
|
||||
}
|
||||
|
||||
await _dataConnect.deleteTeamHub(id: id).execute();
|
||||
}
|
||||
|
||||
@@ -192,4 +236,99 @@ class HubRepositoryImpl implements HubRepositoryInterface {
|
||||
)
|
||||
.toList();
|
||||
}
|
||||
|
||||
Future<_PlaceAddress?> _fetchPlaceAddress(String placeId) async {
|
||||
final Uri uri = Uri.https(
|
||||
'maps.googleapis.com',
|
||||
'/maps/api/place/details/json',
|
||||
<String, String>{
|
||||
'place_id': placeId,
|
||||
'fields': 'address_component',
|
||||
'key': HubsConstants.googlePlacesApiKey,
|
||||
},
|
||||
);
|
||||
try {
|
||||
final http.Response response = await http.get(uri);
|
||||
if (response.statusCode != 200) {
|
||||
return null;
|
||||
}
|
||||
|
||||
final Map<String, dynamic> payload =
|
||||
json.decode(response.body) as Map<String, dynamic>;
|
||||
if (payload['status'] != 'OK') {
|
||||
return null;
|
||||
}
|
||||
|
||||
final Map<String, dynamic>? result =
|
||||
payload['result'] as Map<String, dynamic>?;
|
||||
final List<dynamic>? components =
|
||||
result?['address_components'] as List<dynamic>?;
|
||||
if (components == null || components.isEmpty) {
|
||||
return null;
|
||||
}
|
||||
|
||||
String? streetNumber;
|
||||
String? route;
|
||||
String? city;
|
||||
String? state;
|
||||
String? country;
|
||||
String? zipCode;
|
||||
|
||||
for (final dynamic entry in components) {
|
||||
final Map<String, dynamic> component = entry as Map<String, dynamic>;
|
||||
final List<dynamic> types = component['types'] as List<dynamic>? ?? <dynamic>[];
|
||||
final String? longName = component['long_name'] as String?;
|
||||
final String? shortName = component['short_name'] as String?;
|
||||
|
||||
if (types.contains('street_number')) {
|
||||
streetNumber = longName;
|
||||
} else if (types.contains('route')) {
|
||||
route = longName;
|
||||
} else if (types.contains('locality')) {
|
||||
city = longName;
|
||||
} else if (types.contains('postal_town')) {
|
||||
city ??= longName;
|
||||
} else if (types.contains('administrative_area_level_2')) {
|
||||
city ??= longName;
|
||||
} else if (types.contains('administrative_area_level_1')) {
|
||||
state = shortName ?? longName;
|
||||
} else if (types.contains('country')) {
|
||||
country = shortName ?? longName;
|
||||
} else if (types.contains('postal_code')) {
|
||||
zipCode = longName;
|
||||
}
|
||||
}
|
||||
|
||||
final String? streetValue = <String?>[streetNumber, route]
|
||||
.where((String? value) => value != null && value!.isNotEmpty)
|
||||
.join(' ')
|
||||
.trim();
|
||||
|
||||
return _PlaceAddress(
|
||||
street: streetValue?.isEmpty == true ? null : streetValue,
|
||||
city: city,
|
||||
state: state,
|
||||
country: country,
|
||||
zipCode: zipCode,
|
||||
);
|
||||
} catch (_) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class _PlaceAddress {
|
||||
const _PlaceAddress({
|
||||
this.street,
|
||||
this.city,
|
||||
this.state,
|
||||
this.country,
|
||||
this.zipCode,
|
||||
});
|
||||
|
||||
final String? street;
|
||||
final String? city;
|
||||
final String? state;
|
||||
final String? country;
|
||||
final String? zipCode;
|
||||
}
|
||||
|
||||
@@ -10,11 +10,42 @@ class CreateHubArguments extends UseCaseArgument {
|
||||
/// The physical address of the hub.
|
||||
final String address;
|
||||
|
||||
final String? placeId;
|
||||
final double? latitude;
|
||||
final double? longitude;
|
||||
final String? city;
|
||||
final String? state;
|
||||
final String? street;
|
||||
final String? country;
|
||||
final String? zipCode;
|
||||
|
||||
/// Creates a [CreateHubArguments] instance.
|
||||
///
|
||||
/// Both [name] and [address] are required.
|
||||
const CreateHubArguments({required this.name, required this.address});
|
||||
const CreateHubArguments({
|
||||
required this.name,
|
||||
required this.address,
|
||||
this.placeId,
|
||||
this.latitude,
|
||||
this.longitude,
|
||||
this.city,
|
||||
this.state,
|
||||
this.street,
|
||||
this.country,
|
||||
this.zipCode,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[name, address];
|
||||
List<Object?> get props => <Object?>[
|
||||
name,
|
||||
address,
|
||||
placeId,
|
||||
latitude,
|
||||
longitude,
|
||||
city,
|
||||
state,
|
||||
street,
|
||||
country,
|
||||
zipCode,
|
||||
];
|
||||
}
|
||||
|
||||
@@ -15,7 +15,18 @@ abstract interface class HubRepositoryInterface {
|
||||
///
|
||||
/// Takes the [name] and [address] of the new hub.
|
||||
/// Returns the created [Hub] entity.
|
||||
Future<Hub> createHub({required String name, required String address});
|
||||
Future<Hub> createHub({
|
||||
required String name,
|
||||
required String address,
|
||||
String? placeId,
|
||||
double? latitude,
|
||||
double? longitude,
|
||||
String? city,
|
||||
String? state,
|
||||
String? street,
|
||||
String? country,
|
||||
String? zipCode,
|
||||
});
|
||||
|
||||
/// Deletes a hub by its [id].
|
||||
Future<void> deleteHub(String id);
|
||||
|
||||
@@ -21,6 +21,14 @@ class CreateHubUseCase implements UseCase<CreateHubArguments, Hub> {
|
||||
return _repository.createHub(
|
||||
name: arguments.name,
|
||||
address: arguments.address,
|
||||
placeId: arguments.placeId,
|
||||
latitude: arguments.latitude,
|
||||
longitude: arguments.longitude,
|
||||
city: arguments.city,
|
||||
state: arguments.state,
|
||||
street: arguments.street,
|
||||
country: arguments.country,
|
||||
zipCode: arguments.zipCode,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -84,7 +84,18 @@ class ClientHubsBloc extends Bloc<ClientHubsEvent, ClientHubsState>
|
||||
emit(state.copyWith(status: ClientHubsStatus.actionInProgress));
|
||||
try {
|
||||
await _createHubUseCase(
|
||||
CreateHubArguments(name: event.name, address: event.address),
|
||||
CreateHubArguments(
|
||||
name: event.name,
|
||||
address: event.address,
|
||||
placeId: event.placeId,
|
||||
latitude: event.latitude,
|
||||
longitude: event.longitude,
|
||||
city: event.city,
|
||||
state: event.state,
|
||||
street: event.street,
|
||||
country: event.country,
|
||||
zipCode: event.zipCode,
|
||||
),
|
||||
);
|
||||
final List<Hub> hubs = await _getHubsUseCase();
|
||||
emit(
|
||||
|
||||
@@ -18,11 +18,41 @@ class ClientHubsFetched extends ClientHubsEvent {
|
||||
class ClientHubsAddRequested extends ClientHubsEvent {
|
||||
final String name;
|
||||
final String address;
|
||||
final String? placeId;
|
||||
final double? latitude;
|
||||
final double? longitude;
|
||||
final String? city;
|
||||
final String? state;
|
||||
final String? street;
|
||||
final String? country;
|
||||
final String? zipCode;
|
||||
|
||||
const ClientHubsAddRequested({required this.name, required this.address});
|
||||
const ClientHubsAddRequested({
|
||||
required this.name,
|
||||
required this.address,
|
||||
this.placeId,
|
||||
this.latitude,
|
||||
this.longitude,
|
||||
this.city,
|
||||
this.state,
|
||||
this.street,
|
||||
this.country,
|
||||
this.zipCode,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object?> get props => <Object?>[name, address];
|
||||
List<Object?> get props => <Object?>[
|
||||
name,
|
||||
address,
|
||||
placeId,
|
||||
latitude,
|
||||
longitude,
|
||||
city,
|
||||
state,
|
||||
street,
|
||||
country,
|
||||
zipCode,
|
||||
];
|
||||
}
|
||||
|
||||
/// Event triggered to delete a hub.
|
||||
|
||||
@@ -27,8 +27,12 @@ class ClientHubsPage extends StatelessWidget {
|
||||
create: (BuildContext context) =>
|
||||
Modular.get<ClientHubsBloc>()..add(const ClientHubsFetched()),
|
||||
child: BlocConsumer<ClientHubsBloc, ClientHubsState>(
|
||||
listenWhen: (ClientHubsState previous, ClientHubsState current) {
|
||||
return previous.errorMessage != current.errorMessage ||
|
||||
previous.successMessage != current.successMessage;
|
||||
},
|
||||
listener: (BuildContext context, ClientHubsState state) {
|
||||
if (state.errorMessage != null) {
|
||||
if (state.errorMessage != null && state.errorMessage!.isNotEmpty) {
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text(state.errorMessage!)));
|
||||
@@ -36,7 +40,7 @@ class ClientHubsPage extends StatelessWidget {
|
||||
context,
|
||||
).add(const ClientHubsMessageCleared());
|
||||
}
|
||||
if (state.successMessage != null) {
|
||||
if (state.successMessage != null && state.successMessage!.isNotEmpty) {
|
||||
ScaffoldMessenger.of(
|
||||
context,
|
||||
).showSnackBar(SnackBar(content: Text(state.successMessage!)));
|
||||
@@ -106,9 +110,21 @@ class ClientHubsPage extends StatelessWidget {
|
||||
),
|
||||
if (state.showAddHubDialog)
|
||||
AddHubDialog(
|
||||
onCreate: (String name, String address) {
|
||||
onCreate: (
|
||||
String name,
|
||||
String address, {
|
||||
String? placeId,
|
||||
double? latitude,
|
||||
double? longitude,
|
||||
}) {
|
||||
BlocProvider.of<ClientHubsBloc>(context).add(
|
||||
ClientHubsAddRequested(name: name, address: address),
|
||||
ClientHubsAddRequested(
|
||||
name: name,
|
||||
address: address,
|
||||
placeId: placeId,
|
||||
latitude: latitude,
|
||||
longitude: longitude,
|
||||
),
|
||||
);
|
||||
},
|
||||
onCancel: () => BlocProvider.of<ClientHubsBloc>(
|
||||
|
||||
@@ -1,11 +1,20 @@
|
||||
import 'package:design_system/design_system.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:core_localization/core_localization.dart';
|
||||
import 'package:google_places_flutter/model/prediction.dart';
|
||||
|
||||
import 'hub_address_autocomplete.dart';
|
||||
|
||||
/// A dialog for adding a new hub.
|
||||
class AddHubDialog extends StatefulWidget {
|
||||
/// Callback when the "Create Hub" button is pressed.
|
||||
final Function(String name, String address) onCreate;
|
||||
final void Function(
|
||||
String name,
|
||||
String address, {
|
||||
String? placeId,
|
||||
double? latitude,
|
||||
double? longitude,
|
||||
}) onCreate;
|
||||
|
||||
/// Callback when the dialog is cancelled.
|
||||
final VoidCallback onCancel;
|
||||
@@ -24,18 +33,22 @@ class AddHubDialog extends StatefulWidget {
|
||||
class _AddHubDialogState extends State<AddHubDialog> {
|
||||
late final TextEditingController _nameController;
|
||||
late final TextEditingController _addressController;
|
||||
late final FocusNode _addressFocusNode;
|
||||
Prediction? _selectedPrediction;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
_nameController = TextEditingController();
|
||||
_addressController = TextEditingController();
|
||||
_addressFocusNode = FocusNode();
|
||||
}
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
_nameController.dispose();
|
||||
_addressController.dispose();
|
||||
_addressFocusNode.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@@ -74,12 +87,13 @@ class _AddHubDialogState extends State<AddHubDialog> {
|
||||
),
|
||||
const SizedBox(height: UiConstants.space4),
|
||||
_buildFieldLabel(t.client_hubs.add_hub_dialog.address_label),
|
||||
TextField(
|
||||
HubAddressAutocomplete(
|
||||
controller: _addressController,
|
||||
style: UiTypography.body1r.textPrimary,
|
||||
decoration: _buildInputDecoration(
|
||||
t.client_hubs.add_hub_dialog.address_hint,
|
||||
),
|
||||
hintText: t.client_hubs.add_hub_dialog.address_hint,
|
||||
focusNode: _addressFocusNode,
|
||||
onSelected: (Prediction prediction) {
|
||||
_selectedPrediction = prediction;
|
||||
},
|
||||
),
|
||||
const SizedBox(height: UiConstants.space8),
|
||||
Row(
|
||||
@@ -98,6 +112,13 @@ class _AddHubDialogState extends State<AddHubDialog> {
|
||||
widget.onCreate(
|
||||
_nameController.text,
|
||||
_addressController.text,
|
||||
placeId: _selectedPrediction?.placeId,
|
||||
latitude: double.tryParse(
|
||||
_selectedPrediction?.lat ?? '',
|
||||
),
|
||||
longitude: double.tryParse(
|
||||
_selectedPrediction?.lng ?? '',
|
||||
),
|
||||
);
|
||||
}
|
||||
},
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
import 'package:design_system/design_system.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:google_places_flutter/google_places_flutter.dart';
|
||||
import 'package:google_places_flutter/model/prediction.dart';
|
||||
|
||||
import '../../util/hubs_constants.dart';
|
||||
|
||||
class HubAddressAutocomplete extends StatelessWidget {
|
||||
const HubAddressAutocomplete({
|
||||
required this.controller,
|
||||
required this.hintText,
|
||||
this.focusNode,
|
||||
this.onSelected,
|
||||
super.key,
|
||||
});
|
||||
|
||||
final TextEditingController controller;
|
||||
final String hintText;
|
||||
final FocusNode? focusNode;
|
||||
final void Function(Prediction prediction)? onSelected;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return GooglePlaceAutoCompleteTextField(
|
||||
textEditingController: controller,
|
||||
focusNode: focusNode,
|
||||
googleAPIKey: HubsConstants.googlePlacesApiKey,
|
||||
debounceTime: 500,
|
||||
countries: HubsConstants.supportedCountries,
|
||||
isLatLngRequired: true,
|
||||
getPlaceDetailWithLatLng: (Prediction prediction) {
|
||||
onSelected?.call(prediction);
|
||||
},
|
||||
itemClick: (Prediction prediction) {
|
||||
controller.text = prediction.description ?? '';
|
||||
controller.selection = TextSelection.fromPosition(
|
||||
TextPosition(offset: controller.text.length),
|
||||
);
|
||||
onSelected?.call(prediction);
|
||||
},
|
||||
itemBuilder: (_, _, Prediction prediction) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.all(UiConstants.space2),
|
||||
child: Row(
|
||||
spacing: UiConstants.space1,
|
||||
children: <Widget>[
|
||||
const Icon(UiIcons.mapPin, color: UiColors.iconSecondary),
|
||||
Expanded(
|
||||
child: Text(
|
||||
prediction.description ?? "",
|
||||
style: UiTypography.body1r.textSecondary,
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
},
|
||||
textStyle: UiTypography.body1r.textPrimary,
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
class HubsConstants {
|
||||
static const String googlePlacesApiKey = String.fromEnvironment('GOOGLE_PLACES_API_KEY');
|
||||
static const List<String> supportedCountries = <String>['us'];
|
||||
}
|
||||
@@ -11,11 +11,7 @@ environment:
|
||||
dependencies:
|
||||
flutter:
|
||||
sdk: flutter
|
||||
flutter_bloc: ^8.1.0
|
||||
flutter_modular: ^6.3.2
|
||||
equatable: ^2.0.5
|
||||
lucide_icons: ^0.257.0
|
||||
|
||||
|
||||
# Architecture Packages
|
||||
krow_core:
|
||||
path: ../../../core
|
||||
@@ -27,8 +23,15 @@ dependencies:
|
||||
path: ../../../design_system
|
||||
core_localization:
|
||||
path: ../../../core_localization
|
||||
|
||||
flutter_bloc: ^8.1.0
|
||||
flutter_modular: ^6.3.2
|
||||
equatable: ^2.0.5
|
||||
lucide_icons: ^0.257.0
|
||||
firebase_auth: ^6.1.4
|
||||
firebase_data_connect: ^0.2.2+2
|
||||
google_places_flutter: ^2.1.1
|
||||
http: ^1.2.2
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
||||
@@ -24,8 +24,8 @@ class SettingsActions extends StatelessWidget {
|
||||
delegate: SliverChildListDelegate(<Widget>[
|
||||
const SizedBox(height: UiConstants.space5),
|
||||
|
||||
/// TODO: FEATURE_NOT_YET_IMPLEMENTED
|
||||
// Edit profile is not yet implemented
|
||||
/// TODO: FEATURE_NOT_YET_IMPLEMENTED
|
||||
// Edit profile is not yet implemented
|
||||
|
||||
// Hubs button
|
||||
UiButton.primary(
|
||||
@@ -49,12 +49,19 @@ class SettingsActions extends StatelessWidget {
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Handles the sign-out button click event.
|
||||
void _onSignoutClicked(BuildContext context) {
|
||||
ReadContext(
|
||||
context,
|
||||
).read<ClientSettingsBloc>().add(const ClientSettingsSignOutRequested());
|
||||
}
|
||||
|
||||
/// Shows a confirmation dialog for signing out.
|
||||
Future<void> _showSignOutDialog(BuildContext context) {
|
||||
return showDialog(
|
||||
context: context,
|
||||
builder: (BuildContext context) => AlertDialog(
|
||||
builder: (BuildContext dialogContext) => AlertDialog(
|
||||
backgroundColor: UiColors.bgPopup,
|
||||
elevation: 0,
|
||||
shape: RoundedRectangleBorder(borderRadius: UiConstants.radiusLg),
|
||||
@@ -70,12 +77,7 @@ class SettingsActions extends StatelessWidget {
|
||||
// Log out button
|
||||
UiButton.secondary(
|
||||
text: t.client_settings.profile.log_out,
|
||||
onPressed: () {
|
||||
Modular.to.pop();
|
||||
BlocProvider.of<ClientSettingsBloc>(
|
||||
context,
|
||||
).add(const ClientSettingsSignOutRequested());
|
||||
},
|
||||
onPressed: () => _onSignoutClicked(context),
|
||||
),
|
||||
|
||||
// Cancel button
|
||||
|
||||
@@ -62,10 +62,13 @@ class ViewOrdersRepositoryImpl implements IViewOrdersRepository {
|
||||
'end=${shiftRole.endTime?.toJson()} hours=$hours totalValue=$totalValue',
|
||||
);
|
||||
|
||||
final String eventName =
|
||||
shiftRole.shift.order.eventName ?? shiftRole.shift.title;
|
||||
|
||||
return domain.OrderItem(
|
||||
id: _shiftRoleKey(shiftRole.shiftId, shiftRole.roleId),
|
||||
orderId: shiftRole.shift.order.id,
|
||||
title: '${shiftRole.role.name} - ${shiftRole.shift.title}',
|
||||
title: '${shiftRole.role.name} - $eventName',
|
||||
clientName: businessName,
|
||||
status: status,
|
||||
date: dateStr,
|
||||
@@ -117,6 +120,8 @@ class ViewOrdersRepositoryImpl implements IViewOrdersRepository {
|
||||
'worker_name': application.staff.fullName,
|
||||
'status': 'confirmed',
|
||||
'photo_url': application.staff.photoUrl,
|
||||
'phone': application.staff.phone,
|
||||
'rating': application.staff.averageRating,
|
||||
});
|
||||
}
|
||||
return grouped;
|
||||
@@ -145,7 +150,7 @@ class ViewOrdersRepositoryImpl implements IViewOrdersRepository {
|
||||
if (timestamp == null) {
|
||||
return '';
|
||||
}
|
||||
final DateTime dateTime = timestamp.toDateTime();
|
||||
final DateTime dateTime = timestamp.toDateTime().toLocal();
|
||||
return DateFormat('HH:mm').format(dateTime);
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,7 @@ class ViewOrdersCubit extends Cubit<ViewOrdersState> {
|
||||
|
||||
final GetOrdersUseCase _getOrdersUseCase;
|
||||
final GetAcceptedApplicationsForDayUseCase _getAcceptedAppsUseCase;
|
||||
int _requestId = 0;
|
||||
|
||||
void _init() {
|
||||
updateWeekOffset(0); // Initialize calendar days
|
||||
@@ -33,6 +34,7 @@ class ViewOrdersCubit extends Cubit<ViewOrdersState> {
|
||||
required DateTime rangeEnd,
|
||||
required DateTime dayForApps,
|
||||
}) async {
|
||||
final int requestId = ++_requestId;
|
||||
emit(state.copyWith(status: ViewOrdersStatus.loading));
|
||||
try {
|
||||
final List<OrderItem> orders = await _getOrdersUseCase(
|
||||
@@ -42,6 +44,9 @@ class ViewOrdersCubit extends Cubit<ViewOrdersState> {
|
||||
OrdersDayArguments(day: dayForApps),
|
||||
);
|
||||
final List<OrderItem> updatedOrders = _applyApplications(orders, apps);
|
||||
if (requestId != _requestId) {
|
||||
return;
|
||||
}
|
||||
emit(
|
||||
state.copyWith(
|
||||
status: ViewOrdersStatus.success,
|
||||
@@ -50,6 +55,9 @@ class ViewOrdersCubit extends Cubit<ViewOrdersState> {
|
||||
);
|
||||
_updateDerivedState();
|
||||
} catch (_) {
|
||||
if (requestId != _requestId) {
|
||||
return;
|
||||
}
|
||||
emit(state.copyWith(status: ViewOrdersStatus.failure));
|
||||
}
|
||||
}
|
||||
@@ -88,6 +96,28 @@ class ViewOrdersCubit extends Cubit<ViewOrdersState> {
|
||||
);
|
||||
}
|
||||
|
||||
void jumpToDate(DateTime date) {
|
||||
final DateTime target = DateTime(date.year, date.month, date.day);
|
||||
final DateTime startDate = _calculateCalendarDays(0).first;
|
||||
final int diffDays = target.difference(startDate).inDays;
|
||||
final int targetOffset = (diffDays / 7).floor();
|
||||
final List<DateTime> calendarDays = _calculateCalendarDays(targetOffset);
|
||||
|
||||
emit(
|
||||
state.copyWith(
|
||||
weekOffset: targetOffset,
|
||||
calendarDays: calendarDays,
|
||||
selectedDate: target,
|
||||
),
|
||||
);
|
||||
|
||||
_loadOrdersForRange(
|
||||
rangeStart: calendarDays.first,
|
||||
rangeEnd: calendarDays.last,
|
||||
dayForApps: target,
|
||||
);
|
||||
}
|
||||
|
||||
void _updateDerivedState() {
|
||||
final List<OrderItem> filteredOrders = _calculateFilteredOrders(state);
|
||||
final int activeCount = _calculateCategoryCount('active');
|
||||
|
||||
@@ -20,24 +20,53 @@ import '../navigation/view_orders_navigator.dart';
|
||||
/// - Adhering to the project's Design System.
|
||||
class ViewOrdersPage extends StatelessWidget {
|
||||
/// Creates a [ViewOrdersPage].
|
||||
const ViewOrdersPage({super.key});
|
||||
const ViewOrdersPage({super.key, this.initialDate});
|
||||
|
||||
final DateTime? initialDate;
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return BlocProvider<ViewOrdersCubit>(
|
||||
create: (BuildContext context) => Modular.get<ViewOrdersCubit>(),
|
||||
child: const ViewOrdersView(),
|
||||
child: ViewOrdersView(initialDate: initialDate),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/// The internal view implementation for [ViewOrdersPage].
|
||||
class ViewOrdersView extends StatelessWidget {
|
||||
class ViewOrdersView extends StatefulWidget {
|
||||
/// Creates a [ViewOrdersView].
|
||||
const ViewOrdersView({super.key});
|
||||
const ViewOrdersView({super.key, this.initialDate});
|
||||
|
||||
final DateTime? initialDate;
|
||||
|
||||
@override
|
||||
State<ViewOrdersView> createState() => _ViewOrdersViewState();
|
||||
}
|
||||
|
||||
class _ViewOrdersViewState extends State<ViewOrdersView> {
|
||||
bool _didInitialJump = false;
|
||||
ViewOrdersCubit? _cubit;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
if (widget.initialDate != null) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
if (!mounted) return;
|
||||
if (_didInitialJump) return;
|
||||
_didInitialJump = true;
|
||||
_cubit ??= BlocProvider.of<ViewOrdersCubit>(context);
|
||||
_cubit!.jumpToDate(widget.initialDate!);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
if (_cubit == null) {
|
||||
_cubit = BlocProvider.of<ViewOrdersCubit>(context);
|
||||
}
|
||||
return BlocBuilder<ViewOrdersCubit, ViewOrdersState>(
|
||||
builder: (BuildContext context, ViewOrdersState state) {
|
||||
final List<DateTime> calendarDays = state.calendarDays;
|
||||
@@ -218,6 +247,7 @@ class ViewOrdersView extends StatelessWidget {
|
||||
label: t.client_view_orders.tabs.up_next,
|
||||
isSelected: state.filterTab == 'all',
|
||||
tabId: 'all',
|
||||
count: state.upNextCount,
|
||||
),
|
||||
const SizedBox(width: UiConstants.space6),
|
||||
_buildFilterTab(
|
||||
@@ -225,7 +255,7 @@ class ViewOrdersView extends StatelessWidget {
|
||||
label: t.client_view_orders.tabs.active,
|
||||
isSelected: state.filterTab == 'active',
|
||||
tabId: 'active',
|
||||
count: state.activeCount + state.upNextCount,
|
||||
count: state.activeCount,
|
||||
),
|
||||
const SizedBox(width: UiConstants.space6),
|
||||
_buildFilterTab(
|
||||
|
||||
@@ -3,9 +3,12 @@ import 'package:design_system/design_system.dart';
|
||||
import 'package:firebase_auth/firebase_auth.dart' as firebase;
|
||||
import 'package:firebase_data_connect/firebase_data_connect.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:krow_data_connect/krow_data_connect.dart' as dc;
|
||||
import 'package:krow_domain/krow_domain.dart';
|
||||
import 'package:url_launcher/url_launcher.dart';
|
||||
import '../blocs/view_orders_cubit.dart';
|
||||
|
||||
/// A rich card displaying details of a client order/shift.
|
||||
///
|
||||
@@ -30,7 +33,10 @@ class _ViewOrderCardState extends State<ViewOrderCard> {
|
||||
context: context,
|
||||
isScrollControlled: true,
|
||||
backgroundColor: Colors.transparent,
|
||||
builder: (BuildContext context) => _OrderEditSheet(order: order),
|
||||
builder: (BuildContext context) => _OrderEditSheet(
|
||||
order: order,
|
||||
onUpdated: () => this.context.read<ViewOrdersCubit>().updateWeekOffset(0),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
@@ -192,19 +198,7 @@ class _ViewOrderCardState extends State<ViewOrderCard> {
|
||||
order.clientName,
|
||||
style: UiTypography.body3r.textSecondary,
|
||||
),
|
||||
Padding(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 6,
|
||||
),
|
||||
child: Text(
|
||||
'•',
|
||||
style: UiTypography.body3r.textInactive,
|
||||
),
|
||||
),
|
||||
Text(
|
||||
_formatDate(dateStr: order.date),
|
||||
style: UiTypography.body3m.textSecondary,
|
||||
),
|
||||
const SizedBox(width: 0),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: UiConstants.space2),
|
||||
@@ -241,14 +235,16 @@ class _ViewOrderCardState extends State<ViewOrderCard> {
|
||||
onTap: () => _openEditSheet(order: order),
|
||||
),
|
||||
const SizedBox(width: UiConstants.space2),
|
||||
_buildHeaderIconButton(
|
||||
icon: _expanded
|
||||
? UiIcons.chevronUp
|
||||
: UiIcons.chevronDown,
|
||||
color: UiColors.iconSecondary,
|
||||
bgColor: UiColors.bgSecondary,
|
||||
onTap: () => setState(() => _expanded = !_expanded),
|
||||
),
|
||||
if (order.confirmedApps.isNotEmpty)
|
||||
_buildHeaderIconButton(
|
||||
icon: _expanded
|
||||
? UiIcons.chevronUp
|
||||
: UiIcons.chevronDown,
|
||||
color: UiColors.iconSecondary,
|
||||
bgColor: UiColors.bgSecondary,
|
||||
onTap: () =>
|
||||
setState(() => _expanded = !_expanded),
|
||||
),
|
||||
],
|
||||
),
|
||||
],
|
||||
@@ -276,8 +272,7 @@ class _ViewOrderCardState extends State<ViewOrderCard> {
|
||||
_buildStatDivider(),
|
||||
_buildStatItem(
|
||||
icon: UiIcons.users,
|
||||
value:
|
||||
'${order.filled > 0 ? order.filled : order.workersNeeded}',
|
||||
value: '${order.workersNeeded}',
|
||||
label: 'Workers',
|
||||
),
|
||||
],
|
||||
@@ -320,7 +315,7 @@ class _ViewOrderCardState extends State<ViewOrderCard> {
|
||||
),
|
||||
const SizedBox(width: 8),
|
||||
Text(
|
||||
'${order.filled}/${order.workersNeeded} Workers Filled',
|
||||
'${order.workersNeeded} Workers Filled',
|
||||
style: UiTypography.body2m.textPrimary,
|
||||
),
|
||||
],
|
||||
@@ -492,6 +487,7 @@ class _ViewOrderCardState extends State<ViewOrderCard> {
|
||||
|
||||
/// Builds a detailed row for a worker.
|
||||
Widget _buildWorkerRow(Map<String, dynamic> app) {
|
||||
final String? phone = app['phone'] as String?;
|
||||
return Container(
|
||||
margin: const EdgeInsets.only(bottom: 12),
|
||||
padding: const EdgeInsets.all(12),
|
||||
@@ -522,9 +518,19 @@ class _ViewOrderCardState extends State<ViewOrderCard> {
|
||||
const SizedBox(height: 2),
|
||||
Row(
|
||||
children: <Widget>[
|
||||
const Icon(UiIcons.star, size: 10, color: UiColors.accent),
|
||||
const SizedBox(width: 2),
|
||||
Text('4.8', style: UiTypography.footnote2r.textSecondary),
|
||||
if ((app['rating'] as num?) != null &&
|
||||
(app['rating'] as num) > 0) ...<Widget>[
|
||||
const Icon(
|
||||
UiIcons.star,
|
||||
size: 10,
|
||||
color: UiColors.accent,
|
||||
),
|
||||
const SizedBox(width: 2),
|
||||
Text(
|
||||
(app['rating'] as num).toStringAsFixed(1),
|
||||
style: UiTypography.footnote2r.textSecondary,
|
||||
),
|
||||
],
|
||||
if (app['check_in_time'] != null) ...<Widget>[
|
||||
const SizedBox(width: 8),
|
||||
Container(
|
||||
@@ -543,20 +549,70 @@ class _ViewOrderCardState extends State<ViewOrderCard> {
|
||||
),
|
||||
),
|
||||
),
|
||||
] else if ((app['status'] as String?)?.isNotEmpty ?? false) ...<Widget>[
|
||||
const SizedBox(width: 8),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: 4,
|
||||
vertical: 1,
|
||||
),
|
||||
decoration: BoxDecoration(
|
||||
color: UiColors.bgSecondary,
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
),
|
||||
child: Text(
|
||||
(app['status'] as String).toUpperCase(),
|
||||
style: UiTypography.titleUppercase4m.copyWith(
|
||||
color: UiColors.textSecondary,
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
_buildActionIconButton(icon: UiIcons.phone, onTap: () {}),
|
||||
const SizedBox(width: 8),
|
||||
_buildActionIconButton(icon: UiIcons.messageCircle, onTap: () {}),
|
||||
if (phone != null && phone.isNotEmpty) ...<Widget>[
|
||||
_buildActionIconButton(
|
||||
icon: UiIcons.phone,
|
||||
onTap: () => _confirmAndCall(phone),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
Future<void> _confirmAndCall(String phone) async {
|
||||
final bool? shouldCall = await showDialog<bool>(
|
||||
context: context,
|
||||
builder: (BuildContext context) {
|
||||
return AlertDialog(
|
||||
title: const Text('Call'),
|
||||
content: Text('Do you want to call $phone?'),
|
||||
actions: <Widget>[
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(false),
|
||||
child: const Text('Cancel'),
|
||||
),
|
||||
TextButton(
|
||||
onPressed: () => Navigator.of(context).pop(true),
|
||||
child: const Text('Call'),
|
||||
),
|
||||
],
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
if (shouldCall != true) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Uri uri = Uri(scheme: 'tel', path: phone);
|
||||
await launchUrl(uri);
|
||||
}
|
||||
|
||||
/// Specialized action button for worker rows.
|
||||
Widget _buildActionIconButton({
|
||||
required IconData icon,
|
||||
@@ -644,9 +700,13 @@ class _ShiftRoleKey {
|
||||
/// A sophisticated bottom sheet for editing an existing order,
|
||||
/// following the Unified Order Flow prototype and matching OneTimeOrderView.
|
||||
class _OrderEditSheet extends StatefulWidget {
|
||||
const _OrderEditSheet({required this.order});
|
||||
const _OrderEditSheet({
|
||||
required this.order,
|
||||
this.onUpdated,
|
||||
});
|
||||
|
||||
final OrderItem order;
|
||||
final VoidCallback? onUpdated;
|
||||
|
||||
@override
|
||||
State<_OrderEditSheet> createState() => _OrderEditSheetState();
|
||||
@@ -658,6 +718,7 @@ class _OrderEditSheetState extends State<_OrderEditSheet> {
|
||||
|
||||
late TextEditingController _dateController;
|
||||
late TextEditingController _globalLocationController;
|
||||
late TextEditingController _orderNameController;
|
||||
|
||||
late List<Map<String, dynamic>> _positions;
|
||||
|
||||
@@ -667,6 +728,8 @@ class _OrderEditSheetState extends State<_OrderEditSheet> {
|
||||
List<Vendor> _vendors = const <Vendor>[];
|
||||
Vendor? _selectedVendor;
|
||||
List<_RoleOption> _roles = const <_RoleOption>[];
|
||||
List<dc.ListTeamHubsByOwnerIdTeamHubs> _hubs = const <dc.ListTeamHubsByOwnerIdTeamHubs>[];
|
||||
dc.ListTeamHubsByOwnerIdTeamHubs? _selectedHub;
|
||||
|
||||
String? _shiftId;
|
||||
List<_ShiftRoleKey> _originalShiftRoles = const <_ShiftRoleKey>[];
|
||||
@@ -678,6 +741,7 @@ class _OrderEditSheetState extends State<_OrderEditSheet> {
|
||||
_globalLocationController = TextEditingController(
|
||||
text: widget.order.locationAddress,
|
||||
);
|
||||
_orderNameController = TextEditingController();
|
||||
|
||||
_positions = <Map<String, dynamic>>[
|
||||
<String, dynamic>{
|
||||
@@ -700,6 +764,7 @@ class _OrderEditSheetState extends State<_OrderEditSheet> {
|
||||
void dispose() {
|
||||
_dateController.dispose();
|
||||
_globalLocationController.dispose();
|
||||
_orderNameController.dispose();
|
||||
super.dispose();
|
||||
}
|
||||
|
||||
@@ -728,6 +793,7 @@ class _OrderEditSheetState extends State<_OrderEditSheet> {
|
||||
final List<dc.ListShiftRolesByBusinessAndOrderShiftRoles> shiftRoles =
|
||||
result.data.shiftRoles;
|
||||
if (shiftRoles.isEmpty) {
|
||||
await _loadHubsAndSelect();
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -737,13 +803,14 @@ class _OrderEditSheetState extends State<_OrderEditSheet> {
|
||||
final String dateText = orderDate == null
|
||||
? widget.order.date
|
||||
: DateFormat('yyyy-MM-dd').format(orderDate);
|
||||
final String location = firstShift.order.location ??
|
||||
final String location = firstShift.order.teamHub?.hubName ??
|
||||
firstShift.locationAddress ??
|
||||
firstShift.location ??
|
||||
widget.order.locationAddress;
|
||||
|
||||
_dateController.text = dateText;
|
||||
_globalLocationController.text = location;
|
||||
_orderNameController.text = firstShift.order.eventName ?? '';
|
||||
_shiftId = shiftRoles.first.shiftId;
|
||||
|
||||
final List<Map<String, dynamic>> positions =
|
||||
@@ -774,6 +841,13 @@ class _OrderEditSheetState extends State<_OrderEditSheet> {
|
||||
.toList();
|
||||
|
||||
await _loadVendorsAndSelect(firstShift.order.vendorId);
|
||||
final dc.ListShiftRolesByBusinessAndOrderShiftRolesShiftOrderTeamHub?
|
||||
teamHub = firstShift.order.teamHub;
|
||||
await _loadHubsAndSelect(
|
||||
placeId: teamHub?.placeId,
|
||||
hubName: teamHub?.hubName,
|
||||
address: teamHub?.address,
|
||||
);
|
||||
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
@@ -786,6 +860,75 @@ class _OrderEditSheetState extends State<_OrderEditSheet> {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _loadHubsAndSelect({
|
||||
String? placeId,
|
||||
String? hubName,
|
||||
String? address,
|
||||
}) async {
|
||||
final String? businessId =
|
||||
dc.ClientSessionStore.instance.session?.business?.id;
|
||||
if (businessId == null || businessId.isEmpty) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
final QueryResult<
|
||||
dc.ListTeamHubsByOwnerIdData,
|
||||
dc.ListTeamHubsByOwnerIdVariables> result = await _dataConnect
|
||||
.listTeamHubsByOwnerId(ownerId: businessId)
|
||||
.execute();
|
||||
|
||||
final List<dc.ListTeamHubsByOwnerIdTeamHubs> hubs = result.data.teamHubs;
|
||||
dc.ListTeamHubsByOwnerIdTeamHubs? selected;
|
||||
|
||||
if (placeId != null && placeId.isNotEmpty) {
|
||||
for (final dc.ListTeamHubsByOwnerIdTeamHubs hub in hubs) {
|
||||
if (hub.placeId == placeId) {
|
||||
selected = hub;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (selected == null && hubName != null && hubName.isNotEmpty) {
|
||||
for (final dc.ListTeamHubsByOwnerIdTeamHubs hub in hubs) {
|
||||
if (hub.hubName == hubName) {
|
||||
selected = hub;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (selected == null && address != null && address.isNotEmpty) {
|
||||
for (final dc.ListTeamHubsByOwnerIdTeamHubs hub in hubs) {
|
||||
if (hub.address == address) {
|
||||
selected = hub;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
selected ??= hubs.isNotEmpty ? hubs.first : null;
|
||||
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_hubs = hubs;
|
||||
_selectedHub = selected;
|
||||
if (selected != null) {
|
||||
_globalLocationController.text = selected.address;
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (_) {
|
||||
if (mounted) {
|
||||
setState(() {
|
||||
_hubs = const <dc.ListTeamHubsByOwnerIdTeamHubs>[];
|
||||
_selectedHub = null;
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _loadVendorsAndSelect(String? selectedVendorId) async {
|
||||
try {
|
||||
final QueryResult<dc.ListVendorsData, void> result =
|
||||
@@ -874,7 +1017,7 @@ class _OrderEditSheetState extends State<_OrderEditSheet> {
|
||||
String _formatTimeForField(Timestamp? value) {
|
||||
if (value == null) return '';
|
||||
try {
|
||||
return DateFormat('HH:mm').format(value.toDateTime());
|
||||
return DateFormat('HH:mm').format(value.toDateTime().toLocal());
|
||||
} catch (_) {
|
||||
return '';
|
||||
}
|
||||
@@ -948,7 +1091,8 @@ class _OrderEditSheetState extends State<_OrderEditSheet> {
|
||||
}
|
||||
|
||||
Timestamp _toTimestamp(DateTime date) {
|
||||
final int millis = date.millisecondsSinceEpoch;
|
||||
final DateTime utc = date.toUtc();
|
||||
final int millis = utc.millisecondsSinceEpoch;
|
||||
final int seconds = millis ~/ 1000;
|
||||
final int nanos = (millis % 1000) * 1000000;
|
||||
return Timestamp(nanos, seconds);
|
||||
@@ -987,7 +1131,10 @@ class _OrderEditSheetState extends State<_OrderEditSheet> {
|
||||
}
|
||||
|
||||
final DateTime orderDate = _parseDate(_dateController.text);
|
||||
final String location = _globalLocationController.text;
|
||||
final dc.ListTeamHubsByOwnerIdTeamHubs? selectedHub = _selectedHub;
|
||||
if (selectedHub == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
int totalWorkers = 0;
|
||||
double shiftCost = 0;
|
||||
@@ -1071,19 +1218,32 @@ class _OrderEditSheetState extends State<_OrderEditSheet> {
|
||||
.execute();
|
||||
}
|
||||
|
||||
final DateTime orderDateOnly = DateTime.utc(
|
||||
orderDate.year,
|
||||
orderDate.month,
|
||||
orderDate.day,
|
||||
);
|
||||
|
||||
await _dataConnect
|
||||
.updateOrder(id: widget.order.orderId)
|
||||
.updateOrder(id: widget.order.orderId, teamHubId: selectedHub.id)
|
||||
.vendorId(_selectedVendor?.id)
|
||||
.location(location)
|
||||
.date(_toTimestamp(orderDate))
|
||||
.date(_toTimestamp(orderDateOnly))
|
||||
.eventName(_orderNameController.text)
|
||||
.execute();
|
||||
|
||||
await _dataConnect
|
||||
.updateShift(id: _shiftId!)
|
||||
.title('shift 1 ${DateFormat('yyyy-MM-dd').format(orderDate)}')
|
||||
.date(_toTimestamp(orderDate))
|
||||
.location(location)
|
||||
.locationAddress(location)
|
||||
.date(_toTimestamp(orderDateOnly))
|
||||
.location(selectedHub.hubName)
|
||||
.locationAddress(selectedHub.address)
|
||||
.latitude(selectedHub.latitude)
|
||||
.longitude(selectedHub.longitude)
|
||||
.placeId(selectedHub.placeId)
|
||||
.city(selectedHub.city)
|
||||
.state(selectedHub.state)
|
||||
.street(selectedHub.street)
|
||||
.country(selectedHub.country)
|
||||
.workersNeeded(totalWorkers)
|
||||
.cost(shiftCost)
|
||||
.durationDays(1)
|
||||
@@ -1185,11 +1345,57 @@ class _OrderEditSheetState extends State<_OrderEditSheet> {
|
||||
),
|
||||
const SizedBox(height: UiConstants.space4),
|
||||
|
||||
_buildSectionHeader('LOCATION'),
|
||||
_buildSectionHeader('ORDER NAME'),
|
||||
UiTextField(
|
||||
controller: _globalLocationController,
|
||||
hintText: 'Business address',
|
||||
prefixIcon: UiIcons.mapPin,
|
||||
controller: _orderNameController,
|
||||
hintText: 'Order name',
|
||||
prefixIcon: UiIcons.briefcase,
|
||||
),
|
||||
const SizedBox(height: UiConstants.space4),
|
||||
|
||||
_buildSectionHeader('HUB'),
|
||||
Container(
|
||||
padding: const EdgeInsets.symmetric(
|
||||
horizontal: UiConstants.space3,
|
||||
),
|
||||
height: 48,
|
||||
decoration: BoxDecoration(
|
||||
color: UiColors.white,
|
||||
borderRadius: UiConstants.radiusMd,
|
||||
border: Border.all(color: UiColors.border),
|
||||
),
|
||||
child: DropdownButtonHideUnderline(
|
||||
child: DropdownButton<dc.ListTeamHubsByOwnerIdTeamHubs>(
|
||||
isExpanded: true,
|
||||
value: _selectedHub,
|
||||
icon: const Icon(
|
||||
UiIcons.chevronDown,
|
||||
size: 18,
|
||||
color: UiColors.iconSecondary,
|
||||
),
|
||||
onChanged:
|
||||
(dc.ListTeamHubsByOwnerIdTeamHubs? hub) {
|
||||
if (hub != null) {
|
||||
setState(() {
|
||||
_selectedHub = hub;
|
||||
_globalLocationController.text = hub.address;
|
||||
});
|
||||
}
|
||||
},
|
||||
items: _hubs.map(
|
||||
(dc.ListTeamHubsByOwnerIdTeamHubs hub) {
|
||||
return DropdownMenuItem<
|
||||
dc.ListTeamHubsByOwnerIdTeamHubs>(
|
||||
value: hub,
|
||||
child: Text(
|
||||
hub.hubName,
|
||||
style: UiTypography.body2m.textPrimary,
|
||||
),
|
||||
);
|
||||
},
|
||||
).toList(),
|
||||
),
|
||||
),
|
||||
),
|
||||
const SizedBox(height: UiConstants.space6),
|
||||
|
||||
@@ -1370,7 +1576,19 @@ class _OrderEditSheetState extends State<_OrderEditSheet> {
|
||||
child: _buildInlineTimeInput(
|
||||
label: 'Start',
|
||||
value: pos['start_time'],
|
||||
onTap: () {},
|
||||
onTap: () async {
|
||||
final TimeOfDay? picked = await showTimePicker(
|
||||
context: context,
|
||||
initialTime: TimeOfDay.now(),
|
||||
);
|
||||
if (picked != null && context.mounted) {
|
||||
_updatePosition(
|
||||
index,
|
||||
'start_time',
|
||||
picked.format(context),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(width: UiConstants.space2),
|
||||
@@ -1378,7 +1596,19 @@ class _OrderEditSheetState extends State<_OrderEditSheet> {
|
||||
child: _buildInlineTimeInput(
|
||||
label: 'End',
|
||||
value: pos['end_time'],
|
||||
onTap: () {},
|
||||
onTap: () async {
|
||||
final TimeOfDay? picked = await showTimePicker(
|
||||
context: context,
|
||||
initialTime: TimeOfDay.now(),
|
||||
);
|
||||
if (picked != null && context.mounted) {
|
||||
_updatePosition(
|
||||
index,
|
||||
'end_time',
|
||||
picked.format(context),
|
||||
);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
const SizedBox(width: UiConstants.space2),
|
||||
@@ -1766,7 +1996,10 @@ class _OrderEditSheetState extends State<_OrderEditSheet> {
|
||||
onPressed: () async {
|
||||
setState(() => _isLoading = true);
|
||||
await _saveOrderChanges();
|
||||
if (mounted) Navigator.pop(context);
|
||||
if (mounted) {
|
||||
widget.onUpdated?.call();
|
||||
Navigator.pop(context);
|
||||
}
|
||||
},
|
||||
),
|
||||
),
|
||||
|
||||
@@ -43,6 +43,23 @@ class ViewOrdersModule extends Module {
|
||||
|
||||
@override
|
||||
void routes(RouteManager r) {
|
||||
r.child('/', child: (BuildContext context) => const ViewOrdersPage());
|
||||
r.child(
|
||||
'/',
|
||||
child: (BuildContext context) {
|
||||
final Object? args = Modular.args.data;
|
||||
DateTime? initialDate;
|
||||
if (args is DateTime) {
|
||||
initialDate = args;
|
||||
} else if (args is Map<String, dynamic>) {
|
||||
final Object? rawDate = args['initialDate'];
|
||||
if (rawDate is DateTime) {
|
||||
initialDate = rawDate;
|
||||
} else if (rawDate is String) {
|
||||
initialDate = DateTime.tryParse(rawDate);
|
||||
}
|
||||
}
|
||||
return ViewOrdersPage(initialDate: initialDate);
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
name: view_orders
|
||||
description: Client View Orders feature package
|
||||
publish_to: 'none'
|
||||
version: 1.0.0+1
|
||||
version: 0.0.1
|
||||
resolution: workspace
|
||||
|
||||
environment:
|
||||
|
||||
@@ -16,6 +16,6 @@ extension AuthNavigator on IModularNavigator {
|
||||
|
||||
/// Navigates to the worker home (external to this module).
|
||||
void pushWorkerHome() {
|
||||
pushNamed('/worker-main/home/');
|
||||
pushNamed('/worker-main/home');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
// ignore: unused_import
|
||||
// import 'package:data_connect/data_connect.dart';
|
||||
import '../../domain/entities/payment_summary.dart';
|
||||
import '../../domain/entities/payment_transaction.dart';
|
||||
import '../../domain/repositories/payments_repository.dart';
|
||||
|
||||
@@ -3,6 +3,7 @@ import 'package:firebase_data_connect/firebase_data_connect.dart';
|
||||
import 'package:krow_data_connect/src/session/staff_session_store.dart';
|
||||
import 'package:krow_domain/krow_domain.dart';
|
||||
import '../../domain/repositories/payments_repository.dart';
|
||||
import '../datasources/payments_remote_datasource.dart';
|
||||
|
||||
extension TimestampExt on Timestamp {
|
||||
DateTime toDate() {
|
||||
@@ -11,11 +12,6 @@ extension TimestampExt on Timestamp {
|
||||
}
|
||||
|
||||
/// Implementation of [PaymentsRepository].
|
||||
///
|
||||
/// This class handles the retrieval of payment data by delegating to the
|
||||
/// [FinancialRepositoryMock] from the data connect package.
|
||||
///
|
||||
/// It resides in the data layer and depends on the domain layer for the repository interface.
|
||||
class PaymentsRepositoryImpl implements PaymentsRepository {
|
||||
PaymentsRepositoryImpl();
|
||||
|
||||
|
||||
@@ -0,0 +1,23 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
class PaymentSummary extends Equatable {
|
||||
final double weeklyEarnings;
|
||||
final double monthlyEarnings;
|
||||
final double pendingEarnings;
|
||||
final double totalEarnings;
|
||||
|
||||
const PaymentSummary({
|
||||
required this.weeklyEarnings,
|
||||
required this.monthlyEarnings,
|
||||
required this.pendingEarnings,
|
||||
required this.totalEarnings,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [
|
||||
weeklyEarnings,
|
||||
monthlyEarnings,
|
||||
pendingEarnings,
|
||||
totalEarnings,
|
||||
];
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
class PaymentTransaction extends Equatable {
|
||||
final String id;
|
||||
final String title;
|
||||
final String location;
|
||||
final String address;
|
||||
final String workedTime;
|
||||
final double amount;
|
||||
final String status;
|
||||
final int hours;
|
||||
final double rate;
|
||||
final DateTime date;
|
||||
|
||||
const PaymentTransaction({
|
||||
required this.id,
|
||||
required this.title,
|
||||
required this.location,
|
||||
required this.address,
|
||||
required this.workedTime,
|
||||
required this.amount,
|
||||
required this.status,
|
||||
required this.hours,
|
||||
required this.rate,
|
||||
required this.date,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [
|
||||
id,
|
||||
title,
|
||||
location,
|
||||
address,
|
||||
workedTime,
|
||||
amount,
|
||||
status,
|
||||
hours,
|
||||
rate,
|
||||
date,
|
||||
];
|
||||
}
|
||||
@@ -1,10 +1,14 @@
|
||||
import 'package:krow_domain/krow_domain.dart';
|
||||
import '../entities/payment_summary.dart';
|
||||
import '../entities/payment_transaction.dart';
|
||||
|
||||
/// Repository interface for Payments feature.
|
||||
///
|
||||
/// Defines the contract for data access related to staff payments.
|
||||
/// Implementations of this interface should reside in the data layer.
|
||||
abstract class PaymentsRepository {
|
||||
/// Fetches the list of payments for the current staff member.
|
||||
Future<List<StaffPayment>> getPayments();
|
||||
/// Fetches the payment summary (earnings).
|
||||
Future<PaymentSummary> getPaymentSummary();
|
||||
|
||||
/// Fetches the payment history for a specific period.
|
||||
Future<List<PaymentTransaction>> getPaymentHistory(String period);
|
||||
}
|
||||
|
||||
@@ -1,20 +1,19 @@
|
||||
import 'package:krow_core/core.dart';
|
||||
import 'package:krow_domain/krow_domain.dart';
|
||||
import '../arguments/get_payment_history_arguments.dart';
|
||||
import '../entities/payment_transaction.dart';
|
||||
import '../repositories/payments_repository.dart';
|
||||
|
||||
/// Use case to retrieve payment history filtered by a period.
|
||||
///
|
||||
/// This use case delegates the data retrieval to [PaymentsRepository].
|
||||
class GetPaymentHistoryUseCase extends UseCase<GetPaymentHistoryArguments, List<StaffPayment>> {
|
||||
class GetPaymentHistoryUseCase extends UseCase<GetPaymentHistoryArguments, List<PaymentTransaction>> {
|
||||
final PaymentsRepository repository;
|
||||
|
||||
/// Creates a [GetPaymentHistoryUseCase].
|
||||
GetPaymentHistoryUseCase(this.repository);
|
||||
|
||||
@override
|
||||
Future<List<StaffPayment>> call(GetPaymentHistoryArguments arguments) async {
|
||||
// TODO: Implement filtering by period
|
||||
return await repository.getPayments();
|
||||
Future<List<PaymentTransaction>> call(GetPaymentHistoryArguments arguments) async {
|
||||
return await repository.getPaymentHistory(arguments.period);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,16 @@
|
||||
import 'package:krow_core/core.dart';
|
||||
import 'package:krow_domain/krow_domain.dart';
|
||||
import '../entities/payment_summary.dart';
|
||||
import '../repositories/payments_repository.dart';
|
||||
|
||||
/// Use case to retrieve payment summary information.
|
||||
///
|
||||
/// It fetches the full list of payments, which ideally should be aggregated
|
||||
/// by the presentation layer or a specific data source method.
|
||||
class GetPaymentSummaryUseCase extends NoInputUseCase<List<StaffPayment>> {
|
||||
class GetPaymentSummaryUseCase extends NoInputUseCase<PaymentSummary> {
|
||||
final PaymentsRepository repository;
|
||||
|
||||
/// Creates a [GetPaymentSummaryUseCase].
|
||||
GetPaymentSummaryUseCase(this.repository);
|
||||
|
||||
@override
|
||||
Future<List<StaffPayment>> call() async {
|
||||
return await repository.getPayments();
|
||||
Future<PaymentSummary> call() async {
|
||||
return await repository.getPaymentSummary();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:krow_domain/krow_domain.dart';
|
||||
import '../../../domain/arguments/get_payment_history_arguments.dart';
|
||||
import '../../../domain/usecases/get_payment_summary_usecase.dart';
|
||||
import '../../../domain/entities/payment_summary.dart';
|
||||
import '../../../domain/entities/payment_transaction.dart';
|
||||
import '../../../domain/usecases/get_payment_history_usecase.dart';
|
||||
import '../../models/payment_stats.dart';
|
||||
import '../../../domain/usecases/get_payment_summary_usecase.dart';
|
||||
import 'payments_event.dart';
|
||||
import 'payments_state.dart';
|
||||
|
||||
@@ -25,14 +25,13 @@ class PaymentsBloc extends Bloc<PaymentsEvent, PaymentsState> {
|
||||
) async {
|
||||
emit(PaymentsLoading());
|
||||
try {
|
||||
final List<StaffPayment> allPayments = await getPaymentSummary();
|
||||
final PaymentStats stats = _calculateStats(allPayments);
|
||||
final PaymentSummary currentSummary = await getPaymentSummary();
|
||||
|
||||
final List<StaffPayment> history = await getPaymentHistory(
|
||||
final List<PaymentTransaction> history = await getPaymentHistory(
|
||||
const GetPaymentHistoryArguments('week'),
|
||||
);
|
||||
emit(PaymentsLoaded(
|
||||
summary: stats,
|
||||
summary: currentSummary,
|
||||
history: history,
|
||||
activePeriod: 'week',
|
||||
));
|
||||
@@ -48,7 +47,7 @@ class PaymentsBloc extends Bloc<PaymentsEvent, PaymentsState> {
|
||||
final PaymentsState currentState = state;
|
||||
if (currentState is PaymentsLoaded) {
|
||||
try {
|
||||
final List<StaffPayment> newHistory = await getPaymentHistory(
|
||||
final List<PaymentTransaction> newHistory = await getPaymentHistory(
|
||||
GetPaymentHistoryArguments(event.period),
|
||||
);
|
||||
emit(currentState.copyWith(
|
||||
@@ -60,38 +59,4 @@ class PaymentsBloc extends Bloc<PaymentsEvent, PaymentsState> {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PaymentStats _calculateStats(List<StaffPayment> payments) {
|
||||
double total = 0;
|
||||
double pending = 0;
|
||||
double weekly = 0;
|
||||
double monthly = 0;
|
||||
|
||||
final DateTime now = DateTime.now();
|
||||
|
||||
for (final StaffPayment p in payments) {
|
||||
// Assuming all payments count towards total history
|
||||
total += p.amount;
|
||||
|
||||
if (p.status == PaymentStatus.pending) {
|
||||
pending += p.amount;
|
||||
}
|
||||
|
||||
if (p.paidAt != null) {
|
||||
if (now.difference(p.paidAt!).inDays < 7) {
|
||||
weekly += p.amount;
|
||||
}
|
||||
if (now.month == p.paidAt!.month && now.year == p.paidAt!.year) {
|
||||
monthly += p.amount;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return PaymentStats(
|
||||
totalEarnings: total,
|
||||
pendingEarnings: pending,
|
||||
weeklyEarnings: weekly,
|
||||
monthlyEarnings: monthly,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
import 'package:krow_domain/krow_domain.dart';
|
||||
import '../../models/payment_stats.dart';
|
||||
import '../../../domain/entities/payment_summary.dart';
|
||||
import '../../../domain/entities/payment_transaction.dart';
|
||||
|
||||
abstract class PaymentsState extends Equatable {
|
||||
const PaymentsState();
|
||||
@@ -14,8 +14,8 @@ class PaymentsInitial extends PaymentsState {}
|
||||
class PaymentsLoading extends PaymentsState {}
|
||||
|
||||
class PaymentsLoaded extends PaymentsState {
|
||||
final PaymentStats summary;
|
||||
final List<StaffPayment> history;
|
||||
final PaymentSummary summary;
|
||||
final List<PaymentTransaction> history;
|
||||
final String activePeriod;
|
||||
|
||||
const PaymentsLoaded({
|
||||
@@ -25,8 +25,8 @@ class PaymentsLoaded extends PaymentsState {
|
||||
});
|
||||
|
||||
PaymentsLoaded copyWith({
|
||||
PaymentStats? summary,
|
||||
List<StaffPayment>? history,
|
||||
PaymentSummary? summary,
|
||||
List<PaymentTransaction>? history,
|
||||
String? activePeriod,
|
||||
}) {
|
||||
return PaymentsLoaded(
|
||||
|
||||
@@ -3,7 +3,7 @@ import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:flutter_modular/flutter_modular.dart';
|
||||
import 'package:lucide_icons/lucide_icons.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
import 'package:krow_domain/krow_domain.dart';
|
||||
import '../../domain/entities/payment_transaction.dart';
|
||||
import '../blocs/payments/payments_bloc.dart';
|
||||
import '../blocs/payments/payments_event.dart';
|
||||
import '../blocs/payments/payments_state.dart';
|
||||
@@ -184,19 +184,19 @@ class _PaymentsPageState extends State<PaymentsPage> {
|
||||
),
|
||||
const SizedBox(height: 12),
|
||||
Column(
|
||||
children: state.history.map((StaffPayment payment) {
|
||||
children: state.history.map((PaymentTransaction payment) {
|
||||
return Padding(
|
||||
padding: const EdgeInsets.only(bottom: 8),
|
||||
child: PaymentHistoryItem(
|
||||
amount: payment.amount,
|
||||
title: 'Assignment ${payment.assignmentId}',
|
||||
location: 'Location', // TODO: Fetch from assignment
|
||||
address: '',
|
||||
date: payment.paidAt != null ? DateFormat('E, MMM d').format(payment.paidAt!) : 'Pending',
|
||||
workedTime: '00:00 - 00:00', // TODO: Fetch from assignment
|
||||
hours: 0,
|
||||
rate: 0,
|
||||
status: payment.status.toString().split('.').last,
|
||||
title: payment.title,
|
||||
location: payment.location,
|
||||
address: payment.address,
|
||||
date: DateFormat('E, MMM d').format(payment.date),
|
||||
workedTime: payment.workedTime,
|
||||
hours: payment.hours,
|
||||
rate: payment.rate,
|
||||
status: payment.status,
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
|
||||
@@ -32,4 +32,5 @@ dependencies:
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
flutter_lints: ^3.0.0
|
||||
flutter_lints: ^6.0.0
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -2,9 +2,10 @@ name: staff_certificates
|
||||
description: Staff certificates feature
|
||||
version: 0.0.1
|
||||
publish_to: none
|
||||
resolution: workspace
|
||||
|
||||
environment:
|
||||
sdk: '>=3.0.0 <4.0.0'
|
||||
sdk: '>=3.10.0 <4.0.0'
|
||||
|
||||
dependencies:
|
||||
flutter:
|
||||
|
||||
@@ -1,778 +0,0 @@
|
||||
# Generated by pub
|
||||
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||
packages:
|
||||
_flutterfire_internals:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: _flutterfire_internals
|
||||
sha256: cd83f7d6bd4e4c0b0b4fef802e8796784032e1cc23d7b0e982cf5d05d9bbe182
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.66"
|
||||
archive:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: archive
|
||||
sha256: cb6a278ef2dbb298455e1a713bda08524a175630ec643a242c399c932a0a1f7d
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.6.1"
|
||||
args:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: args
|
||||
sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.7.0"
|
||||
async:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: async
|
||||
sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.13.0"
|
||||
auto_injector:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: auto_injector
|
||||
sha256: "1fc2624898e92485122eb2b1698dd42511d7ff6574f84a3a8606fc4549a1e8f8"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
bloc:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: bloc
|
||||
sha256: "106842ad6569f0b60297619e9e0b1885c2fb9bf84812935490e6c5275777804e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "8.1.4"
|
||||
boolean_selector:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: boolean_selector
|
||||
sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
characters:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: characters
|
||||
sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
clock:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: clock
|
||||
sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.2"
|
||||
code_assets:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: code_assets
|
||||
sha256: "83ccdaa064c980b5596c35dd64a8d3ecc68620174ab9b90b6343b753aa721687"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: collection
|
||||
sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.19.1"
|
||||
core_localization:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "../../../../../core_localization"
|
||||
relative: true
|
||||
source: path
|
||||
version: "0.0.1"
|
||||
crypto:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: crypto
|
||||
sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.7"
|
||||
csv:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: csv
|
||||
sha256: c6aa2679b2a18cb57652920f674488d89712efaf4d3fdf2e537215b35fc19d6c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.0.0"
|
||||
design_system:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "../../../../../design_system"
|
||||
relative: true
|
||||
source: path
|
||||
version: "0.0.1"
|
||||
equatable:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: equatable
|
||||
sha256: "3e0141505477fd8ad55d6eb4e7776d3fe8430be8e497ccb1521370c3f21a3e2b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.8"
|
||||
fake_async:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: fake_async
|
||||
sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.3"
|
||||
ffi:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: ffi
|
||||
sha256: d07d37192dbf97461359c1518788f203b0c9102cfd2c35a716b823741219542c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.5"
|
||||
file:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: file
|
||||
sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.0.1"
|
||||
firebase_app_check:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: firebase_app_check
|
||||
sha256: "45f0d279ea7ae4eac1867a4c85aa225761e3ac0ccf646386a860b2bc16581f76"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.4.1+4"
|
||||
firebase_app_check_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: firebase_app_check_platform_interface
|
||||
sha256: e32b4e6adeaac207a6f7afe0906d97c0811de42fb200d9b6317a09155de65e2b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.1+4"
|
||||
firebase_app_check_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: firebase_app_check_web
|
||||
sha256: "2cbc8a18a34813a7e31d7b30f989973087421cd5d0e397b4dd88a90289aa2bed"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.2+2"
|
||||
firebase_auth:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: firebase_auth
|
||||
sha256: b20d1540460814c5984474c1e9dd833bdbcff6ecd8d6ad86cc9da8cfd581c172
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.1.4"
|
||||
firebase_auth_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: firebase_auth_platform_interface
|
||||
sha256: fd0225320b6bbc92460c86352d16b60aea15f9ef88292774cca97b0522ea9f72
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "8.1.6"
|
||||
firebase_auth_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: firebase_auth_web
|
||||
sha256: be7dccb263b89fbda2a564de9d8193118196e8481ffb937222a025cdfdf82c40
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.1.2"
|
||||
firebase_core:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: firebase_core
|
||||
sha256: "923085c881663ef685269b013e241b428e1fb03cdd0ebde265d9b40ff18abf80"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.4.0"
|
||||
firebase_core_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: firebase_core_platform_interface
|
||||
sha256: cccb4f572325dc14904c02fcc7db6323ad62ba02536833dddb5c02cac7341c64
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.0.2"
|
||||
firebase_core_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: firebase_core_web
|
||||
sha256: "83e7356c704131ca4d8d8dd57e360d8acecbca38b1a3705c7ae46cc34c708084"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.4.0"
|
||||
firebase_data_connect:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: firebase_data_connect
|
||||
sha256: "01d0f8e33c520a6e6f59cf5ac6ff281d1927f7837f094fa8eb5fdb0b1b328ad8"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.2+2"
|
||||
fixnum:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: fixnum
|
||||
sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
flutter:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_bloc:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_bloc
|
||||
sha256: b594505eac31a0518bdcb4b5b79573b8d9117b193cc80cc12e17d639b10aa27a
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "8.1.6"
|
||||
flutter_localizations:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_modular:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_modular
|
||||
sha256: "33a63d9fe61429d12b3dfa04795ed890f17d179d3d38e988ba7969651fcd5586"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.4.1"
|
||||
flutter_test:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_web_plugins:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
font_awesome_flutter:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: font_awesome_flutter
|
||||
sha256: b9011df3a1fa02993630b8fb83526368cf2206a711259830325bab2f1d2a4eb0
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "10.12.0"
|
||||
glob:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: glob
|
||||
sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.3"
|
||||
google_fonts:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: google_fonts
|
||||
sha256: "6996212014b996eaa17074e02b1b925b212f5e053832d9048970dc27255a8fb3"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.1.0"
|
||||
google_identity_services_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: google_identity_services_web
|
||||
sha256: "5d187c46dc59e02646e10fe82665fc3884a9b71bc1c90c2b8b749316d33ee454"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.3.3+1"
|
||||
googleapis_auth:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: googleapis_auth
|
||||
sha256: befd71383a955535060acde8792e7efc11d2fccd03dd1d3ec434e85b68775938
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.6.0"
|
||||
grpc:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: grpc
|
||||
sha256: e93ee3bce45c134bf44e9728119102358c7cd69de7832d9a874e2e74eb8cab40
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.2.4"
|
||||
hooks:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: hooks
|
||||
sha256: "5d309c86e7ce34cd8e37aa71cb30cb652d3829b900ab145e4d9da564b31d59f7"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
http:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http
|
||||
sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.6.0"
|
||||
http2:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http2
|
||||
sha256: "382d3aefc5bd6dc68c6b892d7664f29b5beb3251611ae946a98d35158a82bbfa"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.1"
|
||||
http_parser:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http_parser
|
||||
sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.1.2"
|
||||
intl:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: intl
|
||||
sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.20.2"
|
||||
krow_core:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "../../../../../core"
|
||||
relative: true
|
||||
source: path
|
||||
version: "0.0.1"
|
||||
krow_data_connect:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "../../../../../data_connect"
|
||||
relative: true
|
||||
source: path
|
||||
version: "0.0.1"
|
||||
krow_domain:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "../../../../../domain"
|
||||
relative: true
|
||||
source: path
|
||||
version: "0.0.1"
|
||||
leak_tracker:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker
|
||||
sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "11.0.2"
|
||||
leak_tracker_flutter_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_flutter_testing
|
||||
sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.10"
|
||||
leak_tracker_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_testing
|
||||
sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.2"
|
||||
logging:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: logging
|
||||
sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
lucide_icons:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: lucide_icons
|
||||
sha256: ad24d0fd65707e48add30bebada7d90bff2a1bba0a72d6e9b19d44246b0e83c4
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.257.0"
|
||||
matcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: matcher
|
||||
sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.12.17"
|
||||
material_color_utilities:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: material_color_utilities
|
||||
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.11.1"
|
||||
meta:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: meta
|
||||
sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.17.0"
|
||||
modular_core:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: modular_core
|
||||
sha256: "1db0420a0dfb8a2c6dca846e7cbaa4ffeb778e247916dbcb27fb25aa566e5436"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.4.1"
|
||||
native_toolchain_c:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: native_toolchain_c
|
||||
sha256: "89e83885ba09da5fdf2cdacc8002a712ca238c28b7f717910b34bcd27b0d03ac"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.17.4"
|
||||
nested:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: nested
|
||||
sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
objective_c:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: objective_c
|
||||
sha256: "7fd0c4d8ac8980011753b9bdaed2bf15111365924cdeeeaeb596214ea2b03537"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "9.2.4"
|
||||
path:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path
|
||||
sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.9.1"
|
||||
path_provider:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider
|
||||
sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.5"
|
||||
path_provider_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_android
|
||||
sha256: f2c65e21139ce2c3dad46922be8272bb5963516045659e71bb16e151c93b580e
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.22"
|
||||
path_provider_foundation:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_foundation
|
||||
sha256: "2a376b7d6392d80cd3705782d2caa734ca4727776db0b6ec36ef3f1855197699"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.6.0"
|
||||
path_provider_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_linux
|
||||
sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.1"
|
||||
path_provider_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_platform_interface
|
||||
sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
path_provider_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_windows
|
||||
sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.0"
|
||||
platform:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: platform
|
||||
sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.6"
|
||||
plugin_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: plugin_platform_interface
|
||||
sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.8"
|
||||
protobuf:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: protobuf
|
||||
sha256: "68645b24e0716782e58948f8467fd42a880f255096a821f9e7d0ec625b00c84d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.0"
|
||||
provider:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: provider
|
||||
sha256: "4e82183fa20e5ca25703ead7e05de9e4cceed1fbd1eadc1ac3cb6f565a09f272"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.1.5+1"
|
||||
pub_semver:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pub_semver
|
||||
sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.0"
|
||||
result_dart:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: result_dart
|
||||
sha256: "0666b21fbdf697b3bdd9986348a380aa204b3ebe7c146d8e4cdaa7ce735e6054"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
shared_preferences:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences
|
||||
sha256: "2939ae520c9024cb197fc20dee269cd8cdbf564c8b5746374ec6cacdc5169e64"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.5.4"
|
||||
shared_preferences_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_android
|
||||
sha256: "83af5c682796c0f7719c2bbf74792d113e40ae97981b8f266fa84574573556bc"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.18"
|
||||
shared_preferences_foundation:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_foundation
|
||||
sha256: "4e7eaffc2b17ba398759f1151415869a34771ba11ebbccd1b0145472a619a64f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.5.6"
|
||||
shared_preferences_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_linux
|
||||
sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.1"
|
||||
shared_preferences_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_platform_interface
|
||||
sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.1"
|
||||
shared_preferences_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_web
|
||||
sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.3"
|
||||
shared_preferences_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_windows
|
||||
sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.1"
|
||||
sky_engine:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
slang:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: slang
|
||||
sha256: "13e3b6f07adc51ab751e7889647774d294cbce7a3382f81d9e5029acfe9c37b2"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.12.0"
|
||||
slang_flutter:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: slang_flutter
|
||||
sha256: "0a4545cca5404d6b7487cf61cf1fe56c52daeb08de56a7574ee8381fbad035a0"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.12.0"
|
||||
source_span:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_span
|
||||
sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.10.1"
|
||||
stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stack_trace
|
||||
sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.12.1"
|
||||
stream_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stream_channel
|
||||
sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
string_scanner:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: string_scanner
|
||||
sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.1"
|
||||
term_glyph:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: term_glyph
|
||||
sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.2"
|
||||
test_api:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.7"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: typed_data
|
||||
sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
uuid:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: uuid
|
||||
sha256: a11b666489b1954e01d992f3d601b1804a33937b5a8fe677bd26b8a9f96f96e8
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.5.2"
|
||||
vector_math:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vector_math
|
||||
sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.0"
|
||||
vm_service:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vm_service
|
||||
sha256: "45caa6c5917fa127b5dbcfbd1fa60b14e583afdc08bfc96dda38886ca252eb60"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "15.0.2"
|
||||
watcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: watcher
|
||||
sha256: "1398c9f081a753f9226febe8900fce8f7d0a67163334e1c94a2438339d79d635"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.1"
|
||||
web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: web
|
||||
sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
xdg_directories:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: xdg_directories
|
||||
sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
yaml:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: yaml
|
||||
sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.3"
|
||||
sdks:
|
||||
dart: ">=3.10.7 <4.0.0"
|
||||
flutter: ">=3.38.4"
|
||||
@@ -2,6 +2,7 @@ name: staff_documents
|
||||
description: Staff Documents feature.
|
||||
version: 0.0.1
|
||||
publish_to: none
|
||||
resolution: workspace
|
||||
|
||||
environment:
|
||||
sdk: '>=3.10.0 <4.0.0'
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
import 'package:firebase_data_connect/firebase_data_connect.dart';
|
||||
import 'package:krow_data_connect/krow_data_connect.dart' as dc;
|
||||
import 'package:krow_domain/krow_domain.dart';
|
||||
|
||||
class TaxFormMapper {
|
||||
static TaxForm fromDataConnect(dc.GetTaxFormsByStaffIdTaxForms form) {
|
||||
// Construct the legacy map for the entity
|
||||
final Map<String, dynamic> formData = {
|
||||
'firstName': form.firstName,
|
||||
'lastName': form.lastName,
|
||||
'middleInitial': form.mInitial,
|
||||
'otherLastNames': form.oLastName,
|
||||
'dob': _formatDate(form.dob),
|
||||
'ssn': form.socialSN.toString(),
|
||||
'email': form.email,
|
||||
'phone': form.phone,
|
||||
'address': form.address,
|
||||
'aptNumber': form.apt,
|
||||
'city': form.city,
|
||||
'state': form.state,
|
||||
'zipCode': form.zipCode,
|
||||
|
||||
// I-9 Fields
|
||||
'citizenshipStatus': form.citizen?.stringValue,
|
||||
'uscisNumber': form.uscis,
|
||||
'passportNumber': form.passportNumber,
|
||||
'countryIssuance': form.countryIssue,
|
||||
'preparerUsed': form.prepartorOrTranslator,
|
||||
|
||||
// W-4 Fields
|
||||
'filingStatus': form.marital?.stringValue,
|
||||
'multipleJobs': form.multipleJob,
|
||||
'qualifyingChildren': form.childrens,
|
||||
'otherDependents': form.otherDeps,
|
||||
'otherIncome': form.otherInconme?.toString(),
|
||||
'deductions': form.deductions?.toString(),
|
||||
'extraWithholding': form.extraWithholding?.toString(),
|
||||
|
||||
'signature': form.signature,
|
||||
};
|
||||
|
||||
String title = '';
|
||||
String subtitle = '';
|
||||
String description = '';
|
||||
|
||||
if (form.formType == dc.TaxFormType.I9) {
|
||||
title = 'Form I-9';
|
||||
subtitle = 'Employment Eligibility Verification';
|
||||
description = 'Required for all new hires to verify identity.';
|
||||
} else {
|
||||
title = 'Form W-4';
|
||||
subtitle = 'Employee\'s Withholding Certificate';
|
||||
description = 'Determines federal income tax withholding.';
|
||||
}
|
||||
|
||||
return TaxFormAdapter.fromPrimitives(
|
||||
id: form.id,
|
||||
type: form.formType.stringValue,
|
||||
title: title,
|
||||
subtitle: subtitle,
|
||||
description: description,
|
||||
status: form.status.stringValue,
|
||||
staffId: form.staffId,
|
||||
formData: formData,
|
||||
updatedAt: form.updatedAt?.toDateTime(),
|
||||
);
|
||||
}
|
||||
|
||||
static String? _formatDate(Timestamp? timestamp) {
|
||||
if (timestamp == null) return null;
|
||||
|
||||
final DateTime date = timestamp.toDateTime();
|
||||
|
||||
return '${date.month.toString().padLeft(2, '0')}/${date.day.toString().padLeft(2, '0')}/${date.year}';
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import 'package:krow_data_connect/krow_data_connect.dart' as dc;
|
||||
import 'package:krow_domain/krow_domain.dart';
|
||||
|
||||
import '../../domain/repositories/tax_forms_repository.dart';
|
||||
import '../mappers/tax_form_mapper.dart';
|
||||
|
||||
class TaxFormsRepositoryImpl implements TaxFormsRepository {
|
||||
TaxFormsRepositoryImpl({
|
||||
@@ -33,11 +34,11 @@ class TaxFormsRepositoryImpl implements TaxFormsRepository {
|
||||
@override
|
||||
Future<List<TaxForm>> getTaxForms() async {
|
||||
final String staffId = _getStaffId();
|
||||
final QueryResult<dc.GetTaxFormsBystaffIdData, dc.GetTaxFormsBystaffIdVariables>
|
||||
final QueryResult<dc.GetTaxFormsByStaffIdData, dc.GetTaxFormsByStaffIdVariables>
|
||||
result =
|
||||
await dataConnect.getTaxFormsBystaffId(staffId: staffId).execute();
|
||||
await dataConnect.getTaxFormsByStaffId(staffId: staffId).execute();
|
||||
|
||||
final List<TaxForm> forms = result.data.taxForms.map((dc.GetTaxFormsBystaffIdTaxForms e) => _mapToEntity(e)).toList();
|
||||
final List<TaxForm> forms = result.data.taxForms.map(TaxFormMapper.fromDataConnect).toList();
|
||||
|
||||
// Check if required forms exist, create if not.
|
||||
final Set<TaxFormType> typesPresent = forms.map((TaxForm f) => f.type).toSet();
|
||||
@@ -53,98 +54,161 @@ class TaxFormsRepositoryImpl implements TaxFormsRepository {
|
||||
}
|
||||
|
||||
if (createdNew) {
|
||||
final QueryResult<dc.GetTaxFormsBystaffIdData, dc.GetTaxFormsBystaffIdVariables>
|
||||
final QueryResult<dc.GetTaxFormsByStaffIdData, dc.GetTaxFormsByStaffIdVariables>
|
||||
result2 =
|
||||
await dataConnect.getTaxFormsBystaffId(staffId: staffId).execute();
|
||||
return result2.data.taxForms.map((dc.GetTaxFormsBystaffIdTaxForms e) => _mapToEntity(e)).toList();
|
||||
await dataConnect.getTaxFormsByStaffId(staffId: staffId).execute();
|
||||
return result2.data.taxForms.map(TaxFormMapper.fromDataConnect).toList();
|
||||
}
|
||||
|
||||
return forms;
|
||||
}
|
||||
|
||||
Future<void> _createInitialForm(String staffId, TaxFormType type) async {
|
||||
String title = '';
|
||||
String subtitle = '';
|
||||
String description = '';
|
||||
|
||||
if (type == TaxFormType.i9) {
|
||||
title = 'Form I-9';
|
||||
subtitle = 'Employment Eligibility Verification';
|
||||
description = 'Required for all new hires to verify identity.';
|
||||
} else {
|
||||
title = 'Form W-4';
|
||||
subtitle = 'Employee\'s Withholding Certificate';
|
||||
description = 'Determines federal income tax withholding.';
|
||||
}
|
||||
|
||||
await dataConnect
|
||||
.createTaxForm(
|
||||
staffId: staffId,
|
||||
formType: dc.TaxFormType.values.byName(TaxFormAdapter.typeToString(type)),
|
||||
title: title,
|
||||
formType:
|
||||
dc.TaxFormType.values.byName(TaxFormAdapter.typeToString(type)),
|
||||
firstName: '',
|
||||
lastName: '',
|
||||
socialSN: 0,
|
||||
address: '',
|
||||
status: dc.TaxFormStatus.NOT_STARTED,
|
||||
)
|
||||
.subtitle(subtitle)
|
||||
.description(description)
|
||||
.status(dc.TaxFormStatus.NOT_STARTED)
|
||||
.execute();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> submitForm(TaxFormType type, Map<String, dynamic> data) async {
|
||||
final String staffId = _getStaffId();
|
||||
final QueryResult<dc.GetTaxFormsBystaffIdData, dc.GetTaxFormsBystaffIdVariables>
|
||||
result =
|
||||
await dataConnect.getTaxFormsBystaffId(staffId: staffId).execute();
|
||||
final String targetTypeString = TaxFormAdapter.typeToString(type);
|
||||
|
||||
final dc.GetTaxFormsBystaffIdTaxForms form = result.data.taxForms.firstWhere(
|
||||
(dc.GetTaxFormsBystaffIdTaxForms e) => e.formType.stringValue == targetTypeString,
|
||||
orElse: () => throw Exception('Form not found for submission'),
|
||||
);
|
||||
|
||||
// AnyValue expects a scalar, list, or map.
|
||||
await dataConnect
|
||||
.updateTaxForm(
|
||||
id: form.id,
|
||||
)
|
||||
.formData(AnyValue.fromJson(data))
|
||||
.status(dc.TaxFormStatus.SUBMITTED)
|
||||
.execute();
|
||||
Future<void> updateI9Form(I9TaxForm form) async {
|
||||
final Map<String, dynamic> data = form.formData;
|
||||
final dc.UpdateTaxFormVariablesBuilder builder = dataConnect.updateTaxForm(id: form.id);
|
||||
_mapCommonFields(builder, data);
|
||||
_mapI9Fields(builder, data);
|
||||
await builder.execute();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> updateFormStatus(TaxFormType type, TaxFormStatus status) async {
|
||||
final String staffId = _getStaffId();
|
||||
final QueryResult<dc.GetTaxFormsBystaffIdData, dc.GetTaxFormsBystaffIdVariables>
|
||||
result =
|
||||
await dataConnect.getTaxFormsBystaffId(staffId: staffId).execute();
|
||||
final String targetTypeString = TaxFormAdapter.typeToString(type);
|
||||
|
||||
final dc.GetTaxFormsBystaffIdTaxForms form = result.data.taxForms.firstWhere(
|
||||
(dc.GetTaxFormsBystaffIdTaxForms e) => e.formType.stringValue == targetTypeString,
|
||||
orElse: () => throw Exception('Form not found for update'),
|
||||
);
|
||||
|
||||
await dataConnect
|
||||
.updateTaxForm(
|
||||
id: form.id,
|
||||
)
|
||||
.status(dc.TaxFormStatus.values.byName(TaxFormAdapter.statusToString(status)))
|
||||
.execute();
|
||||
Future<void> submitI9Form(I9TaxForm form) async {
|
||||
final Map<String, dynamic> data = form.formData;
|
||||
final dc.UpdateTaxFormVariablesBuilder builder = dataConnect.updateTaxForm(id: form.id);
|
||||
_mapCommonFields(builder, data);
|
||||
_mapI9Fields(builder, data);
|
||||
await builder.status(dc.TaxFormStatus.SUBMITTED).execute();
|
||||
}
|
||||
|
||||
TaxForm _mapToEntity(dc.GetTaxFormsBystaffIdTaxForms form) {
|
||||
return TaxFormAdapter.fromPrimitives(
|
||||
id: form.id,
|
||||
type: form.formType.stringValue,
|
||||
title: form.title,
|
||||
subtitle: form.subtitle,
|
||||
description: form.description,
|
||||
status: form.status.stringValue,
|
||||
staffId: form.staffId,
|
||||
formData: form.formData, // Adapter expects dynamic
|
||||
updatedAt: form.updatedAt?.toDateTime(),
|
||||
);
|
||||
@override
|
||||
Future<void> updateW4Form(W4TaxForm form) async {
|
||||
final Map<String, dynamic> data = form.formData;
|
||||
final dc.UpdateTaxFormVariablesBuilder builder = dataConnect.updateTaxForm(id: form.id);
|
||||
_mapCommonFields(builder, data);
|
||||
_mapW4Fields(builder, data);
|
||||
await builder.execute();
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> submitW4Form(W4TaxForm form) async {
|
||||
final Map<String, dynamic> data = form.formData;
|
||||
final dc.UpdateTaxFormVariablesBuilder builder = dataConnect.updateTaxForm(id: form.id);
|
||||
_mapCommonFields(builder, data);
|
||||
_mapW4Fields(builder, data);
|
||||
await builder.status(dc.TaxFormStatus.SUBMITTED).execute();
|
||||
}
|
||||
|
||||
void _mapCommonFields(dc.UpdateTaxFormVariablesBuilder builder, Map<String, dynamic> data) {
|
||||
if (data.containsKey('firstName')) builder.firstName(data['firstName'] as String?);
|
||||
if (data.containsKey('lastName')) builder.lastName(data['lastName'] as String?);
|
||||
if (data.containsKey('middleInitial')) builder.mInitial(data['middleInitial'] as String?);
|
||||
if (data.containsKey('otherLastNames')) builder.oLastName(data['otherLastNames'] as String?);
|
||||
if (data.containsKey('dob')) {
|
||||
final String dob = data['dob'] as String;
|
||||
// Handle both ISO string and MM/dd/yyyy manual entry
|
||||
DateTime? date;
|
||||
try {
|
||||
date = DateTime.parse(dob);
|
||||
} catch (_) {
|
||||
try {
|
||||
// Fallback minimal parse for mm/dd/yyyy
|
||||
final List<String> parts = dob.split('/');
|
||||
if (parts.length == 3) {
|
||||
date = DateTime(
|
||||
int.parse(parts[2]),
|
||||
int.parse(parts[0]),
|
||||
int.parse(parts[1]),
|
||||
);
|
||||
}
|
||||
} catch (_) {}
|
||||
}
|
||||
if (date != null) {
|
||||
final int ms = date.millisecondsSinceEpoch;
|
||||
final int seconds = (ms / 1000).floor();
|
||||
builder.dob(Timestamp(0, seconds));
|
||||
}
|
||||
}
|
||||
if (data.containsKey('ssn') && data['ssn']?.toString().isNotEmpty == true) {
|
||||
builder.socialSN(int.tryParse(data['ssn'].toString().replaceAll(RegExp(r'\D'), '')) ?? 0);
|
||||
}
|
||||
if (data.containsKey('email')) builder.email(data['email'] as String?);
|
||||
if (data.containsKey('phone')) builder.phone(data['phone'] as String?);
|
||||
if (data.containsKey('address')) builder.address(data['address'] as String?);
|
||||
if (data.containsKey('aptNumber')) builder.apt(data['aptNumber'] as String?);
|
||||
if (data.containsKey('city')) builder.city(data['city'] as String?);
|
||||
if (data.containsKey('state')) builder.state(data['state'] as String?);
|
||||
if (data.containsKey('zipCode')) builder.zipCode(data['zipCode'] as String?);
|
||||
}
|
||||
|
||||
void _mapI9Fields(dc.UpdateTaxFormVariablesBuilder builder, Map<String, dynamic> data) {
|
||||
if (data.containsKey('citizenshipStatus')) {
|
||||
final String status = data['citizenshipStatus'] as String;
|
||||
// Map string to enum if possible, or handle otherwise.
|
||||
// Generated enum: CITIZEN, NONCITIZEN_NATIONAL, PERMANENT_RESIDENT, ALIEN_AUTHORIZED
|
||||
try {
|
||||
builder.citizen(dc.CitizenshipStatus.values.byName(status.toUpperCase()));
|
||||
} catch (_) {}
|
||||
}
|
||||
if (data.containsKey('uscisNumber')) builder.uscis(data['uscisNumber'] as String?);
|
||||
if (data.containsKey('passportNumber')) builder.passportNumber(data['passportNumber'] as String?);
|
||||
if (data.containsKey('countryIssuance')) builder.countryIssue(data['countryIssuance'] as String?);
|
||||
if (data.containsKey('preparerUsed')) builder.prepartorOrTranslator(data['preparerUsed'] as bool?);
|
||||
if (data.containsKey('signature')) builder.signature(data['signature'] as String?);
|
||||
// Note: admissionNumber not in builder based on file read
|
||||
}
|
||||
|
||||
void _mapW4Fields(dc.UpdateTaxFormVariablesBuilder builder, Map<String, dynamic> data) {
|
||||
if (data.containsKey('cityStateZip')) {
|
||||
final String csz = data['cityStateZip'] as String;
|
||||
// Extremely basic split: City, State Zip
|
||||
final List<String> parts = csz.split(',');
|
||||
if (parts.length >= 2) {
|
||||
builder.city(parts[0].trim());
|
||||
final String stateZip = parts[1].trim();
|
||||
final List<String> szParts = stateZip.split(' ');
|
||||
if (szParts.isNotEmpty) builder.state(szParts[0]);
|
||||
if (szParts.length > 1) builder.zipCode(szParts.last);
|
||||
}
|
||||
}
|
||||
if (data.containsKey('filingStatus')) {
|
||||
// MARITIAL_STATUS_SINGLE, MARITIAL_STATUS_MARRIED, MARITIAL_STATUS_HEAD
|
||||
try {
|
||||
final String status = data['filingStatus'] as String;
|
||||
// Simple mapping assumptions:
|
||||
if (status.contains('single')) builder.marital(dc.MaritalStatus.SINGLE);
|
||||
else if (status.contains('married')) builder.marital(dc.MaritalStatus.MARRIED);
|
||||
else if (status.contains('head')) builder.marital(dc.MaritalStatus.HEAD);
|
||||
} catch (_) {}
|
||||
}
|
||||
if (data.containsKey('multipleJobs')) builder.multipleJob(data['multipleJobs'] as bool?);
|
||||
if (data.containsKey('qualifyingChildren')) builder.childrens(data['qualifyingChildren'] as int?);
|
||||
if (data.containsKey('otherDependents')) builder.otherDeps(data['otherDependents'] as int?);
|
||||
if (data.containsKey('otherIncome')) {
|
||||
builder.otherInconme(double.tryParse(data['otherIncome'].toString()));
|
||||
}
|
||||
if (data.containsKey('deductions')) {
|
||||
builder.deductions(double.tryParse(data['deductions'].toString()));
|
||||
}
|
||||
if (data.containsKey('extraWithholding')) {
|
||||
builder.extraWithholding(double.tryParse(data['extraWithholding'].toString()));
|
||||
}
|
||||
if (data.containsKey('signature')) builder.signature(data['signature'] as String?);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
import 'package:equatable/equatable.dart';
|
||||
|
||||
enum TaxFormType { i9, w4 }
|
||||
|
||||
enum TaxFormStatus { notStarted, inProgress, submitted, approved, rejected }
|
||||
|
||||
class TaxFormEntity extends Equatable {
|
||||
final TaxFormType type;
|
||||
final String title;
|
||||
final String subtitle;
|
||||
final String description;
|
||||
final TaxFormStatus status;
|
||||
final DateTime? lastUpdated;
|
||||
|
||||
const TaxFormEntity({
|
||||
required this.type,
|
||||
required this.title,
|
||||
required this.subtitle,
|
||||
required this.description,
|
||||
this.status = TaxFormStatus.notStarted,
|
||||
this.lastUpdated,
|
||||
});
|
||||
|
||||
@override
|
||||
List<Object?> get props => [type, title, subtitle, description, status, lastUpdated];
|
||||
}
|
||||
@@ -2,6 +2,8 @@ import 'package:krow_domain/krow_domain.dart';
|
||||
|
||||
abstract class TaxFormsRepository {
|
||||
Future<List<TaxForm>> getTaxForms();
|
||||
Future<void> submitForm(TaxFormType type, Map<String, dynamic> data);
|
||||
Future<void> updateFormStatus(TaxFormType type, TaxFormStatus status);
|
||||
Future<void> updateI9Form(I9TaxForm form);
|
||||
Future<void> submitI9Form(I9TaxForm form);
|
||||
Future<void> updateW4Form(W4TaxForm form);
|
||||
Future<void> submitW4Form(W4TaxForm form);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
import 'package:krow_domain/krow_domain.dart';
|
||||
import '../repositories/tax_forms_repository.dart';
|
||||
|
||||
class SaveI9FormUseCase {
|
||||
final TaxFormsRepository _repository;
|
||||
|
||||
SaveI9FormUseCase(this._repository);
|
||||
|
||||
Future<void> call(I9TaxForm form) async {
|
||||
return _repository.updateI9Form(form);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import 'package:krow_domain/krow_domain.dart';
|
||||
import '../repositories/tax_forms_repository.dart';
|
||||
|
||||
class SaveW4FormUseCase {
|
||||
final TaxFormsRepository _repository;
|
||||
|
||||
SaveW4FormUseCase(this._repository);
|
||||
|
||||
Future<void> call(W4TaxForm form) async {
|
||||
return _repository.updateW4Form(form);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import 'package:krow_domain/krow_domain.dart';
|
||||
import '../repositories/tax_forms_repository.dart';
|
||||
|
||||
class SubmitI9FormUseCase {
|
||||
final TaxFormsRepository _repository;
|
||||
|
||||
SubmitI9FormUseCase(this._repository);
|
||||
|
||||
Future<void> call(I9TaxForm form) async {
|
||||
return _repository.submitI9Form(form);
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
import 'package:krow_domain/krow_domain.dart';
|
||||
import '../repositories/tax_forms_repository.dart';
|
||||
|
||||
class SubmitTaxFormUseCase {
|
||||
final TaxFormsRepository _repository;
|
||||
|
||||
SubmitTaxFormUseCase(this._repository);
|
||||
|
||||
Future<void> call(TaxFormType type, Map<String, dynamic> data) async {
|
||||
return _repository.submitForm(type, data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import 'package:krow_domain/krow_domain.dart';
|
||||
import '../repositories/tax_forms_repository.dart';
|
||||
|
||||
class SubmitW4FormUseCase {
|
||||
final TaxFormsRepository _repository;
|
||||
|
||||
SubmitW4FormUseCase(this._repository);
|
||||
|
||||
Future<void> call(W4TaxForm form) async {
|
||||
return _repository.submitW4Form(form);
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,47 @@
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:krow_domain/krow_domain.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
||||
import '../../../domain/usecases/submit_tax_form_usecase.dart';
|
||||
import '../../../domain/usecases/submit_i9_form_usecase.dart';
|
||||
import 'form_i9_state.dart';
|
||||
|
||||
class FormI9Cubit extends Cubit<FormI9State> {
|
||||
final SubmitTaxFormUseCase _submitTaxFormUseCase;
|
||||
final SubmitI9FormUseCase _submitI9FormUseCase;
|
||||
String _formId = '';
|
||||
|
||||
FormI9Cubit(this._submitTaxFormUseCase) : super(const FormI9State());
|
||||
FormI9Cubit(this._submitI9FormUseCase) : super(const FormI9State());
|
||||
|
||||
void initialize(TaxForm? form) {
|
||||
if (form == null || form.formData.isEmpty) {
|
||||
emit(const FormI9State()); // Reset to empty if no form
|
||||
return;
|
||||
}
|
||||
|
||||
final Map<String, dynamic> data = form.formData;
|
||||
_formId = form.id;
|
||||
emit(FormI9State(
|
||||
firstName: data['firstName'] as String? ?? '',
|
||||
lastName: data['lastName'] as String? ?? '',
|
||||
middleInitial: data['middleInitial'] as String? ?? '',
|
||||
otherLastNames: data['otherLastNames'] as String? ?? '',
|
||||
dob: data['dob'] as String? ?? '',
|
||||
ssn: data['ssn'] as String? ?? '',
|
||||
email: data['email'] as String? ?? '',
|
||||
phone: data['phone'] as String? ?? '',
|
||||
address: data['address'] as String? ?? '',
|
||||
aptNumber: data['aptNumber'] as String? ?? '',
|
||||
city: data['city'] as String? ?? '',
|
||||
state: data['state'] as String? ?? '',
|
||||
zipCode: data['zipCode'] as String? ?? '',
|
||||
citizenshipStatus: data['citizenshipStatus'] as String? ?? '',
|
||||
uscisNumber: data['uscisNumber'] as String? ?? '',
|
||||
admissionNumber: data['admissionNumber'] as String? ?? '',
|
||||
passportNumber: data['passportNumber'] as String? ?? '',
|
||||
countryIssuance: data['countryIssuance'] as String? ?? '',
|
||||
preparerUsed: data['preparerUsed'] as bool? ?? false,
|
||||
signature: data['signature'] as String? ?? '',
|
||||
));
|
||||
}
|
||||
|
||||
void nextStep(int totalSteps) {
|
||||
if (state.currentStep < totalSteps - 1) {
|
||||
@@ -52,18 +86,36 @@ class FormI9Cubit extends Cubit<FormI9State> {
|
||||
Future<void> submit() async {
|
||||
emit(state.copyWith(status: FormI9Status.submitting));
|
||||
try {
|
||||
await _submitTaxFormUseCase(
|
||||
TaxFormType.i9,
|
||||
<String, dynamic>{
|
||||
'firstName': state.firstName,
|
||||
'lastName': state.lastName,
|
||||
'middleInitial': state.middleInitial,
|
||||
'citizenshipStatus': state.citizenshipStatus,
|
||||
'ssn': state.ssn,
|
||||
'signature': state.signature,
|
||||
// ... add other fields as needed for backend
|
||||
},
|
||||
final Map<String, dynamic> formData = {
|
||||
'firstName': state.firstName,
|
||||
'lastName': state.lastName,
|
||||
'middleInitial': state.middleInitial,
|
||||
'otherLastNames': state.otherLastNames,
|
||||
'dob': state.dob,
|
||||
'ssn': state.ssn,
|
||||
'email': state.email,
|
||||
'phone': state.phone,
|
||||
'address': state.address,
|
||||
'aptNumber': state.aptNumber,
|
||||
'city': state.city,
|
||||
'state': state.state,
|
||||
'zipCode': state.zipCode,
|
||||
'citizenshipStatus': state.citizenshipStatus,
|
||||
'uscisNumber': state.uscisNumber,
|
||||
'admissionNumber': state.admissionNumber,
|
||||
'passportNumber': state.passportNumber,
|
||||
'countryIssuance': state.countryIssuance,
|
||||
'preparerUsed': state.preparerUsed,
|
||||
'signature': state.signature,
|
||||
};
|
||||
|
||||
final I9TaxForm form = I9TaxForm(
|
||||
id: _formId.isNotEmpty ? _formId : const Uuid().v4(),
|
||||
title: 'Form I-9',
|
||||
formData: formData,
|
||||
);
|
||||
|
||||
await _submitI9FormUseCase(form);
|
||||
emit(state.copyWith(status: FormI9Status.success));
|
||||
} catch (e) {
|
||||
emit(state.copyWith(
|
||||
|
||||
@@ -1,13 +1,47 @@
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:krow_domain/krow_domain.dart';
|
||||
import 'package:uuid/uuid.dart';
|
||||
|
||||
import '../../../domain/usecases/submit_tax_form_usecase.dart';
|
||||
import '../../../domain/usecases/submit_w4_form_usecase.dart';
|
||||
import 'form_w4_state.dart';
|
||||
|
||||
class FormW4Cubit extends Cubit<FormW4State> {
|
||||
final SubmitTaxFormUseCase _submitTaxFormUseCase;
|
||||
final SubmitW4FormUseCase _submitW4FormUseCase;
|
||||
String _formId = '';
|
||||
|
||||
FormW4Cubit(this._submitTaxFormUseCase) : super(const FormW4State());
|
||||
FormW4Cubit(this._submitW4FormUseCase) : super(const FormW4State());
|
||||
|
||||
void initialize(TaxForm? form) {
|
||||
if (form == null || form.formData.isEmpty) {
|
||||
emit(const FormW4State()); // Reset
|
||||
return;
|
||||
}
|
||||
|
||||
final Map<String, dynamic> data = form.formData;
|
||||
_formId = form.id;
|
||||
|
||||
// Combine address parts if needed, or take existing
|
||||
final String city = data['city'] as String? ?? '';
|
||||
final String stateVal = data['state'] as String? ?? '';
|
||||
final String zip = data['zipCode'] as String? ?? '';
|
||||
final String cityStateZip = '$city, $stateVal $zip'.trim();
|
||||
|
||||
emit(FormW4State(
|
||||
firstName: data['firstName'] as String? ?? '',
|
||||
lastName: data['lastName'] as String? ?? '',
|
||||
ssn: data['ssn'] as String? ?? '',
|
||||
address: data['address'] as String? ?? '',
|
||||
cityStateZip: cityStateZip.contains(',') ? cityStateZip : '',
|
||||
filingStatus: data['filingStatus'] as String? ?? '',
|
||||
multipleJobs: data['multipleJobs'] as bool? ?? false,
|
||||
qualifyingChildren: data['qualifyingChildren'] as int? ?? 0,
|
||||
otherDependents: data['otherDependents'] as int? ?? 0,
|
||||
otherIncome: data['otherIncome'] as String? ?? '',
|
||||
deductions: data['deductions'] as String? ?? '',
|
||||
extraWithholding: data['extraWithholding'] as String? ?? '',
|
||||
signature: data['signature'] as String? ?? '',
|
||||
));
|
||||
}
|
||||
|
||||
void nextStep(int totalSteps) {
|
||||
if (state.currentStep < totalSteps - 1) {
|
||||
@@ -45,18 +79,29 @@ class FormW4Cubit extends Cubit<FormW4State> {
|
||||
Future<void> submit() async {
|
||||
emit(state.copyWith(status: FormW4Status.submitting));
|
||||
try {
|
||||
await _submitTaxFormUseCase(
|
||||
TaxFormType.w4,
|
||||
<String, dynamic>{
|
||||
'firstName': state.firstName,
|
||||
'lastName': state.lastName,
|
||||
'ssn': state.ssn,
|
||||
'filingStatus': state.filingStatus,
|
||||
'multipleJobs': state.multipleJobs,
|
||||
'signature': state.signature,
|
||||
// ... add other fields as needed
|
||||
},
|
||||
final Map<String, dynamic> formData = {
|
||||
'firstName': state.firstName,
|
||||
'lastName': state.lastName,
|
||||
'ssn': state.ssn,
|
||||
'address': state.address,
|
||||
'cityStateZip': state.cityStateZip, // Note: Repository should split this if needed.
|
||||
'filingStatus': state.filingStatus,
|
||||
'multipleJobs': state.multipleJobs,
|
||||
'qualifyingChildren': state.qualifyingChildren,
|
||||
'otherDependents': state.otherDependents,
|
||||
'otherIncome': state.otherIncome,
|
||||
'deductions': state.deductions,
|
||||
'extraWithholding': state.extraWithholding,
|
||||
'signature': state.signature,
|
||||
};
|
||||
|
||||
final W4TaxForm form = W4TaxForm(
|
||||
id: _formId.isNotEmpty ? _formId : const Uuid().v4(),
|
||||
title: 'Form W-4',
|
||||
formData: formData,
|
||||
);
|
||||
|
||||
await _submitW4FormUseCase(form);
|
||||
emit(state.copyWith(status: FormW4Status.success));
|
||||
} catch (e) {
|
||||
emit(state.copyWith(
|
||||
|
||||
@@ -2,12 +2,14 @@ import 'package:design_system/design_system.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_modular/flutter_modular.dart' hide ModularWatchExtension;
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:krow_domain/krow_domain.dart';
|
||||
|
||||
import '../blocs/i9/form_i9_cubit.dart';
|
||||
import '../blocs/i9/form_i9_state.dart';
|
||||
|
||||
class FormI9Page extends StatefulWidget {
|
||||
const FormI9Page({super.key});
|
||||
final TaxForm? form;
|
||||
const FormI9Page({super.key, this.form});
|
||||
|
||||
@override
|
||||
State<FormI9Page> createState() => _FormI9PageState();
|
||||
@@ -22,6 +24,16 @@ class _FormI9PageState extends State<FormI9Page> {
|
||||
'SD', 'TN', 'TX', 'UT', 'VT', 'VA', 'WA', 'WV', 'WI', 'WY'
|
||||
];
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
if (widget.form != null) {
|
||||
// Use post-frame callback or simple direct call since we are using Modular.get in build
|
||||
// But better helper:
|
||||
Modular.get<FormI9Cubit>().initialize(widget.form);
|
||||
}
|
||||
}
|
||||
|
||||
final List<Map<String, String>> _steps = <Map<String, String>>[
|
||||
<String, String>{'title': 'Personal Information', 'subtitle': 'Name and contact details'},
|
||||
<String, String>{'title': 'Address', 'subtitle': 'Your current address'},
|
||||
|
||||
@@ -2,18 +2,81 @@ import 'package:design_system/design_system.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_modular/flutter_modular.dart' hide ModularWatchExtension;
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:krow_domain/krow_domain.dart';
|
||||
|
||||
import '../blocs/w4/form_w4_cubit.dart';
|
||||
import '../blocs/w4/form_w4_state.dart';
|
||||
|
||||
class FormW4Page extends StatefulWidget {
|
||||
const FormW4Page({super.key});
|
||||
final TaxForm? form;
|
||||
const FormW4Page({super.key, this.form});
|
||||
|
||||
@override
|
||||
State<FormW4Page> createState() => _FormW4PageState();
|
||||
}
|
||||
|
||||
class _FormW4PageState extends State<FormW4Page> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
if (widget.form != null) {
|
||||
Modular.get<FormW4Cubit>().initialize(widget.form);
|
||||
}
|
||||
}
|
||||
|
||||
final List<String> _usStates = <String>[
|
||||
'Alabama',
|
||||
'Alaska',
|
||||
'Arizona',
|
||||
'Arkansas',
|
||||
'California',
|
||||
'Colorado',
|
||||
'Connecticut',
|
||||
'Delaware',
|
||||
'Florida',
|
||||
'Georgia',
|
||||
'Hawaii',
|
||||
'Idaho',
|
||||
'Illinois',
|
||||
'Indiana',
|
||||
'Iowa',
|
||||
'Kansas',
|
||||
'Kentucky',
|
||||
'Louisiana',
|
||||
'Maine',
|
||||
'Maryland',
|
||||
'Massachusetts',
|
||||
'Michigan',
|
||||
'Minnesota',
|
||||
'Mississippi',
|
||||
'Missouri',
|
||||
'Montana',
|
||||
'Nebraska',
|
||||
'Nevada',
|
||||
'New Hampshire',
|
||||
'New Jersey',
|
||||
'New Mexico',
|
||||
'New York',
|
||||
'North Carolina',
|
||||
'North Dakota',
|
||||
'Ohio',
|
||||
'Oklahoma',
|
||||
'Oregon',
|
||||
'Pennsylvania',
|
||||
'Rhode Island',
|
||||
'South Carolina',
|
||||
'South Dakota',
|
||||
'Tennessee',
|
||||
'Texas',
|
||||
'Utah',
|
||||
'Vermont',
|
||||
'Virginia',
|
||||
'Washington',
|
||||
'West Virginia',
|
||||
'Wisconsin',
|
||||
'Wyoming',
|
||||
];
|
||||
|
||||
final List<Map<String, String>> _steps = <Map<String, String>>[
|
||||
<String, String>{'title': 'Personal Information', 'subtitle': 'Step 1'},
|
||||
<String, String>{'title': 'Filing Status', 'subtitle': 'Step 1c'},
|
||||
|
||||
@@ -140,14 +140,14 @@ class TaxFormsPage extends StatelessWidget {
|
||||
|
||||
Widget _buildFormCard(TaxForm form) {
|
||||
// Helper to get icon based on type (could be in entity or a mapper)
|
||||
final String icon = form.type == TaxFormType.i9 ? '🛂' : '📋';
|
||||
final String icon = form is I9TaxForm ? '🛂' : '📋';
|
||||
|
||||
return GestureDetector(
|
||||
onTap: () {
|
||||
if (form.type == TaxFormType.i9) {
|
||||
Modular.to.pushNamed('i9');
|
||||
} else if (form.type == TaxFormType.w4) {
|
||||
Modular.to.pushNamed('w4');
|
||||
if (form is I9TaxForm) {
|
||||
Modular.to.pushNamed('i9', arguments: form);
|
||||
} else if (form is W4TaxForm) {
|
||||
Modular.to.pushNamed('w4', arguments: form);
|
||||
}
|
||||
},
|
||||
child: Container(
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
import 'package:firebase_auth/firebase_auth.dart';
|
||||
import 'package:flutter_modular/flutter_modular.dart';
|
||||
import 'package:krow_data_connect/krow_data_connect.dart';
|
||||
import 'package:krow_domain/krow_domain.dart';
|
||||
import 'data/repositories/tax_forms_repository_impl.dart';
|
||||
import 'domain/repositories/tax_forms_repository.dart';
|
||||
import 'domain/usecases/get_tax_forms_usecase.dart';
|
||||
import 'domain/usecases/submit_tax_form_usecase.dart';
|
||||
import 'domain/usecases/submit_i9_form_usecase.dart';
|
||||
import 'domain/usecases/submit_w4_form_usecase.dart';
|
||||
import 'presentation/blocs/i9/form_i9_cubit.dart';
|
||||
import 'presentation/blocs/tax_forms/tax_forms_cubit.dart';
|
||||
import 'presentation/blocs/w4/form_w4_cubit.dart';
|
||||
@@ -24,7 +26,8 @@ class StaffTaxFormsModule extends Module {
|
||||
|
||||
// Use Cases
|
||||
i.addLazySingleton(GetTaxFormsUseCase.new);
|
||||
i.addLazySingleton(SubmitTaxFormUseCase.new);
|
||||
i.addLazySingleton(SubmitI9FormUseCase.new);
|
||||
i.addLazySingleton(SubmitW4FormUseCase.new);
|
||||
|
||||
// Blocs
|
||||
i.addLazySingleton(TaxFormsCubit.new);
|
||||
@@ -35,7 +38,13 @@ class StaffTaxFormsModule extends Module {
|
||||
@override
|
||||
void routes(RouteManager r) {
|
||||
r.child('/', child: (_) => const TaxFormsPage());
|
||||
r.child('/i9', child: (_) => const FormI9Page());
|
||||
r.child('/w4', child: (_) => const FormW4Page());
|
||||
r.child(
|
||||
'/i9',
|
||||
child: (_) => FormI9Page(form: r.args.data as TaxForm?),
|
||||
);
|
||||
r.child(
|
||||
'/w4',
|
||||
child: (_) => FormW4Page(form: r.args.data as TaxForm?),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,778 +0,0 @@
|
||||
# Generated by pub
|
||||
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||
packages:
|
||||
_flutterfire_internals:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: _flutterfire_internals
|
||||
sha256: cd83f7d6bd4e4c0b0b4fef802e8796784032e1cc23d7b0e982cf5d05d9bbe182
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.66"
|
||||
archive:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: archive
|
||||
sha256: cb6a278ef2dbb298455e1a713bda08524a175630ec643a242c399c932a0a1f7d
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.6.1"
|
||||
args:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: args
|
||||
sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.7.0"
|
||||
async:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: async
|
||||
sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.13.0"
|
||||
auto_injector:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: auto_injector
|
||||
sha256: "1fc2624898e92485122eb2b1698dd42511d7ff6574f84a3a8606fc4549a1e8f8"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
bloc:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: bloc
|
||||
sha256: "106842ad6569f0b60297619e9e0b1885c2fb9bf84812935490e6c5275777804e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "8.1.4"
|
||||
boolean_selector:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: boolean_selector
|
||||
sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
characters:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: characters
|
||||
sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
clock:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: clock
|
||||
sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.2"
|
||||
code_assets:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: code_assets
|
||||
sha256: "83ccdaa064c980b5596c35dd64a8d3ecc68620174ab9b90b6343b753aa721687"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: collection
|
||||
sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.19.1"
|
||||
core_localization:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "../../../../../core_localization"
|
||||
relative: true
|
||||
source: path
|
||||
version: "0.0.1"
|
||||
crypto:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: crypto
|
||||
sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.7"
|
||||
csv:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: csv
|
||||
sha256: c6aa2679b2a18cb57652920f674488d89712efaf4d3fdf2e537215b35fc19d6c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.0.0"
|
||||
design_system:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "../../../../../design_system"
|
||||
relative: true
|
||||
source: path
|
||||
version: "0.0.1"
|
||||
equatable:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: equatable
|
||||
sha256: "3e0141505477fd8ad55d6eb4e7776d3fe8430be8e497ccb1521370c3f21a3e2b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.8"
|
||||
fake_async:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: fake_async
|
||||
sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.3"
|
||||
ffi:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: ffi
|
||||
sha256: d07d37192dbf97461359c1518788f203b0c9102cfd2c35a716b823741219542c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.5"
|
||||
file:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: file
|
||||
sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.0.1"
|
||||
firebase_app_check:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: firebase_app_check
|
||||
sha256: "45f0d279ea7ae4eac1867a4c85aa225761e3ac0ccf646386a860b2bc16581f76"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.4.1+4"
|
||||
firebase_app_check_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: firebase_app_check_platform_interface
|
||||
sha256: e32b4e6adeaac207a6f7afe0906d97c0811de42fb200d9b6317a09155de65e2b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.1+4"
|
||||
firebase_app_check_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: firebase_app_check_web
|
||||
sha256: "2cbc8a18a34813a7e31d7b30f989973087421cd5d0e397b4dd88a90289aa2bed"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.2+2"
|
||||
firebase_auth:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: firebase_auth
|
||||
sha256: b20d1540460814c5984474c1e9dd833bdbcff6ecd8d6ad86cc9da8cfd581c172
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.1.4"
|
||||
firebase_auth_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: firebase_auth_platform_interface
|
||||
sha256: fd0225320b6bbc92460c86352d16b60aea15f9ef88292774cca97b0522ea9f72
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "8.1.6"
|
||||
firebase_auth_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: firebase_auth_web
|
||||
sha256: be7dccb263b89fbda2a564de9d8193118196e8481ffb937222a025cdfdf82c40
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.1.2"
|
||||
firebase_core:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: firebase_core
|
||||
sha256: "923085c881663ef685269b013e241b428e1fb03cdd0ebde265d9b40ff18abf80"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.4.0"
|
||||
firebase_core_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: firebase_core_platform_interface
|
||||
sha256: cccb4f572325dc14904c02fcc7db6323ad62ba02536833dddb5c02cac7341c64
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.0.2"
|
||||
firebase_core_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: firebase_core_web
|
||||
sha256: "83e7356c704131ca4d8d8dd57e360d8acecbca38b1a3705c7ae46cc34c708084"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.4.0"
|
||||
firebase_data_connect:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: firebase_data_connect
|
||||
sha256: "01d0f8e33c520a6e6f59cf5ac6ff281d1927f7837f094fa8eb5fdb0b1b328ad8"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.2.2+2"
|
||||
fixnum:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: fixnum
|
||||
sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
flutter:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_bloc:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_bloc
|
||||
sha256: b594505eac31a0518bdcb4b5b79573b8d9117b193cc80cc12e17d639b10aa27a
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "8.1.6"
|
||||
flutter_localizations:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_modular:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_modular
|
||||
sha256: "33a63d9fe61429d12b3dfa04795ed890f17d179d3d38e988ba7969651fcd5586"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.4.1"
|
||||
flutter_test:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_web_plugins:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
font_awesome_flutter:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: font_awesome_flutter
|
||||
sha256: b9011df3a1fa02993630b8fb83526368cf2206a711259830325bab2f1d2a4eb0
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "10.12.0"
|
||||
glob:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: glob
|
||||
sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.3"
|
||||
google_fonts:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: google_fonts
|
||||
sha256: "6996212014b996eaa17074e02b1b925b212f5e053832d9048970dc27255a8fb3"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.1.0"
|
||||
google_identity_services_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: google_identity_services_web
|
||||
sha256: "5d187c46dc59e02646e10fe82665fc3884a9b71bc1c90c2b8b749316d33ee454"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.3.3+1"
|
||||
googleapis_auth:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: googleapis_auth
|
||||
sha256: befd71383a955535060acde8792e7efc11d2fccd03dd1d3ec434e85b68775938
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.6.0"
|
||||
grpc:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: grpc
|
||||
sha256: e93ee3bce45c134bf44e9728119102358c7cd69de7832d9a874e2e74eb8cab40
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.2.4"
|
||||
hooks:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: hooks
|
||||
sha256: "5d309c86e7ce34cd8e37aa71cb30cb652d3829b900ab145e4d9da564b31d59f7"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
http:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http
|
||||
sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.6.0"
|
||||
http2:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http2
|
||||
sha256: "382d3aefc5bd6dc68c6b892d7664f29b5beb3251611ae946a98d35158a82bbfa"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.1"
|
||||
http_parser:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http_parser
|
||||
sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.1.2"
|
||||
intl:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: intl
|
||||
sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.20.2"
|
||||
krow_core:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "../../../../../core"
|
||||
relative: true
|
||||
source: path
|
||||
version: "0.0.1"
|
||||
krow_data_connect:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "../../../../../data_connect"
|
||||
relative: true
|
||||
source: path
|
||||
version: "0.0.1"
|
||||
krow_domain:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "../../../../../domain"
|
||||
relative: true
|
||||
source: path
|
||||
version: "0.0.1"
|
||||
leak_tracker:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker
|
||||
sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "11.0.2"
|
||||
leak_tracker_flutter_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_flutter_testing
|
||||
sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.10"
|
||||
leak_tracker_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_testing
|
||||
sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.2"
|
||||
logging:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: logging
|
||||
sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
lucide_icons:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: lucide_icons
|
||||
sha256: ad24d0fd65707e48add30bebada7d90bff2a1bba0a72d6e9b19d44246b0e83c4
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.257.0"
|
||||
matcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: matcher
|
||||
sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.12.17"
|
||||
material_color_utilities:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: material_color_utilities
|
||||
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.11.1"
|
||||
meta:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: meta
|
||||
sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.17.0"
|
||||
modular_core:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: modular_core
|
||||
sha256: "1db0420a0dfb8a2c6dca846e7cbaa4ffeb778e247916dbcb27fb25aa566e5436"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.4.1"
|
||||
native_toolchain_c:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: native_toolchain_c
|
||||
sha256: "89e83885ba09da5fdf2cdacc8002a712ca238c28b7f717910b34bcd27b0d03ac"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.17.4"
|
||||
nested:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: nested
|
||||
sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
objective_c:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: objective_c
|
||||
sha256: "7fd0c4d8ac8980011753b9bdaed2bf15111365924cdeeeaeb596214ea2b03537"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "9.2.4"
|
||||
path:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path
|
||||
sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.9.1"
|
||||
path_provider:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider
|
||||
sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.5"
|
||||
path_provider_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_android
|
||||
sha256: f2c65e21139ce2c3dad46922be8272bb5963516045659e71bb16e151c93b580e
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.22"
|
||||
path_provider_foundation:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_foundation
|
||||
sha256: "2a376b7d6392d80cd3705782d2caa734ca4727776db0b6ec36ef3f1855197699"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.6.0"
|
||||
path_provider_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_linux
|
||||
sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.1"
|
||||
path_provider_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_platform_interface
|
||||
sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
path_provider_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_windows
|
||||
sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.0"
|
||||
platform:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: platform
|
||||
sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.6"
|
||||
plugin_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: plugin_platform_interface
|
||||
sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.8"
|
||||
protobuf:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: protobuf
|
||||
sha256: "68645b24e0716782e58948f8467fd42a880f255096a821f9e7d0ec625b00c84d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.0"
|
||||
provider:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: provider
|
||||
sha256: "4e82183fa20e5ca25703ead7e05de9e4cceed1fbd1eadc1ac3cb6f565a09f272"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.1.5+1"
|
||||
pub_semver:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pub_semver
|
||||
sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.0"
|
||||
result_dart:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: result_dart
|
||||
sha256: "0666b21fbdf697b3bdd9986348a380aa204b3ebe7c146d8e4cdaa7ce735e6054"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
shared_preferences:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences
|
||||
sha256: "2939ae520c9024cb197fc20dee269cd8cdbf564c8b5746374ec6cacdc5169e64"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.5.4"
|
||||
shared_preferences_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_android
|
||||
sha256: "83af5c682796c0f7719c2bbf74792d113e40ae97981b8f266fa84574573556bc"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.18"
|
||||
shared_preferences_foundation:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_foundation
|
||||
sha256: "4e7eaffc2b17ba398759f1151415869a34771ba11ebbccd1b0145472a619a64f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.5.6"
|
||||
shared_preferences_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_linux
|
||||
sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.1"
|
||||
shared_preferences_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_platform_interface
|
||||
sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.1"
|
||||
shared_preferences_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_web
|
||||
sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.3"
|
||||
shared_preferences_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_windows
|
||||
sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.1"
|
||||
sky_engine:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
slang:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: slang
|
||||
sha256: "13e3b6f07adc51ab751e7889647774d294cbce7a3382f81d9e5029acfe9c37b2"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.12.0"
|
||||
slang_flutter:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: slang_flutter
|
||||
sha256: "0a4545cca5404d6b7487cf61cf1fe56c52daeb08de56a7574ee8381fbad035a0"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.12.0"
|
||||
source_span:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_span
|
||||
sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.10.1"
|
||||
stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stack_trace
|
||||
sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.12.1"
|
||||
stream_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stream_channel
|
||||
sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
string_scanner:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: string_scanner
|
||||
sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.1"
|
||||
term_glyph:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: term_glyph
|
||||
sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.2"
|
||||
test_api:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.7"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: typed_data
|
||||
sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
uuid:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: uuid
|
||||
sha256: a11b666489b1954e01d992f3d601b1804a33937b5a8fe677bd26b8a9f96f96e8
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.5.2"
|
||||
vector_math:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vector_math
|
||||
sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.0"
|
||||
vm_service:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vm_service
|
||||
sha256: "45caa6c5917fa127b5dbcfbd1fa60b14e583afdc08bfc96dda38886ca252eb60"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "15.0.2"
|
||||
watcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: watcher
|
||||
sha256: "1398c9f081a753f9226febe8900fce8f7d0a67163334e1c94a2438339d79d635"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.1"
|
||||
web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: web
|
||||
sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
xdg_directories:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: xdg_directories
|
||||
sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
yaml:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: yaml
|
||||
sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.3"
|
||||
sdks:
|
||||
dart: ">=3.10.7 <4.0.0"
|
||||
flutter: ">=3.38.4"
|
||||
@@ -2,6 +2,7 @@ name: staff_tax_forms
|
||||
description: Staff Tax Forms feature.
|
||||
version: 0.0.1
|
||||
publish_to: none
|
||||
resolution: workspace
|
||||
|
||||
environment:
|
||||
sdk: '>=3.10.0 <4.0.0'
|
||||
|
||||
@@ -34,4 +34,4 @@ dependencies:
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
flutter_lints: ^2.0.0
|
||||
flutter_lints: ^6.0.0
|
||||
|
||||
@@ -2,9 +2,10 @@ name: staff_time_card
|
||||
description: Staff Time Card Feature
|
||||
version: 0.0.1
|
||||
publish_to: none
|
||||
resolution: workspace
|
||||
|
||||
environment:
|
||||
sdk: '>=3.0.0 <4.0.0'
|
||||
sdk: '>=3.10.0 <4.0.0'
|
||||
flutter: ">=3.0.0"
|
||||
|
||||
dependencies:
|
||||
|
||||
@@ -31,4 +31,4 @@ dependencies:
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
flutter_lints: ^2.0.0
|
||||
flutter_lints: ^6.0.0
|
||||
|
||||
@@ -25,11 +25,13 @@ class PersonalInfoBloc extends Bloc<PersonalInfoEvent, PersonalInfoState>
|
||||
required UpdatePersonalInfoUseCase updatePersonalInfoUseCase,
|
||||
}) : _getPersonalInfoUseCase = getPersonalInfoUseCase,
|
||||
_updatePersonalInfoUseCase = updatePersonalInfoUseCase,
|
||||
super(const PersonalInfoState()) {
|
||||
super(const PersonalInfoState.initial()) {
|
||||
on<PersonalInfoLoadRequested>(_onLoadRequested);
|
||||
on<PersonalInfoFieldUpdated>(_onFieldUpdated);
|
||||
on<PersonalInfoSaveRequested>(_onSaveRequested);
|
||||
on<PersonalInfoPhotoUploadRequested>(_onPhotoUploadRequested);
|
||||
on<PersonalInfoFieldChanged>(_onFieldChanged);
|
||||
on<PersonalInfoAddressSelected>(_onAddressSelected);
|
||||
on<PersonalInfoFormSubmitted>(_onSubmitted);
|
||||
|
||||
add(const PersonalInfoLoadRequested());
|
||||
}
|
||||
|
||||
/// Handles loading staff profile information.
|
||||
@@ -67,8 +69,8 @@ class PersonalInfoBloc extends Bloc<PersonalInfoEvent, PersonalInfoState>
|
||||
}
|
||||
|
||||
/// Handles updating a field value in the current staff profile.
|
||||
void _onFieldUpdated(
|
||||
PersonalInfoFieldUpdated event,
|
||||
void _onFieldChanged(
|
||||
PersonalInfoFieldChanged event,
|
||||
Emitter<PersonalInfoState> emit,
|
||||
) {
|
||||
final Map<String, dynamic> updatedValues = Map.from(state.formValues);
|
||||
@@ -77,8 +79,8 @@ class PersonalInfoBloc extends Bloc<PersonalInfoEvent, PersonalInfoState>
|
||||
}
|
||||
|
||||
/// Handles saving staff profile information.
|
||||
Future<void> _onSaveRequested(
|
||||
PersonalInfoSaveRequested event,
|
||||
Future<void> _onSubmitted(
|
||||
PersonalInfoFormSubmitted event,
|
||||
Emitter<PersonalInfoState> emit,
|
||||
) async {
|
||||
if (state.staff == null) return;
|
||||
@@ -116,33 +118,16 @@ class PersonalInfoBloc extends Bloc<PersonalInfoEvent, PersonalInfoState>
|
||||
}
|
||||
}
|
||||
|
||||
/// Handles uploading a profile photo.
|
||||
Future<void> _onPhotoUploadRequested(
|
||||
PersonalInfoPhotoUploadRequested event,
|
||||
void _onAddressSelected(
|
||||
PersonalInfoAddressSelected event,
|
||||
Emitter<PersonalInfoState> emit,
|
||||
) async {
|
||||
if (state.staff == null) return;
|
||||
|
||||
emit(state.copyWith(status: PersonalInfoStatus.uploadingPhoto));
|
||||
try {
|
||||
// TODO: Implement photo upload when repository method is available
|
||||
// final photoUrl = await _repository.uploadProfilePhoto(event.filePath);
|
||||
// final updatedStaff = Staff(...);
|
||||
// emit(state.copyWith(
|
||||
// status: PersonalInfoStatus.loaded,
|
||||
// staff: updatedStaff,
|
||||
// ));
|
||||
|
||||
// For now, just return to loaded state
|
||||
emit(state.copyWith(status: PersonalInfoStatus.loaded));
|
||||
} catch (e) {
|
||||
emit(state.copyWith(
|
||||
status: PersonalInfoStatus.error,
|
||||
errorMessage: e.toString(),
|
||||
));
|
||||
}
|
||||
) {
|
||||
// TODO: Implement Google Places logic if needed
|
||||
}
|
||||
|
||||
/// With _onPhotoUploadRequested and _onSaveRequested removed or renamed,
|
||||
/// there are no errors pointing to them here.
|
||||
|
||||
@override
|
||||
void dispose() {
|
||||
close();
|
||||
|
||||
@@ -14,11 +14,11 @@ class PersonalInfoLoadRequested extends PersonalInfoEvent {
|
||||
}
|
||||
|
||||
/// Event to update a field value.
|
||||
class PersonalInfoFieldUpdated extends PersonalInfoEvent {
|
||||
class PersonalInfoFieldChanged extends PersonalInfoEvent {
|
||||
final String field;
|
||||
final dynamic value;
|
||||
|
||||
const PersonalInfoFieldUpdated({
|
||||
const PersonalInfoFieldChanged({
|
||||
required this.field,
|
||||
required this.value,
|
||||
});
|
||||
@@ -27,17 +27,16 @@ class PersonalInfoFieldUpdated extends PersonalInfoEvent {
|
||||
List<Object?> get props => [field, value];
|
||||
}
|
||||
|
||||
/// Event to save personal information.
|
||||
class PersonalInfoSaveRequested extends PersonalInfoEvent {
|
||||
const PersonalInfoSaveRequested();
|
||||
/// Event to submit the form.
|
||||
class PersonalInfoFormSubmitted extends PersonalInfoEvent {
|
||||
const PersonalInfoFormSubmitted();
|
||||
}
|
||||
|
||||
/// Event to upload a profile photo.
|
||||
class PersonalInfoPhotoUploadRequested extends PersonalInfoEvent {
|
||||
final String filePath;
|
||||
|
||||
const PersonalInfoPhotoUploadRequested({required this.filePath});
|
||||
|
||||
/// Event when an address is selected from autocomplete.
|
||||
class PersonalInfoAddressSelected extends PersonalInfoEvent {
|
||||
final String address;
|
||||
const PersonalInfoAddressSelected(this.address);
|
||||
|
||||
@override
|
||||
List<Object?> get props => [filePath];
|
||||
List<Object?> get props => [address];
|
||||
}
|
||||
|
||||
@@ -49,6 +49,13 @@ class PersonalInfoState extends Equatable {
|
||||
this.errorMessage,
|
||||
});
|
||||
|
||||
/// Initial state.
|
||||
const PersonalInfoState.initial()
|
||||
: status = PersonalInfoStatus.initial,
|
||||
staff = null,
|
||||
formValues = const {},
|
||||
errorMessage = null;
|
||||
|
||||
/// Creates a copy of this state with the given fields replaced.
|
||||
PersonalInfoState copyWith({
|
||||
PersonalInfoStatus? status,
|
||||
|
||||
@@ -26,8 +26,7 @@ class PersonalInfoPage extends StatelessWidget {
|
||||
Widget build(BuildContext context) {
|
||||
final TranslationsStaffOnboardingPersonalInfoEn i18n = t.staff.onboarding.personal_info;
|
||||
return BlocProvider<PersonalInfoBloc>(
|
||||
create: (BuildContext context) => Modular.get<PersonalInfoBloc>()
|
||||
..add(const PersonalInfoLoadRequested()),
|
||||
create: (BuildContext context) => Modular.get<PersonalInfoBloc>(),
|
||||
child: BlocListener<PersonalInfoBloc, PersonalInfoState>(
|
||||
listener: (BuildContext context, PersonalInfoState state) {
|
||||
if (state.status == PersonalInfoStatus.saved) {
|
||||
|
||||
@@ -56,7 +56,7 @@ class _PersonalInfoContentState extends State<PersonalInfoContent> {
|
||||
|
||||
void _onPhoneChanged() {
|
||||
context.read<PersonalInfoBloc>().add(
|
||||
PersonalInfoFieldUpdated(
|
||||
PersonalInfoFieldChanged(
|
||||
field: 'phone',
|
||||
value: _phoneController.text,
|
||||
),
|
||||
@@ -73,7 +73,7 @@ class _PersonalInfoContentState extends State<PersonalInfoContent> {
|
||||
.toList();
|
||||
|
||||
context.read<PersonalInfoBloc>().add(
|
||||
PersonalInfoFieldUpdated(
|
||||
PersonalInfoFieldChanged(
|
||||
field: 'preferredLocations',
|
||||
value: locations,
|
||||
),
|
||||
@@ -81,7 +81,7 @@ class _PersonalInfoContentState extends State<PersonalInfoContent> {
|
||||
}
|
||||
|
||||
void _handleSave() {
|
||||
context.read<PersonalInfoBloc>().add(const PersonalInfoSaveRequested());
|
||||
context.read<PersonalInfoBloc>().add(const PersonalInfoFormSubmitted());
|
||||
}
|
||||
|
||||
void _handlePhotoTap() {
|
||||
|
||||
@@ -1,650 +0,0 @@
|
||||
# Generated by pub
|
||||
# See https://dart.dev/tools/pub/glossary#lockfile
|
||||
packages:
|
||||
async:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: async
|
||||
sha256: "758e6d74e971c3e5aceb4110bfd6698efc7f501675bcfe0c775459a8140750eb"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.13.0"
|
||||
auto_injector:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: auto_injector
|
||||
sha256: "1fc2624898e92485122eb2b1698dd42511d7ff6574f84a3a8606fc4549a1e8f8"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
bloc:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: bloc
|
||||
sha256: "106842ad6569f0b60297619e9e0b1885c2fb9bf84812935490e6c5275777804e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "8.1.4"
|
||||
boolean_selector:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: boolean_selector
|
||||
sha256: "8aab1771e1243a5063b8b0ff68042d67334e3feab9e95b9490f9a6ebf73b42ea"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
characters:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: characters
|
||||
sha256: f71061c654a3380576a52b451dd5532377954cf9dbd272a78fc8479606670803
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
clock:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: clock
|
||||
sha256: fddb70d9b5277016c77a80201021d40a2247104d9f4aa7bab7157b7e3f05b84b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.2"
|
||||
code_assets:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: code_assets
|
||||
sha256: "83ccdaa064c980b5596c35dd64a8d3ecc68620174ab9b90b6343b753aa721687"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
collection:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: collection
|
||||
sha256: "2f5709ae4d3d59dd8f7cd309b4e023046b57d8a6c82130785d2b0e5868084e76"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.19.1"
|
||||
core_localization:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "../../../core_localization"
|
||||
relative: true
|
||||
source: path
|
||||
version: "0.0.1"
|
||||
crypto:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: crypto
|
||||
sha256: c8ea0233063ba03258fbcf2ca4d6dadfefe14f02fab57702265467a19f27fadf
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.7"
|
||||
csv:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: csv
|
||||
sha256: c6aa2679b2a18cb57652920f674488d89712efaf4d3fdf2e537215b35fc19d6c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.0.0"
|
||||
design_system:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "../../../design_system"
|
||||
relative: true
|
||||
source: path
|
||||
version: "0.0.1"
|
||||
equatable:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: equatable
|
||||
sha256: "3e0141505477fd8ad55d6eb4e7776d3fe8430be8e497ccb1521370c3f21a3e2b"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.0.8"
|
||||
fake_async:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: fake_async
|
||||
sha256: "5368f224a74523e8d2e7399ea1638b37aecfca824a3cc4dfdf77bf1fa905ac44"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.3"
|
||||
ffi:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: ffi
|
||||
sha256: d07d37192dbf97461359c1518788f203b0c9102cfd2c35a716b823741219542c
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.5"
|
||||
file:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: file
|
||||
sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.0.1"
|
||||
fixnum:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: fixnum
|
||||
sha256: b6dc7065e46c974bc7c5f143080a6764ec7a4be6da1285ececdc37be96de53be
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
flutter:
|
||||
dependency: "direct main"
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_bloc:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_bloc
|
||||
sha256: b594505eac31a0518bdcb4b5b79573b8d9117b193cc80cc12e17d639b10aa27a
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "8.1.6"
|
||||
flutter_lints:
|
||||
dependency: "direct dev"
|
||||
description:
|
||||
name: flutter_lints
|
||||
sha256: "9e8c3858111da373efc5aa341de011d9bd23e2c5c5e0c62bccf32438e192d7b1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.2"
|
||||
flutter_localizations:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_modular:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: flutter_modular
|
||||
sha256: "33a63d9fe61429d12b3dfa04795ed890f17d179d3d38e988ba7969651fcd5586"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.4.1"
|
||||
flutter_test:
|
||||
dependency: "direct dev"
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
flutter_web_plugins:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
font_awesome_flutter:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: font_awesome_flutter
|
||||
sha256: b9011df3a1fa02993630b8fb83526368cf2206a711259830325bab2f1d2a4eb0
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "10.12.0"
|
||||
glob:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: glob
|
||||
sha256: c3f1ee72c96f8f78935e18aa8cecced9ab132419e8625dc187e1c2408efc20de
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.3"
|
||||
google_fonts:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: google_fonts
|
||||
sha256: "6996212014b996eaa17074e02b1b925b212f5e053832d9048970dc27255a8fb3"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "7.1.0"
|
||||
hooks:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: hooks
|
||||
sha256: "5d309c86e7ce34cd8e37aa71cb30cb652d3829b900ab145e4d9da564b31d59f7"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
http:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http
|
||||
sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.6.0"
|
||||
http_parser:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: http_parser
|
||||
sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.1.2"
|
||||
intl:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: intl
|
||||
sha256: "3df61194eb431efc39c4ceba583b95633a403f46c9fd341e550ce0bfa50e9aa5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.20.2"
|
||||
krow_core:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "../../../core"
|
||||
relative: true
|
||||
source: path
|
||||
version: "0.0.1"
|
||||
krow_data_connect:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "../../../data_connect"
|
||||
relative: true
|
||||
source: path
|
||||
version: "0.0.1"
|
||||
krow_domain:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
path: "../../../domain"
|
||||
relative: true
|
||||
source: path
|
||||
version: "0.0.1"
|
||||
leak_tracker:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker
|
||||
sha256: "33e2e26bdd85a0112ec15400c8cbffea70d0f9c3407491f672a2fad47915e2de"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "11.0.2"
|
||||
leak_tracker_flutter_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_flutter_testing
|
||||
sha256: "1dbc140bb5a23c75ea9c4811222756104fbcd1a27173f0c34ca01e16bea473c1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.10"
|
||||
leak_tracker_testing:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: leak_tracker_testing
|
||||
sha256: "8d5a2d49f4a66b49744b23b018848400d23e54caf9463f4eb20df3eb8acb2eb1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.2"
|
||||
lints:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: lints
|
||||
sha256: cbf8d4b858bb0134ef3ef87841abdf8d63bfc255c266b7bf6b39daa1085c4290
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.0.0"
|
||||
logging:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: logging
|
||||
sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.3.0"
|
||||
lucide_icons:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: lucide_icons
|
||||
sha256: ad24d0fd65707e48add30bebada7d90bff2a1bba0a72d6e9b19d44246b0e83c4
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.257.0"
|
||||
matcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: matcher
|
||||
sha256: dc58c723c3c24bf8d3e2d3ad3f2f9d7bd9cf43ec6feaa64181775e60190153f2
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.12.17"
|
||||
material_color_utilities:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: material_color_utilities
|
||||
sha256: f7142bb1154231d7ea5f96bc7bde4bda2a0945d2806bb11670e30b850d56bdec
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.11.1"
|
||||
meta:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: meta
|
||||
sha256: "23f08335362185a5ea2ad3a4e597f1375e78bce8a040df5c600c8d3552ef2394"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.17.0"
|
||||
modular_core:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: modular_core
|
||||
sha256: "1db0420a0dfb8a2c6dca846e7cbaa4ffeb778e247916dbcb27fb25aa566e5436"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.4.1"
|
||||
native_toolchain_c:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: native_toolchain_c
|
||||
sha256: "89e83885ba09da5fdf2cdacc8002a712ca238c28b7f717910b34bcd27b0d03ac"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.17.4"
|
||||
nested:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: nested
|
||||
sha256: "03bac4c528c64c95c722ec99280375a6f2fc708eec17c7b3f07253b626cd2a20"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.0.0"
|
||||
objective_c:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: objective_c
|
||||
sha256: "7fd0c4d8ac8980011753b9bdaed2bf15111365924cdeeeaeb596214ea2b03537"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "9.2.4"
|
||||
path:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path
|
||||
sha256: "75cca69d1490965be98c73ceaea117e8a04dd21217b37b292c9ddbec0d955bc5"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.9.1"
|
||||
path_provider:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider
|
||||
sha256: "50c5dd5b6e1aaf6fb3a78b33f6aa3afca52bf903a8a5298f53101fdaee55bbcd"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.5"
|
||||
path_provider_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_android
|
||||
sha256: f2c65e21139ce2c3dad46922be8272bb5963516045659e71bb16e151c93b580e
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.22"
|
||||
path_provider_foundation:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_foundation
|
||||
sha256: "2a376b7d6392d80cd3705782d2caa734ca4727776db0b6ec36ef3f1855197699"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.6.0"
|
||||
path_provider_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_linux
|
||||
sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.1"
|
||||
path_provider_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_platform_interface
|
||||
sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.2"
|
||||
path_provider_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: path_provider_windows
|
||||
sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.3.0"
|
||||
platform:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: platform
|
||||
sha256: "5d6b1b0036a5f331ebc77c850ebc8506cbc1e9416c27e59b439f917a902a4984"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.6"
|
||||
plugin_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: plugin_platform_interface
|
||||
sha256: "4820fbfdb9478b1ebae27888254d445073732dae3d6ea81f0b7e06d5dedc3f02"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.8"
|
||||
provider:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: provider
|
||||
sha256: "4e82183fa20e5ca25703ead7e05de9e4cceed1fbd1eadc1ac3cb6f565a09f272"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "6.1.5+1"
|
||||
pub_semver:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: pub_semver
|
||||
sha256: "5bfcf68ca79ef689f8990d1160781b4bad40a3bd5e5218ad4076ddb7f4081585"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.0"
|
||||
result_dart:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: result_dart
|
||||
sha256: "0666b21fbdf697b3bdd9986348a380aa204b3ebe7c146d8e4cdaa7ce735e6054"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.1"
|
||||
shared_preferences:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences
|
||||
sha256: "2939ae520c9024cb197fc20dee269cd8cdbf564c8b5746374ec6cacdc5169e64"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.5.4"
|
||||
shared_preferences_android:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_android
|
||||
sha256: "83af5c682796c0f7719c2bbf74792d113e40ae97981b8f266fa84574573556bc"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.18"
|
||||
shared_preferences_foundation:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_foundation
|
||||
sha256: "4e7eaffc2b17ba398759f1151415869a34771ba11ebbccd1b0145472a619a64f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.5.6"
|
||||
shared_preferences_linux:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_linux
|
||||
sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.1"
|
||||
shared_preferences_platform_interface:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_platform_interface
|
||||
sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.1"
|
||||
shared_preferences_web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_web
|
||||
sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.3"
|
||||
shared_preferences_windows:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: shared_preferences_windows
|
||||
sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.4.1"
|
||||
sky_engine:
|
||||
dependency: transitive
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
slang:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: slang
|
||||
sha256: "13e3b6f07adc51ab751e7889647774d294cbce7a3382f81d9e5029acfe9c37b2"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.12.0"
|
||||
slang_flutter:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: slang_flutter
|
||||
sha256: "0a4545cca5404d6b7487cf61cf1fe56c52daeb08de56a7574ee8381fbad035a0"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.12.0"
|
||||
source_span:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: source_span
|
||||
sha256: "254ee5351d6cb365c859e20ee823c3bb479bf4a293c22d17a9f1bf144ce86f7c"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.10.1"
|
||||
stack_trace:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stack_trace
|
||||
sha256: "8b27215b45d22309b5cddda1aa2b19bdfec9df0e765f2de506401c071d38d1b1"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.12.1"
|
||||
stream_channel:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: stream_channel
|
||||
sha256: "969e04c80b8bcdf826f8f16579c7b14d780458bd97f56d107d3950fdbeef059d"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.1.4"
|
||||
string_scanner:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: string_scanner
|
||||
sha256: "921cd31725b72fe181906c6a94d987c78e3b98c2e205b397ea399d4054872b43"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.1"
|
||||
term_glyph:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: term_glyph
|
||||
sha256: "7f554798625ea768a7518313e58f83891c7f5024f88e46e7182a4558850a4b8e"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.2"
|
||||
test_api:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: test_api
|
||||
sha256: ab2726c1a94d3176a45960b6234466ec367179b87dd74f1611adb1f3b5fb9d55
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "0.7.7"
|
||||
typed_data:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: typed_data
|
||||
sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.4.0"
|
||||
uuid:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: uuid
|
||||
sha256: a11b666489b1954e01d992f3d601b1804a33937b5a8fe677bd26b8a9f96f96e8
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "4.5.2"
|
||||
vector_math:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vector_math
|
||||
sha256: d530bd74fea330e6e364cda7a85019c434070188383e1cd8d9777ee586914c5b
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "2.2.0"
|
||||
vm_service:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: vm_service
|
||||
sha256: "45caa6c5917fa127b5dbcfbd1fa60b14e583afdc08bfc96dda38886ca252eb60"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "15.0.2"
|
||||
watcher:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: watcher
|
||||
sha256: "1398c9f081a753f9226febe8900fce8f7d0a67163334e1c94a2438339d79d635"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.1"
|
||||
web:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: web
|
||||
sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.1"
|
||||
xdg_directories:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: xdg_directories
|
||||
sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15"
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.1.0"
|
||||
yaml:
|
||||
dependency: transitive
|
||||
description:
|
||||
name: yaml
|
||||
sha256: b9da305ac7c39faa3f030eccd175340f968459dae4af175130b3fc47e40d76ce
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "3.1.3"
|
||||
sdks:
|
||||
dart: ">=3.10.7 <4.0.0"
|
||||
flutter: ">=3.38.4"
|
||||
@@ -2,9 +2,10 @@ name: staff_shifts
|
||||
description: A new Flutter package project.
|
||||
version: 0.0.1
|
||||
publish_to: 'none'
|
||||
resolution: workspace
|
||||
|
||||
environment:
|
||||
sdk: '>=3.0.0 <4.0.0'
|
||||
sdk: '>=3.10.0 <4.0.0'
|
||||
flutter: ">=3.0.0"
|
||||
|
||||
dependencies:
|
||||
@@ -30,4 +31,4 @@ dependencies:
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
sdk: flutter
|
||||
flutter_lints: ^3.0.0
|
||||
flutter_lints: ^6.0.0
|
||||
|
||||
Reference in New Issue
Block a user