feat: legacy mobile apps created

This commit is contained in:
Achintha Isuru
2025-12-02 23:51:04 -05:00
parent 850441ca64
commit 8e7753b324
1519 changed files with 0 additions and 16 deletions

View File

@@ -0,0 +1,39 @@
import 'package:injectable/injectable.dart';
import 'package:krow/core/application/clients/api/api_client.dart';
import 'package:krow/core/data/models/staff/bank_acc.dart';
import 'package:krow/features/profile/bank_account/data/gql.dart';
@injectable
class BankAccountApiProvider {
final ApiClient _apiClient;
BankAccountApiProvider(this._apiClient);
Stream<BankAcc?> getStaffBankAcc() async* {
await for (var response
in _apiClient.queryWithCache(schema: staffBankAccount)) {
if (response == null || response.data == null) {
continue;
}
if (response.hasException) {
throw Exception(response.exception.toString());
}
final bankAcc =
BankAcc.fromJson(response.data?['me']?['bank_account'] ?? {});
yield bankAcc;
}
}
Future<void> putBunkAccount(BankAcc bankAcc) async {
final Map<String, dynamic> variables = {
'input': bankAcc.toJson(),
};
var result =
await _apiClient.mutate(schema: updateBankAccount, body: variables);
if (result.hasException) {
throw Exception(result.exception.toString());
}
}
}

View File

@@ -0,0 +1,7 @@
import 'package:krow/core/data/models/staff/bank_acc.dart';
abstract class BankAccountRepository {
Stream<BankAcc?> getStaffBankAcc();
Future<void> putBankAcc(BankAcc address);
}

View File

@@ -0,0 +1,28 @@
const String staffBankAccount = r'''
query staffAddress {
me {
id
bank_account {
id
holder_name
bank_name
number
routing_number
country
state
city
street
building
zip
}
}
}
''';
const String updateBankAccount = r'''
mutation updateBankAccount($input: UpdateStaffBankAccountInput!) {
update_staff_bank_account(input: $input) {
}
}
''';

View File

@@ -0,0 +1,22 @@
import 'package:injectable/injectable.dart';
import 'package:krow/core/data/models/staff/bank_acc.dart';
import 'package:krow/features/profile/bank_account/data/bank_account_api_provider.dart';
import 'package:krow/features/profile/bank_account/data/bank_account_repository.dart';
@Singleton(as: BankAccountRepository)
class BankAccountRepositoryImpl implements BankAccountRepository {
final BankAccountApiProvider _apiProvider;
BankAccountRepositoryImpl({required BankAccountApiProvider apiProvider})
: _apiProvider = apiProvider;
@override
Stream<BankAcc?> getStaffBankAcc() {
return _apiProvider.getStaffBankAcc();
}
@override
Future<void> putBankAcc(BankAcc acc) {
return _apiProvider.putBunkAccount(acc);
}
}

View File

@@ -0,0 +1,32 @@
import 'dart:async';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:krow/core/application/di/injectable.dart';
import 'package:krow/core/data/models/staff/bank_acc.dart';
import 'package:krow/features/profile/bank_account/data/bank_account_repository.dart';
part 'bank_account_event.dart';
part 'bank_account_state.dart';
class BankAccountBloc extends Bloc<BankAccountEvent, BankAccountState> {
BankAccountBloc() : super(const BankAccountState()) {
on<BankAccountEventInit>(_onInit);
on<BankAccountEventUpdate>(_onSubmit);
}
FutureOr<void> _onInit(event, emit) async {
await for (var account
in getIt<BankAccountRepository>().getStaffBankAcc()) {
emit(state.copyWith(bankAcc: account));
}
}
FutureOr<void> _onSubmit(BankAccountEventUpdate event, emit) async {
emit(state.copyWith(inLoading: true));
var newBankAcc = BankAcc.fromJson(event.bankAcc);
await getIt<BankAccountRepository>().putBankAcc(newBankAcc);
emit(state.copyWith(success: true, bankAcc: newBankAcc));
}
}

View File

@@ -0,0 +1,12 @@
part of 'bank_account_bloc.dart';
@immutable
sealed class BankAccountEvent {}
class BankAccountEventInit extends BankAccountEvent {}
class BankAccountEventUpdate extends BankAccountEvent {
final Map<String, String> bankAcc;
BankAccountEventUpdate(this.bankAcc);
}

View File

