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,79 @@
const String _staffPaymentFields = '''
id
rate
assignment {
clock_in
clock_out
start_at
end_at
break_in
break_out
position {
shift {
event {
name
date
business {
id
name
avatar
}
}
}
business_skill {
skill {
name
}
}
}
}
work_hours
amount
status
paid_at
created_at
updated_at
''';
const String getWorkSummaryQuerySchema = '''
query GetWorkSummary {
staff_work_summary {
weekly_hours
monthly_hours
weekly_earnings
monthly_earnings
}
}
''';
const String getPaymentsQuerySchema = '''
query GetStaffPayments (\$status: StaffPaymentStatusInput!, \$first: Int!, \$after: String) {
staff_payments(status: \$status, first: \$first, after: \$after) {
pageInfo {
hasNextPage
}
edges {
node {
$_staffPaymentFields
}
cursor
}
}
}
''';
const String confirmPaymentMutationSchema = '''
mutation ConfirmStaffPayment (\$id: ID!) {
confirm_staff_payment(id: \$id) {
$_staffPaymentFields
}
}
''';
const String declinePaymentMutationSchema = '''
mutation DeclineStaffPayment (\$id: ID!, \$reason: String!, \$details: String) {
decline_staff_payment(id: \$id, reason: \$reason, details: \$details) {
$_staffPaymentFields
}
}
''';

View File

@@ -0,0 +1,67 @@
class EarningModel {
EarningModel({
required this.id,
required this.rate,
required this.businessName,
required this.businessAvatar,
required this.businessSkill,
required this.workHours,
required this.amount,
required this.status,
required this.paidAt,
required this.clockInAt,
required this.clockOutAt,
this.breakIn,
this.breakOut,
required this.eventName,
required this.eventDate,
});
factory EarningModel.fromJson(Map<String, dynamic> json) {
final assignment = json['assignment'] as Map<String, dynamic>;
final positionData = assignment['position'] as Map<String, dynamic>;
final eventData = positionData['shift']['event'] as Map<String, dynamic>;
final businessData = eventData['business'] as Map<String, dynamic>;
return EarningModel(
id: json['id'] as String? ?? '',
rate: (json['rate'] as num? ?? 0).toDouble(),
businessName: businessData['name'] as String? ?? '',
businessAvatar: businessData['avatar'] as String? ?? '',
businessSkill:
positionData['business_skill']['skill']['name'] as String? ?? '',
workHours: (json['work_hours'] as num? ?? 0).toDouble(),
amount: (json['amount'] as num? ?? 0).toDouble(),
status: json['status'] as String? ?? 'failed',
paidAt: DateTime.tryParse(
json['paid_at'] as String? ?? '',
),
clockInAt: DateTime.parse(
assignment['clock_in'] ?? assignment['start_at'] as String,
),
clockOutAt: DateTime.parse(
assignment['clock_out'] ?? assignment['end_at'] as String,
),
breakIn: DateTime.tryParse(assignment['break_in'] ?? ''),
breakOut: DateTime.tryParse(assignment['break_out'] ?? ''),
eventName: eventData['name'] as String? ?? '',
eventDate: DateTime.parse(eventData['date'] as String? ?? ''),
);
}
final String id;
final double rate;
final String businessName;
final String businessAvatar;
final String businessSkill;
final double workHours;
final double amount;
final String status;
final DateTime? paidAt;
final DateTime clockInAt;
final DateTime clockOutAt;
final DateTime? breakIn;
final DateTime? breakOut;
final String eventName;
final DateTime eventDate;
}

View File

