@@ -183,6 +183,66 @@ define(function (require, exports, module) {
183183 _setupDocumentClickHandler ( ) ;
184184 }
185185
186+ // Cached entitlements data
187+ let cachedEntitlements = null ;
188+
189+ /**
190+ * Get entitlements from API or cache
191+ * Returns null if user is not logged in
192+ */
193+ async function getEntitlements ( forceRefresh = false ) {
194+ // Return null if not logged in
195+ if ( ! KernalModeTrust . loginService . isLoggedIn ( ) ) {
196+ return null ;
197+ }
198+
199+ // Return cached data if available and not forcing refresh
200+ if ( cachedEntitlements && ! forceRefresh ) {
201+ return cachedEntitlements ;
202+ }
203+
204+ try {
205+ const accountBaseURL = KernalModeTrust . loginService . getAccountBaseURL ( ) ;
206+ const language = Phoenix . app && Phoenix . app . language ? Phoenix . app . language : 'en' ;
207+ let url = `${ accountBaseURL } /getAppEntitlements?lang=${ language } ` ;
208+ let fetchOptions = {
209+ method : 'GET' ,
210+ headers : {
211+ 'Accept' : 'application/json'
212+ }
213+ } ;
214+
215+ // Handle different authentication methods for browser vs desktop
216+ if ( Phoenix . isNativeApp ) {
217+ // Desktop app: use appSessionID and validationCode
218+ const profile = KernalModeTrust . loginService . getProfile ( ) ;
219+ if ( profile && profile . apiKey && profile . validationCode ) {
220+ url += `&appSessionID=${ encodeURIComponent ( profile . apiKey ) } &validationCode=${ encodeURIComponent ( profile . validationCode ) } ` ;
221+ } else {
222+ console . error ( 'Missing appSessionID or validationCode for desktop app entitlements' ) ;
223+ return null ;
224+ }
225+ } else {
226+ // Browser app: use session cookies
227+ fetchOptions . credentials = 'include' ;
228+ }
229+
230+ const response = await fetch ( url , fetchOptions ) ;
231+
232+ if ( response . ok ) {
233+ const result = await response . json ( ) ;
234+ if ( result . isSuccess ) {
235+ cachedEntitlements = result ;
236+ return cachedEntitlements ;
237+ }
238+ }
239+ } catch ( error ) {
240+ console . error ( 'Failed to fetch entitlements:' , error ) ;
241+ }
242+
243+ return null ;
244+ }
245+
186246 let userEmail = "" ;
187247 class SecureEmail extends HTMLElement {
188248 constructor ( ) {
@@ -284,6 +344,50 @@ define(function (require, exports, module) {
284344 }
285345 }
286346
347+ /**
348+ * Update popup content with entitlements data
349+ */
350+ function _updatePopupWithEntitlements ( entitlements ) {
351+ if ( ! $popup || ! entitlements ) {
352+ return ;
353+ }
354+
355+ // Update plan information
356+ if ( entitlements . plan ) {
357+ const $planName = $popup . find ( '.user-plan-name' ) ;
358+ $planName . text ( entitlements . plan . name ) ;
359+
360+ // Update plan class based on paid subscriber status
361+ $planName . removeClass ( 'user-plan-free user-plan-paid' ) ;
362+ const planClass = entitlements . plan . paidSubscriber ? 'user-plan-paid' : 'user-plan-free' ;
363+ $planName . addClass ( planClass ) ;
364+ }
365+
366+ // Update quota section if available
367+ if ( entitlements . profileview && entitlements . profileview . quota ) {
368+ const $quotaSection = $popup . find ( '.quota-section' ) ;
369+ const quota = entitlements . profileview . quota ;
370+
371+ // Remove forced-hidden and show quota section
372+ $quotaSection . removeClass ( 'forced-hidden' ) ;
373+
374+ // Update quota content
375+ $quotaSection . find ( '.titleText' ) . text ( quota . titleText ) ;
376+ $quotaSection . find ( '.usageText' ) . text ( quota . usageText ) ;
377+ $quotaSection . find ( '.progress-fill' ) . css ( 'width' , quota . usedPercent + '%' ) ;
378+ }
379+
380+ // Update HTML message if available
381+ if ( entitlements . profileview && entitlements . profileview . htmlMessage ) {
382+ const $htmlMessageSection = $popup . find ( '.html-message' ) ;
383+ $htmlMessageSection . removeClass ( 'forced-hidden' ) ;
384+ $htmlMessageSection . html ( entitlements . profileview . htmlMessage ) ;
385+ }
386+
387+ // Reposition popup after content changes
388+ positionPopup ( ) ;
389+ }
390+
287391 /**
288392 * Shows the user profile popup when the user is logged in
289393 */
@@ -296,24 +400,36 @@ define(function (require, exports, module) {
296400 const profileData = KernalModeTrust . loginService . getProfile ( ) ;
297401 userEmail = profileData . email ;
298402 userName = profileData . firstName + " " + profileData . lastName ;
403+
404+ // Default template data (fallback) - start with cached plan info if available
299405 const templateData = {
300406 initials : profileData . profileIcon . initials ,
301407 avatarColor : profileData . profileIcon . color ,
302- planClass : "user-plan-free" , // "user-plan-paid" for paid plan
408+ planClass : "user-plan-free" ,
303409 planName : "Free Plan" ,
304- quotaUsed : "7,000" ,
305- quotaTotal : "10,000" ,
306- quotaUnit : "tokens" ,
307- quotaPercent : 70 ,
410+ titleText : "Ai Quota Used" ,
411+ usageText : "100 / 200 credits" ,
412+ usedPercent : 0 ,
308413 Strings : Strings
309414 } ;
310415
311- // Render template with data
416+ // Check for cached entitlements synchronously for immediate basic plan info
417+ if ( cachedEntitlements && cachedEntitlements . plan ) {
418+ templateData . planClass = cachedEntitlements . plan . paidSubscriber ? "user-plan-paid" : "user-plan-free" ;
419+ templateData . planName = cachedEntitlements . plan . name ;
420+ }
421+
422+ // Render template with data immediately
312423 const renderedTemplate = Mustache . render ( profileTemplate , templateData ) ;
313424 $popup = $ ( renderedTemplate ) ;
314425
315426 $ ( "body" ) . append ( $popup ) ;
316427 isPopupVisible = true ;
428+
429+ // Apply cached entitlements immediately if available (including quota/messages)
430+ if ( cachedEntitlements ) {
431+ _updatePopupWithEntitlements ( cachedEntitlements ) ;
432+ }
317433 positionPopup ( ) ;
318434
319435 PopUpManager . addPopUp ( $popup , function ( ) {
@@ -347,6 +463,26 @@ define(function (require, exports, module) {
347463
348464 // Load user details iframe for browser apps (after popup is created)
349465 _loadUserDetailsIframe ( ) ;
466+
467+ // Refresh entitlements in background and update popup if still visible
468+ _refreshEntitlementsInBackground ( ) ;
469+ }
470+
471+ /**
472+ * Refresh entitlements in background and update popup if still visible
473+ */
474+ async function _refreshEntitlementsInBackground ( ) {
475+ try {
476+ // Fetch fresh entitlements from API
477+ const freshEntitlements = await getEntitlements ( true ) ; // Force refresh to get latest data
478+
479+ // Only update popup if it's still visible
480+ if ( isPopupVisible && $popup && freshEntitlements ) {
481+ _updatePopupWithEntitlements ( freshEntitlements ) ;
482+ }
483+ } catch ( error ) {
484+ console . error ( 'Failed to refresh entitlements in background:' , error ) ;
485+ }
350486 }
351487
352488 /**
@@ -421,6 +557,9 @@ define(function (require, exports, module) {
421557 closePopup ( ) ;
422558 }
423559 _removeProfileIcon ( ) ;
560+
561+ // Clear cached entitlements when user logs out
562+ cachedEntitlements = null ;
424563 }
425564
426565 function setLoggedIn ( initial , color ) {
@@ -429,9 +568,15 @@ define(function (require, exports, module) {
429568 closePopup ( ) ;
430569 }
431570 _updateProfileIcon ( initial , color ) ;
571+
572+ // Preload entitlements when user logs in
573+ getEntitlements ( ) . catch ( error => {
574+ console . error ( 'Failed to preload entitlements on login:' , error ) ;
575+ } ) ;
432576 }
433577
434578 exports . init = init ;
435579 exports . setNotLoggedIn = setNotLoggedIn ;
436580 exports . setLoggedIn = setLoggedIn ;
581+ exports . getEntitlements = getEntitlements ;
437582} ) ;
0 commit comments