From bbef5b376dc16423cb7511e3ebe0b28bbe0a30d6 Mon Sep 17 00:00:00 2001 From: Rishad7594 Date: Wed, 8 Apr 2026 19:18:33 +0530 Subject: [PATCH] ... --- analyze_output.txt | Bin 19802 -> 11234 bytes lib/core/api/api_client.dart | 15 +++++---- .../models/gamification_models.dart | 29 +++++++++++++----- .../providers/gamification_provider.dart | 26 ++++++++++++++-- lib/screens/contribute_screen.dart | 2 +- lib/screens/profile_screen.dart | 13 ++++---- 6 files changed, 62 insertions(+), 23 deletions(-) diff --git a/analyze_output.txt b/analyze_output.txt index 3bce1bebefbcf0de38d7b8788d92cf548a00ae3f..ab269ef7a9a798cd970c52d5f0f65d45cba3297d 100644 GIT binary patch literal 11234 zcmeHN%Wl(95S=v=|6o=D2`ch_>;NG)l|U`9LW&Z{X(OdhWv7(z@xVDVwv&`Rn%D`V zy^7q}u{(EsJZGN%{p*c{(vz{we`O(fnxQ$80zL4dY5TSAN4UI8PXp_Y#Wp_u-CnD2O&n;hZEl#YEzBGrd)iR zXG|Swf^ALpP37F2v&@S*+fe;6JzWXWnNj;vK59ht^=PJ^+#)!ihvgIW!)b1l=2J2L zbE0|30CE@eD)Z|D&G^0~KK>+I?JH!BH4}*Cxx9cTQ`LzPV*(wRKc%Zu56feiPfc*d z%%X_wss)}B-MX|AiL36mi)tfa*{0_B7EUHp3gP~1WRtGk;Kckd-;}HpY?=_u%5k z-yI@i*HBNlm_s{m=H(_aWrbS@x)zmo!!$oP-)l8byFZ8h`x%GhEYmy&L&?wjP7KX| z=cn*P>^-t4+Cxqo$SF2fZ=duGy}~iQ6e|k)Y3HT(@5+|p>03aM{n@muwi}~d<#4xh zC~KZ;y@2*!Udmy*3R7k!?Ag`!!t@aS?+h84%~k%52W-L8oCnOCQ?yY-vC^N<6?XH0 zn=I?$^fxQWIGV~pH^Eyq6XxWO2$7F&hPQfdzKGGBZHJjG{J&E?+EEJ|(Q1OA zHCguRCV86}XBfFz-hJOyF$S{FP+jUXxVKg8{U>8y8`!Y=PHUXQ_;Kc9+dHNo(1Ex@ zHqJMyjMxQF9{D}-fQMsiuDGB4MU#XKKZVcOp`qh;ms)O&eQ1{gl_GNJzR^%A#Ul`ibskZ`EIugcp@+C`&IdW`b9BADKnq@^18rvGjC`kQpN`_sH}glU!LGl_1FZ_;Z1 z5wj$%R_e_p5z9ivyu#peNVS~HI!o4qR!vUGr&5rKdhAyPwS4iCRFd#(nI=R%nwFR& zB$H*i9{hfE)MmbqUw&yfT8PCZrYc%93Mge^H~^rIN0~eHcL8|d6FtMsumm1ZuqJsi zMXG@)mWw4X(o*JI%1SAe!vmsf{Tc(vnKIio43MkVNg%o)bAvR#K!6$=hv0=O=tdI#||wn{Tsm9jF;>DEB?1LhB7t~%3z zQNy6lC_h(n*)gq75Hia(Y-MTDy3;8O6d_}f8qk8J)G^P)(xUbJkWoET(2Z-K z-SyYP@_m;%tv{u8|4)a_W6mR?Nf+C*h2Q$|3gOh9h!f}q^+aA^{MDG;q*+Gh{92G) z5;2>Bj|RgWb8vwTdjft@q$<$8qgPZ^4DFy;-VM>%1IlBzGJc&2!Er#jPn%Xp)Da;<*=g%@Yn8-F{4VX}3#Q(N)z~oXCuh2Q!1{e)yDIK@ zVh?OYTCMjw6saRL2oxPT1otIC>YxW~SL%n?KtXsqvRl700LnPS7KEXZhAo8iZk%CL zpo}$a3f#a9TZp9nnyxqBu!WY@2ezx?j%V0H#~zqr3qjBI*b1|JuaG{$uq7hSc)^<- z?KrxW^7EN0$$3|O$P;n9DZePhOcWxIALQB^%duVaVOimJ1%}AP0{b$HwGfl6fsnW)3EM%Dh%=dt!QCpK@Df4wBuEX5mk# zv^r41&zhF`daxv24+hJyoYw=8)JC>TPjH$cZiamU#eP9 zR17XqMx-qdd^kST16|$lVXCwbWrlcRw0WT+7K$PbWTbn!y=G|uRcBaQ?SZ(xscZg7 zdZ5Y7A4wm@3Hp+c?wUQEQS&W(N7&z64S1N4W_v04%A+*I>RVHbGOI1;*4Im#VF#?K zZ8NIRbap4z;pJ#eojVTrPS50)ej4M%{&#_Y#Aak* zXwv58Cd5=321Je9g}OL#qB-DUyLGjFcPqMV&nxIQ+6`*4z2O5kRd1TnMzXsav%~dy z6DGv6y8)uwOh1LYT2k~tD_ii|TyQ8sF(%nme9~@8PcJ7bo+3MbR zk59mC6Q$9F zK*0>GD`j?(7+sZm*oo(2A++gt!;(`w1?&YZ(EF&}aqD$at_rv)D1qCU?0r20wdcdI zYK0Q~b3E_3PZm6w9$$X6K6{HWVqq^*T9!CNiHw@_ttR9J&W=&%anXrgz8t-);4bBY zStOqGf?1_98f4 z46hsgz2l<_KgcWc9*00($zt_EofJh#b5-5Jhmjv;b-onM)i{NdmN4>S-5=Z^5!I>f zKB=chn~t$_9Qlx6y;K*;=(pcrlb^*xR^se77R%;ZvkgoA?-ltDnt1h7&9kPN2F6)Y b$l^nyV4zGBo5j@C_j0ko!8^^p5gGjtp0Y|# diff --git a/lib/core/api/api_client.dart b/lib/core/api/api_client.dart index a64de9b..8aaa314 100644 --- a/lib/core/api/api_client.dart +++ b/lib/core/api/api_client.dart @@ -109,21 +109,24 @@ class ApiClient { bool requiresAuth = true, }) async { // build final query params including auth if needed - final Map finalParams = {}; + final originalUri = Uri.parse(url); + final queryParams = {...originalUri.queryParameters}; if (requiresAuth) { final token = await TokenStorage.getToken(); final username = await TokenStorage.getUsername(); if (token != null && username != null) { - finalParams['token'] = token; - finalParams['username'] = username; + queryParams['token'] = token; + queryParams['username'] = username; } // Guest mode: proceed without token — let backend decide } - if (params != null) finalParams.addAll(params); + if (params != null) { + queryParams.addAll(params.map((k, v) => MapEntry(k, v?.toString() ?? ''))); + } - final uri = Uri.parse(url).replace(queryParameters: finalParams.map((k, v) => MapEntry(k, v?.toString()))); + final uri = originalUri.replace(queryParameters: queryParams); late http.Response response; try { @@ -133,7 +136,7 @@ class ApiClient { throw Exception('Network error: $e'); } - return _handleResponse(url, response, finalParams); + return _handleResponse(url, response, queryParams); } // --------------------------------------------------------------------------- diff --git a/lib/features/gamification/models/gamification_models.dart b/lib/features/gamification/models/gamification_models.dart index ccabc0c..6971aef 100644 --- a/lib/features/gamification/models/gamification_models.dart +++ b/lib/features/gamification/models/gamification_models.dart @@ -1,6 +1,8 @@ // lib/features/gamification/models/gamification_models.dart // Data models matching TechDocs v2 DB schema for the Contributor Module. +import 'package:flutter/foundation.dart'; + // --------------------------------------------------------------------------- // Tier enum — matches PRD v3 §4.4 thresholds (based on Lifetime EP) // --------------------------------------------------------------------------- @@ -68,13 +70,21 @@ int tierStartEp(ContributorTier tier) { // --------------------------------------------------------------------------- class UserGamificationProfile { final String userId; - final int lifetimeEp; // Never resets. Used for tiers + leaderboard. - final int currentEp; // Liquid EP accumulated this month. - final int currentRp; // Spendable Reward Points. + final String username; + final String? avatarUrl; + final String? district; + final String? eventifyId; + final int lifetimeEp; // Never resets. Used for tiers + leaderboard. + final int currentEp; // Liquid EP accumulated this month. + final int currentRp; // Spendable Reward Points. final ContributorTier tier; const UserGamificationProfile({ required this.userId, + required this.username, + this.avatarUrl, + this.district, + this.eventifyId, required this.lifetimeEp, required this.currentEp, required this.currentRp, @@ -82,12 +92,17 @@ class UserGamificationProfile { }); factory UserGamificationProfile.fromJson(Map json) { - final ep = (json['lifetime_ep'] as int?) ?? 0; + debugPrint('Mapping UserGamificationProfile from JSON: $json'); + final ep = (json['lifetime_ep'] as int?) ?? (json['points'] as int?) ?? (json['total_points'] as int?) ?? 0; return UserGamificationProfile( - userId: json['user_id'] as String? ?? '', + userId: (json['user_id'] ?? json['email'] ?? json['userId'] ?? '').toString(), + username: (json['username'] ?? json['name'] ?? json['full_name'] ?? json['display_name'] ?? '').toString(), + avatarUrl: json['profile_image'] as String? ?? json['avatar_url'] as String? ?? json['profile_pic'] as String?, + district: json['district'] as String? ?? json['location'] as String?, + eventifyId: (json['eventify_id'] ?? json['eventifyId'] ?? json['id'] ?? '').toString(), lifetimeEp: ep, - currentEp: (json['current_ep'] as int?) ?? 0, - currentRp: (json['current_rp'] as int?) ?? 0, + currentEp: (json['current_ep'] as int?) ?? (json['monthly_points'] as int?) ?? (json['points_this_month'] as int?) ?? 0, + currentRp: (json['current_rp'] as int?) ?? (json['reward_points'] as int?) ?? (json['rp'] as int?) ?? 0, tier: tierFromEp(ep), ); } diff --git a/lib/features/gamification/providers/gamification_provider.dart b/lib/features/gamification/providers/gamification_provider.dart index 74eeaf4..eb75b5d 100644 --- a/lib/features/gamification/providers/gamification_provider.dart +++ b/lib/features/gamification/providers/gamification_provider.dart @@ -36,8 +36,10 @@ class GamificationProvider extends ChangeNotifier { // Load everything at once (called when ContributeScreen or ProfileScreen mounts) // --------------------------------------------------------------------------- Future loadAll({bool force = false}) async { - // Skip if recently loaded (within 2 minutes) unless forced - if (!force && _lastLoadTime != null && DateTime.now().difference(_lastLoadTime!) < _loadTtl) { + debugPrint('GamificationProvider.loadAll(force: $force) called'); + // Skip if recently loaded (within 2 minutes) unless forced or profile is null + if (!force && profile != null && _lastLoadTime != null && DateTime.now().difference(_lastLoadTime!) < _loadTtl) { + debugPrint('GamificationProvider.loadAll skipped due to TTL'); return; } @@ -46,10 +48,20 @@ class GamificationProvider extends ChangeNotifier { notifyListeners(); try { + debugPrint('GamificationProvider: Requesting dashboard, leaderboard, etc...'); final results = await Future.wait([ _service.getDashboard().catchError((e) { debugPrint('Dashboard error: $e'); - return const DashboardResponse(profile: UserGamificationProfile(userId: '', lifetimeEp: 0, currentEp: 0, currentRp: 0, tier: ContributorTier.BRONZE)); + return const DashboardResponse( + profile: UserGamificationProfile( + userId: '', + username: '', + lifetimeEp: 0, + currentEp: 0, + currentRp: 0, + tier: ContributorTier.BRONZE, + ), + ); }), _service.getLeaderboard(district: leaderboardDistrict, timePeriod: leaderboardTimePeriod).catchError((e) { debugPrint('Leaderboard error: $e'); @@ -179,6 +191,10 @@ class GamificationProvider extends ChangeNotifier { if (profile != null) { profile = UserGamificationProfile( userId: profile!.userId, + username: profile!.username, + avatarUrl: profile!.avatarUrl, + district: profile!.district, + eventifyId: profile!.eventifyId, lifetimeEp: profile!.lifetimeEp, currentEp: profile!.currentEp, currentRp: profile!.currentRp - item.rpCost, @@ -195,6 +211,10 @@ class GamificationProvider extends ChangeNotifier { if (profile != null) { profile = UserGamificationProfile( userId: profile!.userId, + username: profile!.username, + avatarUrl: profile!.avatarUrl, + district: profile!.district, + eventifyId: profile!.eventifyId, lifetimeEp: profile!.lifetimeEp, currentEp: profile!.currentEp, currentRp: profile!.currentRp + item.rpCost, diff --git a/lib/screens/contribute_screen.dart b/lib/screens/contribute_screen.dart index 4e4b348..1522466 100644 --- a/lib/screens/contribute_screen.dart +++ b/lib/screens/contribute_screen.dart @@ -1785,7 +1785,7 @@ class _ContributeScreenState extends State try { final data = { - 'title': _titleCtl.text.trim(), + 'event_name': _titleCtl.text.trim(), 'category': _selectedCategory, 'district': _selectedDistrict, 'date': _selectedDate!.toIso8601String(), diff --git a/lib/screens/profile_screen.dart b/lib/screens/profile_screen.dart index aa78c74..90ccbb4 100644 --- a/lib/screens/profile_screen.dart +++ b/lib/screens/profile_screen.dart @@ -111,7 +111,7 @@ class _ProfileScreenState extends State // Load gamification data for profile EP cards WidgetsBinding.instance.addPostFrameCallback((_) { - if (mounted) context.read().loadAll(); + if (mounted) context.read().loadAll(force: true); }); } @@ -829,7 +829,7 @@ class _ProfileScreenState extends State // Username Text( - _username, + (p?.username.isNotEmpty == true) ? p!.username : _username, style: const TextStyle( color: Colors.white, fontSize: 24, @@ -840,10 +840,11 @@ class _ProfileScreenState extends State const SizedBox(height: 12), // Eventify ID Badge - if (_eventifyId != null) + if ((p?.eventifyId ?? _eventifyId) != null) GestureDetector( onTap: () { - Clipboard.setData(ClipboardData(text: _eventifyId!)); + final id = p?.eventifyId ?? _eventifyId!; + Clipboard.setData(ClipboardData(text: id)); ScaffoldMessenger.of(context).showSnackBar( const SnackBar(content: Text('ID copied to clipboard')), ); @@ -861,7 +862,7 @@ class _ProfileScreenState extends State const Icon(Icons.copy, size: 14, color: Colors.white70), const SizedBox(width: 8), Text( - _eventifyId!, + p?.eventifyId ?? _eventifyId!, style: const TextStyle( color: Colors.white, fontSize: 13, @@ -882,7 +883,7 @@ class _ProfileScreenState extends State const Icon(Icons.location_on_outlined, color: Colors.white, size: 18), const SizedBox(width: 4), Text( - _district ?? 'No district selected', + p?.district ?? _district ?? 'No district selected', style: const TextStyle(color: Colors.white, fontSize: 16, fontWeight: FontWeight.w400), ), ],