@@ -0,0 +1,70 @@
class EarningsSummaryModel {
EarningsSummaryModel({
required this.totalEarningsByWeek,
required this.totalEarningsByMonth,
required this.totalWorkedHoursByWeek,
required this.totalWorkedHoursByMonth,
required this.payoutByWeek,
required this.payoutByMonth,
required this.startDatePeriod,
required this.endDatePeriod,
required this.maxEarningInPeriod,
required this.minEarningInPeriod,
});
//TODO: Additional fields that are used in the Earnings History screen are for now returning default values.
factory EarningsSummaryModel.fromJson(Map<String, dynamic> json) {
final time = DateTime.now();
return EarningsSummaryModel(
totalEarningsByWeek: (json['weekly_earnings'] as num?)?.toDouble() ?? 0,
totalEarningsByMonth: (json['monthly_earnings'] as num?)?.toDouble() ?? 0,
totalWorkedHoursByWeek: (json['weekly_hours'] as num?)?.toDouble() ?? 0,
totalWorkedHoursByMonth: (json['monthly_hours'] as num?)?.toDouble() ?? 0,
payoutByWeek: 0,
payoutByMonth: 0,
startDatePeriod: DateTime(time.year, time.month),
endDatePeriod: DateTime(time.year, time.month, 28),
maxEarningInPeriod: 0,
minEarningInPeriod: 0,
);
}
final double totalEarningsByWeek;
final double totalEarningsByMonth;
final double totalWorkedHoursByWeek;
final double totalWorkedHoursByMonth;
final double payoutByWeek;
final double payoutByMonth;
final DateTime? startDatePeriod;
final DateTime? endDatePeriod;
final int maxEarningInPeriod;
final int minEarningInPeriod;
EarningsSummaryModel copyWith({
double? totalEarningsByWeek,
double? totalEarningsByMonth,
double? totalWorkedHoursByWeek,
double? totalWorkedHoursByMonth,
double? payoutByWeek,
double? payoutByMonth,
DateTime? startDatePeriod,
DateTime? endDatePeriod,
int? maxEarningInPeriod,
int? minEarningInPeriod,
}) {
return EarningsSummaryModel(
totalEarningsByWeek: totalEarningsByWeek ?? this.totalEarningsByWeek,
totalEarningsByMonth: totalEarningsByMonth ?? this.totalEarningsByMonth,
totalWorkedHoursByWeek:
totalWorkedHoursByWeek ?? this.totalWorkedHoursByWeek,
totalWorkedHoursByMonth:
totalWorkedHoursByMonth ?? this.totalWorkedHoursByMonth,
payoutByWeek: payoutByWeek ?? this.payoutByWeek,
payoutByMonth: payoutByMonth ?? this.payoutByMonth,
startDatePeriod: startDatePeriod,
endDatePeriod: endDatePeriod,
maxEarningInPeriod: maxEarningInPeriod ?? this.maxEarningInPeriod,
minEarningInPeriod: minEarningInPeriod ?? this.minEarningInPeriod,
);
}
}

View File

@@ -0,0 +1,88 @@
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 'package:krow/features/earning/data/earning_qgl.dart';
import 'package:krow/features/earning/data/models/earning_model.dart';
import 'package:krow/features/earning/data/models/earnings_summary_model.dart';
@injectable
class StaffEarningApiProvider {
StaffEarningApiProvider({required ApiClient client}) : _client = client;
final ApiClient _client;
Future<EarningsSummaryModel> fetchStaffEarningsSummary() async {
final QueryResult result = await _client.query(
schema: getWorkSummaryQuerySchema,
);
if (result.hasException) {
throw Exception(result.exception.toString());
}
return EarningsSummaryModel.fromJson(
result.data?['staff_work_summary'] as Map<String, dynamic>? ?? {},
);
}
Future<PaginationWrapper<EarningModel>> fetchEarnings({
required String status,
required int limit,
String? cursor,
}) async {
final QueryResult result = await _client.query(
schema: getPaymentsQuerySchema,
body: {'status': status, 'first': limit, 'after': cursor},
);
if (result.hasException) {
throw Exception(result.exception.toString());
}
return PaginationWrapper<EarningModel>.fromJson(
result.data?['staff_payments'] ?? {},
EarningModel.fromJson,
);
}
Future<EarningModel?> _processPaymentMutation({
required String schema,
required Map<String, dynamic> body,
required String mutationName,
}) async {
final QueryResult result = await _client.mutate(
schema: schema,
body: body,
);
if (result.hasException) throw Exception(result.exception.toString());
if (result.data == null) {
throw Exception('Payment data is missing on mutation $mutationName');
}
final jsonData = result.data?[mutationName] as Map<String, dynamic>;
return EarningModel.fromJson(jsonData);
}
Future<EarningModel?> confirmPayment({required String id}) {
return _processPaymentMutation(
schema: confirmPaymentMutationSchema,
body: {'id': id},
mutationName: 'confirm_staff_payment',
);
}
Future<EarningModel?> declinePayment({
required String id,
required String reason,
String? details,
}) {
return _processPaymentMutation(
schema: declinePaymentMutationSchema,
body: {'id': id, 'reason': reason, 'details': details},
mutationName: 'decline_staff_payment',
);
}
}

