feat: Refactor code structure and optimize performance across multiple modules

This commit is contained in:
Achintha Isuru
2025-11-17 23:29:28 -05:00
parent 831570f2e0
commit a64cbd9edf
1508 changed files with 105319 additions and 0 deletions

View File

@@ -0,0 +1,61 @@
import 'package:graphql_flutter/graphql_flutter.dart';
import 'package:injectable/injectable.dart';
import 'package:krow/core/application/clients/api/api_client.dart';
import 'package:krow/core/data/models/pagination_wrapper/pagination_wrapper.dart';
import 'invoices_gql.dart';
import 'models/invoice_model.dart';
@Injectable()
class InvoiceApiProvider {
final ApiClient _client;
InvoiceApiProvider({required ApiClient client}) : _client = client;
Future<PaginationWrapper> fetchInvoices(String? status,
{String? after}) async {
final QueryResult result = await _client.query(
schema: getInvoicesQuery,
body: {
if (status != null) 'status': status,
'first': 20,
'after': after
});
if (result.hasException) {
throw Exception(result.exception.toString());
}
return PaginationWrapper.fromJson(
result.data!['client_invoices'], (json) => InvoiceModel.fromJson(json));
}
Future<void>approveInvoice({required String invoiceId}) async{
final QueryResult result = await _client.mutate(
schema: approveInvoiceMutation,
body: {
'id': invoiceId,
});
if (result.hasException) {
throw Exception(result.exception.toString());
}
}
Future<void> disputeInvoice({
required String invoiceId,
required String reason,
required String comment,
}) async {
final QueryResult result = await _client.mutate(
schema: disputeInvoiceMutation,
body: {
'id': invoiceId,
'reason': reason,
'comment': comment,
});
if (result.hasException) {
throw Exception(result.exception.toString());
}
}
}

View File

@@ -0,0 +1,145 @@
const String getInvoicesQuery = '''
query GetInvoices (\$status: InvoiceStatus, \$first: Int!, \$after: String) {
client_invoices( status: \$status, first: \$first, after: \$after) {
pageInfo {
hasNextPage
hasPreviousPage
startCursor
endCursor
total
count
currentPage
lastPage
}
edges {
...invoice
cursor
}
}
}
fragment invoice on InvoiceEdge {
node {
id
business {
id
name
avatar
registration
contact{
id
first_name
last_name
email
phone
}
}
event {
id
status
start_time
end_time
contract_type
...hub
name
date
purchase_order
}
status
contract_type
contract_value
total
addons_amount
work_amount
issued_at
due_at
...items
dispute {
id
reason
details
support_note
}
}
}
fragment items on Invoice {
items {
id
staff {
id
first_name
last_name
email
phone
address
}
position {
id
start_time
end_time
break
count
rate
...business_skill
staff{
id
pivot {
id
status
start_at
end_at
clock_in
clock_out
}
}
}
work_hours
rate
addons_amount
work_amount
total_amount
}
}
fragment business_skill on EventShiftPosition {
business_skill {
id
skill {
id
name
slug
}
price
is_active
}
}
fragment hub on Event {
hub {
id
name
full_address {
formatted_address
}
}
}
''';
const String approveInvoiceMutation = '''
mutation ApproveInvoice(\$id: ID!) {
confirm_client_invoice(id: \$id) {
id
}
}
''';
const String disputeInvoiceMutation = '''
mutation DisputeInvoice(\$id: ID!, \$reason: String!, \$comment: String!) {
dispute_client_invoice(id: \$id, reason: \$reason, details: \$comment) {
id
}
}
''';

View File

@@ -0,0 +1,66 @@
import 'dart:async';
import 'package:injectable/injectable.dart';
import 'package:krow/features/invoice/data/invoices_api_provider.dart';
import 'package:krow/features/invoice/domain/invoice_entity.dart';
import 'package:krow/features/invoice/domain/invoices_repository.dart';
@Singleton(as: InvoicesRepository)
class InvoicesRepositoryImpl extends InvoicesRepository {
final InvoiceApiProvider _apiProvider;
StreamController<InvoiceStatusFilterType>? _statusController;
InvoicesRepositoryImpl({required InvoiceApiProvider apiProvider})
: _apiProvider = apiProvider;
@override
Stream<InvoiceStatusFilterType> get statusStream {
_statusController ??= StreamController<InvoiceStatusFilterType>.broadcast();
return _statusController!.stream;
}
@override
void dispose() {
_statusController?.close();
}
@override
Future<List<InvoiceListEntity>> getInvoices(
{String? lastItemId,
required InvoiceStatusFilterType statusFilter}) async {
var paginationWrapper = await _apiProvider.fetchInvoices(
statusFilter == InvoiceStatusFilterType.all ? null : statusFilter.name,
after: lastItemId);
return paginationWrapper.edges.map((e) {
return InvoiceListEntity.fromModel(
e.node,
cursor: (paginationWrapper.pageInfo.hasNextPage) ? e.cursor : null,
);
}).toList();
}
@override
Future<void> approveInvoice({required String invoiceId}) async{
await _apiProvider.approveInvoice(invoiceId: invoiceId).then((value) {
_statusController?.add(InvoiceStatusFilterType.verified);
});
_statusController?.add(InvoiceStatusFilterType.all);
_statusController?.add(InvoiceStatusFilterType.open);
}
@override
Future<void> disputeInvoice({
required String invoiceId,
required String reason,
required String comment,
}) async {
await _apiProvider.disputeInvoice(
invoiceId: invoiceId,
reason: reason,
comment: comment,
);
_statusController?.add(InvoiceStatusFilterType.disputed);
_statusController?.add(InvoiceStatusFilterType.all);
_statusController?.add(InvoiceStatusFilterType.open);
}
}

