Implement verified curriculum, video review, watch sessions, ledger safeguards, and remove demo data

This commit is contained in:
Hamza-Ayed
2026-09-09 17:04:08 +03:00
parent 0dc5df7b5e
commit 5af684a892
49 changed files with 1809 additions and 3005 deletions
@@ -28,6 +28,8 @@ class TeacherMonetizationState {
final List<TeacherPayoutRequestModel> payoutRequests;
final bool isSubmittingPayout;
final bool isLoading;
final bool financialUnavailable;
final String financialMessage;
const TeacherMonetizationState({
required this.institutionalStudents,
@@ -44,6 +46,8 @@ class TeacherMonetizationState {
required this.payoutRequests,
required this.isSubmittingPayout,
this.isLoading = false,
this.financialUnavailable = false,
this.financialMessage = '',
});
TeacherMonetizationState copyWith({
@@ -61,6 +65,8 @@ class TeacherMonetizationState {
List<TeacherPayoutRequestModel>? payoutRequests,
bool? isSubmittingPayout,
bool? isLoading,
bool? financialUnavailable,
String? financialMessage,
}) {
return TeacherMonetizationState(
institutionalStudents:
@@ -78,6 +84,8 @@ class TeacherMonetizationState {
payoutRequests: payoutRequests ?? this.payoutRequests,
isSubmittingPayout: isSubmittingPayout ?? this.isSubmittingPayout,
isLoading: isLoading ?? this.isLoading,
financialUnavailable: financialUnavailable ?? this.financialUnavailable,
financialMessage: financialMessage ?? this.financialMessage,
);
}
}
@@ -116,16 +124,21 @@ class TeacherMonetizationCubit extends Cubit<TeacherMonetizationState> {
final mkt =
audience['marketplace_students'] as Map<String, dynamic>? ?? {};
final wallet = data['wallet'] as Map<String, dynamic>? ?? {};
final unavailable = data['financial_status'] == 'unavailable';
final instCount = (inst['count'] as num?)?.toDouble() ?? 0.0;
final paidCount = (mkt['count'] as num?)?.toDouble() ?? 0.0;
final gross =
(data['gross_revenue_jod'] as num?)?.toDouble() ?? (paidCount * 20.0);
final tShare = gross * 0.55;
final dShare = gross * 0.15;
final pShare = gross * 0.30;
final split = data['revenue_split'] as Map<String, dynamic>? ?? {};
// Never infer a wallet from subscriber counts, course prices, or old
// percentages. The backend returns null until a provider-backed ledger
// and eligible watch-minute policy are live.
final gross = (data['gross_revenue_jod'] as num?)?.toDouble() ?? 0.0;
final tShare = (split['teacher_amount_jod'] as num?)?.toDouble() ?? 0.0;
final dShare =
(split['directorate_amount_jod'] as num?)?.toDouble() ?? 0.0;
final pShare = (split['platform_amount_jod'] as num?)?.toDouble() ?? 0.0;
final avail =
(wallet['available_balance_jod'] as num?)?.toDouble() ?? tShare;
(wallet['available_balance_jod'] as num?)?.toDouble() ?? 0.0;
final withdrawn =
(wallet['total_withdrawn_jod'] as num?)?.toDouble() ?? 0.0;
final alias = wallet['cliq_payout_alias']?.toString() ?? '';
@@ -154,6 +167,8 @@ class TeacherMonetizationCubit extends Cubit<TeacherMonetizationState> {
cliqAlias: alias,
courses: courses,
payoutRequests: payouts,
financialUnavailable: unavailable,
financialMessage: data['financial_message']?.toString() ?? '',
isLoading: false,
));
} catch (_) {
@@ -162,15 +177,8 @@ class TeacherMonetizationCubit extends Cubit<TeacherMonetizationState> {
}
void updateSubscribers(double subscribers) {
final gross = subscribers * state.pricePerCourse;
emit(state.copyWith(
studentSubscribers: subscribers,
grossRevenue: gross,
teacherShare: gross * 0.55,
directorateShare: gross * 0.15,
platformShare: gross * 0.30,
availableBalance: gross * 0.55,
));
// Presentation must not fabricate revenue from a manually changed count.
emit(state.copyWith(studentSubscribers: subscribers));
}
Future<void> submitCliqPayout({
@@ -186,14 +194,8 @@ class TeacherMonetizationCubit extends Cubit<TeacherMonetizationState> {
final updatedList =
List<TeacherPayoutRequestModel>.from(state.payoutRequests)
..insert(0, req);
final newAvail =
(state.availableBalance - amountJod).clamp(0.0, double.infinity);
final newWithdrawn = state.totalWithdrawn + amountJod;
emit(state.copyWith(
payoutRequests: updatedList,
availableBalance: newAvail,
totalWithdrawn: newWithdrawn,
isSubmittingPayout: false,
));
} catch (_) {