View File

@@ -0,0 +1,106 @@
import 'package:injectable/injectable.dart';
import 'package:krow/features/earning/data/models/earning_model.dart';
import 'package:krow/features/earning/data/staff_earning_api_provider.dart';
import 'package:krow/features/earning/domain/entities/earning_shift_entity.dart';
import 'package:krow/features/earning/domain/entities/earnings_batch_entity.dart';
import 'package:krow/features/earning/domain/entities/earnings_summary_entity.dart';
import 'package:krow/features/earning/domain/staff_earning_repository.dart';
@Injectable(as: StaffEarningRepository)
class StaffEarningRepositoryImpl implements StaffEarningRepository {
StaffEarningRepositoryImpl({
required StaffEarningApiProvider apiProvider,
}) : _apiProvider = apiProvider;
final StaffEarningApiProvider _apiProvider;
EarningShiftEntity _convertEarningModel(EarningModel data) {
return EarningShiftEntity(
id: data.id,
status: EarningStatus.fromString(data.status),
businessImageUrl: data.businessAvatar,
skillName: data.businessSkill,
businessName: data.businessName,
totalBreakTime: data.breakIn != null
? data.breakOut?.difference(data.breakIn!).inSeconds
: null,
paymentStatus: 1,
earned: data.amount,
clockIn: data.clockInAt,
clockOut: data.clockOutAt,
eventName: data.eventName,
eventDate: data.eventDate,
);
}
@override
Future<EarningsSummaryEntity> getStaffEarningsData() async {
final data = await _apiProvider.fetchStaffEarningsSummary();
return EarningsSummaryEntity(
totalEarningsByWeek: data.totalEarningsByWeek,
totalEarningsByMonth: data.totalEarningsByMonth,
totalWorkedHoursByWeek: data.totalWorkedHoursByWeek,
totalWorkedHoursByMonth: data.totalWorkedHoursByMonth,
payoutByWeek: data.payoutByWeek,
payoutByMonth: data.payoutByMonth,
startDatePeriod: data.startDatePeriod,
endDatePeriod: data.endDatePeriod,
maxEarningInPeriod: data.maxEarningInPeriod,
minEarningInPeriod: data.minEarningInPeriod,
);
}
@override
Future<EarningsBatchEntity> getEarningsBatch({
required String status,
int limit = 10,
String? lastEntryCursor,
}) async {
final paginationInfo = await _apiProvider.fetchEarnings(
status: status,
limit: limit,
cursor: lastEntryCursor,
);
return EarningsBatchEntity(
batchStatus: status,
hasNextBatch: paginationInfo.pageInfo?.hasNextPage??false,
cursor: paginationInfo.pageInfo?.endCursor ??
paginationInfo.edges.lastOrNull?.cursor,
earnings: paginationInfo.edges.map(
(edgeData) {
return _convertEarningModel(edgeData.node);
},
).toList(),
);
}
@override
Future<EarningShiftEntity?> confirmStaffEarning({
required String earningId,
}) async {
final result = await _apiProvider.confirmPayment(id: earningId);
if (result == null) return null;
return _convertEarningModel(result);
}
@override
Future<EarningShiftEntity?> disputeStaffEarning({
required String id,
required String reason,
String? details,
}) async {
final result = await _apiProvider.declinePayment(
id: id,
reason: reason,
details: details,
);
if (result == null) return null;
return _convertEarningModel(result);
}
}