@@ -0,0 +1,77 @@
part of 'bank_account_bloc.dart';
@immutable
class BankAccountState {
final BankAcc? bankAcc;
final bool inLoading;
final bool success;
const BankAccountState(
{this.inLoading = false, this.success = false, this.bankAcc});
BankAccountState copyWith({
BankAcc? bankAcc,
bool? inLoading,
bool? success,
}) {
return BankAccountState(
inLoading: inLoading ?? false,
success: success ?? false,
bankAcc: bankAcc ?? this.bankAcc,
);
}
Map<String, BankAccField> mapFromBankAccount() {
return {
'holder_name': BankAccField(
title: 'account_holder_name'.tr(),
value: bankAcc?.holderName,
),
'bank_name': BankAccField(
title: 'bank_name'.tr(),
value: bankAcc?.bankName,
),
'number': BankAccField(
title: 'account_number'.tr(),
value: bankAcc?.number,
),
'routing_number': BankAccField(
title: 'routing_number_us'.tr(),
value: bankAcc?.routingNumber,
optional: true),
'country': BankAccField(
title: 'country'.tr(),
value: bankAcc?.country,
),
'state': BankAccField(
title: 'state'.tr(),
value: bankAcc?.state,
),
'city': BankAccField(
title: 'city'.tr(),
value: bankAcc?.city,
),
'street': BankAccField(
title: 'street_address'.tr(),
value: bankAcc?.street,
),
'building': BankAccField(
title: 'apt_suite_building'.tr(),
value: bankAcc?.building,
optional: true),
'zip': BankAccField(
title: 'zip_code'.tr(),
value: bankAcc?.zip,
),
};
}
}
class BankAccField {
String title;
String? value;
bool optional;
BankAccField({required this.title, this.value = '', this.optional = false});
}

View File

@@ -0,0 +1,18 @@
import 'package:auto_route/auto_route.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:krow/features/profile/bank_account/domain/bloc/bank_account_bloc.dart';
@RoutePage()
class BankAccountFlowScreen extends StatelessWidget {
const BankAccountFlowScreen({super.key});
@override
Widget build(BuildContext context) {
return MultiBlocProvider(providers: [
BlocProvider<BankAccountBloc>(
create: (context) => BankAccountBloc()..add(BankAccountEventInit()),
),
], child: const AutoRouter());
}
}

View File

@@ -0,0 +1,161 @@
import 'package:auto_route/auto_route.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:gap/gap.dart';
import 'package:krow/core/presentation/styles/kw_box_decorations.dart';
import 'package:krow/core/presentation/styles/kw_text_styles.dart';
import 'package:krow/core/presentation/styles/theme.dart';
import 'package:krow/core/presentation/widgets/scroll_layout_helper.dart';
import 'package:krow/core/presentation/widgets/ui_kit/kw_app_bar.dart';
import 'package:krow/core/presentation/widgets/ui_kit/kw_button.dart';
import 'package:krow/core/presentation/widgets/ui_kit/kw_input.dart';
import 'package:krow/features/profile/bank_account/domain/bloc/bank_account_bloc.dart';
@RoutePage()
class BankAccountEditScreen extends StatefulWidget {
const BankAccountEditScreen({super.key});
@override
State<BankAccountEditScreen> createState() => _BankAccountEditScreenState();
}
class _BankAccountEditScreenState extends State<BankAccountEditScreen> {
Map<String, BankAccField>? bankAccInfo;
final Map<String, TextEditingController> _controllers = {};
var showInputError = false;
@override
void initState() {
WidgetsBinding.instance.addPostFrameCallback((_) {
bankAccInfo = Map.of(
BlocProvider.of<BankAccountBloc>(context).state.mapFromBankAccount());
bankAccInfo?.keys.forEach((key) {
_controllers[key] =
TextEditingController(text: bankAccInfo?[key]?.value);
});
setState(() {});
});
super.initState();
}
@override
Widget build(BuildContext context) {
return BlocConsumer<BankAccountBloc, BankAccountState>(
listener: (context, state) {
if (state.success) {
context.router.maybePop();
}
},
builder: (context, state) {
return Scaffold(
appBar: KwAppBar(
titleText: 'edit_bank_account'.tr(),
showNotification: true,
),
body: ScrollLayoutHelper(
padding: const EdgeInsets.all(16),
upperWidget: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'edit_information_below'.tr(),
style: AppTextStyles.bodySmallReg
.copyWith(color: AppColors.blackGray),
),
const Gap(24),
Container(
padding: const EdgeInsets.all(12),
decoration: KwBoxDecorations.primaryLight12,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildCardHeader(context, 'account_details'.tr()),
...(bankAccInfo?.entries
.take(4)
.map((entry) =>
_buildInput(entry.key, entry.value))
.toList() ??
[])
],
),
),
const Gap(12),
Container(
padding: const EdgeInsets.all(12),
decoration: KwBoxDecorations.primaryLight12,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildCardHeader(context, 'billing_address'.tr()),
...(bankAccInfo?.entries
.skip(4)
.map((entry) =>
_buildInput(entry.key, entry.value))
.toList() ??
[])
],
),
),
const Gap(24),
],
),
lowerWidget: KwButton.primary(
label: 'save_changes'.tr(),
onPressed: () {
if (bankAccInfo?.values.any((element) =>
!element.optional &&
(element.value?.isEmpty ?? false)) ??
false) {
setState(() {
showInputError = true;
});
return;
}
var newAcc = bankAccInfo?.map((key, value) => MapEntry(
key,
_controllers[key]?.text ?? '',
));
BlocProvider.of<BankAccountBloc>(context)
.add(BankAccountEventUpdate(newAcc!));
},
),
),
);
},
);
}
Widget _buildCardHeader(BuildContext context, String title) {
return Text(
title,
style: AppTextStyles.bodyMediumMed,
);
}
Widget _buildInput(String key, BankAccField field) {
var hasError = !field.optional &&
(_controllers[key]?.text.isEmpty ?? false) &&
showInputError;
return Padding(
padding: const EdgeInsets.only(top: 8.0),
child: KwTextInput(
hintText: '',
title: field.title,
onChanged: (value) {
setState(() {
field.value = value;
});
},
helperText: hasError ? 'field_cant_be_empty'.tr() : null,
showError: hasError,
controller: _controllers[key],
),
);
}
}