View File

@@ -0,0 +1,24 @@
import 'package:json_annotation/json_annotation.dart';
part 'invoice_decline_model.g.dart';
@JsonSerializable(fieldRename: FieldRename.snake)
class InvoiceDeclineModel{
String id;
String? reason;
String? details;
String? supportNote;
InvoiceDeclineModel({
required this.id,
required this.reason,
this.details,
this.supportNote,
});
factory InvoiceDeclineModel.fromJson(Map<String, dynamic> json) {
return _$InvoiceDeclineModelFromJson(json);
}
Map<String, dynamic> toJson() => _$InvoiceDeclineModelToJson(this);
}

View File

@@ -0,0 +1,35 @@
import 'package:json_annotation/json_annotation.dart';
import 'package:krow/core/data/models/shift/event_shift_position_model.dart';
import 'package:krow/core/data/models/staff/staff_model.dart';
import 'package:krow/core/entity/staff_contact_entity.dart';
import 'package:krow/features/events/presentation/event_details/widgets/role/asigned_staff.dart';
part 'invoice_item_model.g.dart';
@JsonSerializable(fieldRename: FieldRename.snake)
class InvoiceItemModel {
final String id;
final StaffModel? staff;
final EventShiftPositionModel? position;
final double? workHours;
final double? rate;
final double? addonsAmount;
final double? workAmount;
final double? totalAmount;
InvoiceItemModel(
{required this.id,
required this.staff,
required this.position,
required this.workHours,
required this.rate,
required this.addonsAmount,
required this.workAmount,
required this.totalAmount});
factory InvoiceItemModel.fromJson(Map<String, dynamic> json) {
return _$InvoiceItemModelFromJson(json);
}
Map<String, dynamic> toJson() => _$InvoiceItemModelToJson(this);
}

View File

@@ -0,0 +1,75 @@
import 'package:json_annotation/json_annotation.dart';
import 'package:krow/core/data/models/event/business_model.dart';
import 'package:krow/core/data/models/event/event_model.dart';
import 'package:krow/features/invoice/data/models/invoice_decline_model.dart';
import 'package:krow/features/invoice/data/models/invoice_item_model.dart';
part 'invoice_model.g.dart';
enum InvoiceStatus { open, disputed, resolved, paid, overdue, verified }
@JsonSerializable(fieldRename: FieldRename.snake)
class InvoiceModel {
final String id;
final InvoiceStatus status;
final BusinessModel? business;
final EventModel? event;
final String? contractType;
final String? contractValue;
final String? dueAt;
final double? total;
final double? addonsAmount;
final double? workAmount;
final List<InvoiceItemModel>? items;
final InvoiceDeclineModel? dispute;
factory InvoiceModel.fromJson(Map<String, dynamic> json) {
return _$InvoiceModelFromJson(json);
}
InvoiceModel(
{required this.id,
required this.status,
required this.business,
required this.event,
required this.contractType,
required this.contractValue,
required this.total,
required this.addonsAmount,
required this.dueAt,
required this.workAmount,
required this.items,
required this.dispute});
Map<String, dynamic> toJson() => _$InvoiceModelToJson(this);
copyWith({
String? id,
InvoiceStatus? status,
BusinessModel? business,
EventModel? event,
String? contractType,
String? contractValue,
String? dueAt,
double? total,
double? addonsAmount,
double? workAmount,
List<InvoiceItemModel>? items,
InvoiceDeclineModel? dispute,
}) {
return InvoiceModel(
id: id ?? this.id,
status: status ?? this.status,
business: business ?? this.business,
event: event ?? this.event,
contractType: contractType ?? this.contractType,
contractValue: contractValue ?? this.contractValue,
total: total ?? this.total,
addonsAmount: addonsAmount ?? this.addonsAmount,
workAmount: workAmount ?? this.workAmount,
items: items ?? this.items,
dueAt: dueAt ?? this.dueAt,
dispute: dispute ?? this.dispute,
);
}
}