View File

@@ -0,0 +1,109 @@
import 'package:auto_route/auto_route.dart';
import 'package:easy_localization/easy_localization.dart';
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:gap/gap.dart';
import 'package:krow/core/application/routing/routes.gr.dart';
import 'package:krow/core/presentation/gen/assets.gen.dart';
import 'package:krow/core/presentation/styles/kw_box_decorations.dart';
import 'package:krow/core/presentation/styles/kw_text_styles.dart';
import 'package:krow/core/presentation/styles/theme.dart';
import 'package:krow/core/presentation/widgets/scroll_layout_helper.dart';
import 'package:krow/core/presentation/widgets/ui_kit/kw_app_bar.dart';
import 'package:krow/features/profile/bank_account/domain/bloc/bank_account_bloc.dart';
@RoutePage()
class BankAccountScreen extends StatefulWidget implements AutoRouteWrapper {
const BankAccountScreen({super.key});
@override
State<BankAccountScreen> createState() => _BankAccountScreenState();
@override
Widget wrappedRoute(BuildContext context) {
return this;
}
}
class _BankAccountScreenState extends State<BankAccountScreen> {
@override
Widget build(BuildContext context) {
return BlocBuilder<BankAccountBloc, BankAccountState>(
builder: (context, state) {
return Scaffold(
appBar: KwAppBar(
titleText: 'bank_account'.tr(),
showNotification: true,
),
body: ScrollLayoutHelper(
padding: const EdgeInsets.all(16),
upperWidget: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
Text(
'securely_manage_bank_account'.tr(),
style: AppTextStyles.bodySmallReg
.copyWith(color: AppColors.blackGray),
),
const Gap(24),
Container(
padding: const EdgeInsets.all(12),
decoration: KwBoxDecorations.primaryLight12,
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
_buildCardHeader(context),
...(state
.mapFromBankAccount()
.entries
.map((entry) => infoRow(
entry.value.title, entry.value.value ?? ''))
.expand((e) => e)
.toList())
],
),
),
],
),
lowerWidget: Container(),
),
);
},
);
}
Row _buildCardHeader(BuildContext context) {
return Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text(
'your_payment_details'.tr(),
style: AppTextStyles.bodyMediumMed,
),
GestureDetector(
onTap: () {
context.router.push(const BankAccountEditRoute());
},
child: Assets.images.icons.edit.svg(height: 16, width: 16))
],
);
}
List<Widget> infoRow(
String title,
String value,
) {
return [
const Gap(24),
Text(
'$title:'.toUpperCase(),
style: AppTextStyles.captionReg.copyWith(color: AppColors.blackGray),
),
const Gap(8),
Text(
value,
style: AppTextStyles.bodyMediumMed,
),
];
}
}