@@ -20,6 +20,7 @@ export function LicenseModal({ open, onClose, showToast }: LicenseModalProps) {
2020 const [ licenseKey , setLicenseKey ] = React . useState ( '' ) ;
2121 const [ type , setType ] = React . useState ( '' ) ;
2222 const [ isLicenseValid , setIsLicenseValid ] = React . useState ( false ) ;
23+ const [ licenseChecked , setLicenseChecked ] = React . useState ( false ) ;
2324 const [ agent , setAgent ] = React . useState ( 'Ollama' ) ;
2425 const [ host , setHost ] = React . useState ( 'http://localhost:11434' ) ;
2526 const [ model , setModel ] = React . useState ( 'ministral-3:3b' ) ;
@@ -77,16 +78,20 @@ export function LicenseModal({ open, onClose, showToast }: LicenseModalProps) {
7778 } ;
7879 loadApiConfig ( ) ;
7980
80- // Check initial license state
81+ // Check initial license state (restore from cache synchronously first)
82+ LicenseManager . restoreFromCache ( ) ;
8183 if ( LicenseManager . hasActiveLicense ( ) ) {
8284 setIsLicenseValid ( true ) ;
85+ setType ( LicenseManager . getType ( ) ) ;
86+ setLicenseChecked ( true ) ;
8387 }
8488
8589 // Subscribe to license changes to update type
8690 const unsubscribe = LicenseManager . subscribe ( ( ) => {
8791 setIsLicenseValid ( LicenseManager . hasActiveLicense ( ) ) ;
8892 const updatedType = LicenseManager . getType ( ) ;
8993 setType ( updatedType ) ;
94+ setLicenseChecked ( true ) ;
9095 } ) ;
9196
9297 return ( ) => unsubscribe ( ) ;
@@ -140,6 +145,18 @@ export function LicenseModal({ open, onClose, showToast }: LicenseModalProps) {
140145 }
141146 } , [ isLicenseValid , licenseKey ] ) ;
142147
148+ // Derive license tier: Free, Premium, or PremiumPlus
149+ const licenseTier : 'Free' | 'Premium' | 'PremiumPlus' = isLicenseValid
150+ ? ( type === 'PremiumPlus' ? 'PremiumPlus' : 'Premium' )
151+ : 'Free' ;
152+
153+ // When license tier is Free, force agent to Ollama (only after initial license check)
154+ React . useEffect ( ( ) => {
155+ if ( licenseChecked && licenseTier === 'Free' && agent !== 'Ollama' ) {
156+ setAgent ( 'Ollama' ) ;
157+ }
158+ } , [ licenseChecked , licenseTier , agent ] ) ;
159+
143160 const handleSaveLicense = async ( ) => {
144161 localStorage . setItem ( 'easyeditor-user-name' , name ) ;
145162 await LicenseManager . setLicenseData ( email , licenseKey ) ;
@@ -326,8 +343,8 @@ export function LicenseModal({ open, onClose, showToast }: LicenseModalProps) {
326343 < div className = "about-card" >
327344 < h3 > { t ( 'about.easyai_credits' ) } </ h3 >
328345 < div style = { { marginTop : '10px' } } >
329- < p style = { { fontSize : '0.9em' , margin : '4px 0' } } > < strong > { t ( 'about.credits_agent' ) } </ strong > { isLicenseValid ? ( import . meta . env . VITE_PREMIUM_AGENT || 'Ollama' ) : agent } </ p >
330- < p style = { { fontSize : '0.9em' , margin : '4px 0' } } > < strong > { t ( 'about.credits_model' ) } </ strong > { isLicenseValid ? ( import . meta . env . VITE_PREMIUM_MODEL || 'ministral-3:3b' ) : model } </ p >
346+ < p style = { { fontSize : '0.9em' , margin : '4px 0' } } > < strong > { t ( 'about.credits_agent' ) } </ strong > { t ( 'about.query_built' ) } </ p >
347+ < p style = { { fontSize : '0.9em' , margin : '4px 0' } } > < strong > { t ( 'about.credits_model' ) } </ strong > { t ( 'about.query_built' ) } </ p >
331348 < p style = { { fontSize : '0.9em' , margin : '4px 0' } } > < strong > { t ( 'about.credits_monthly' ) } </ strong > { monthlyCredits !== null ? monthlyCredits : t ( 'about.query_built' ) } </ p >
332349 < p style = { { fontSize : '0.9em' , margin : '4px 0' } } > < strong > { t ( 'about.credits_topup' ) } </ strong > { topUpCredits !== null ? topUpCredits : t ( 'about.query_built' ) } </ p >
333350 < p style = { { fontSize : '0.9em' , margin : '4px 0' } } > < strong > { t ( 'about.credits_used' ) } </ strong > { usedCredits !== null ? usedCredits : t ( 'about.query_built' ) } </ p >
@@ -345,76 +362,98 @@ export function LicenseModal({ open, onClose, showToast }: LicenseModalProps) {
345362 </ div >
346363
347364 < div className = "about-card" style = { { flex : 1 } } >
348- < h3 > { t ( 'about.api_hosting' ) } </ h3 >
365+ < h3 > { t ( 'about.api_hosting' ) } ( { licenseTier === 'PremiumPlus' ? 'PremiumPlus' : licenseTier === 'Premium' ? 'Premium' : 'Free' } ) </ h3 >
349366 < div style = { { display : 'flex' , flexDirection : 'column' , gap : '3px' , marginTop : '10px' } } >
350367 < div >
351368 < label style = { { display : 'block' , fontSize : '0.9em' , marginBottom : '4px' } } > { t ( 'about.api_agent' ) } </ label >
352369 < select
353370 className = "license-name-input"
354371 style = { { width : '95%' , boxSizing : 'border-box' , padding : '4px' , borderRadius : '4px' , border : '1px solid var(--border-color, #ccc)' } }
355372 value = { agent }
356- onChange = { ( e ) => {
357- const selected = e . target . value ;
358- if ( selected === 'Premium' || selected === 'PremiumPlus' ) {
359- if ( showToast ) {
360- showToast ( t ( 'about.premium_not_implemented' ) , 'warning' ) ;
361- }
362- // Revert the select back to current agent
363- e . target . value = agent ;
364- return ;
365- }
366- setAgent ( selected ) ;
367- } }
373+ disabled = { licenseTier === 'Free' }
374+ onChange = { ( e ) => setAgent ( e . target . value ) }
368375 >
369376 < option value = "Ollama" > Ollama</ option >
370- < option value = "Gemini" > Gemini</ option >
371- < option value = "Bedrock" > Bedrock</ option >
372- < option value = "Claude" > Claude</ option >
373- < option value = "Premium" > Premium</ option >
374- < option value = "PremiumPlus" > PremiumPlus</ option >
377+ { licenseTier !== 'Free' && (
378+ < >
379+ < option value = "Gemini" > Gemini</ option >
380+ < option value = "Bedrock" > Bedrock</ option >
381+ < option value = "Claude" > Claude</ option >
382+ </ >
383+ ) }
384+ { licenseTier === 'PremiumPlus' && (
385+ < option value = "PremiumPlus" > PremiumPlus</ option >
386+ ) }
375387 </ select >
376388 </ div >
377- < div >
378- < label style = { { display : 'block' , fontSize : '0.9em' , marginBottom : '4px' } } > { t ( 'about.api_host' ) } </ label >
379- < input
380- type = "text"
381- className = "license-name-input"
382- style = { {
383- width : '95%' ,
384- boxSizing : 'border-box' ,
385- padding : '4px' ,
386- borderRadius : '4px' ,
387- border : '1px solid var(--border-color, #ccc)' ,
388- opacity : agent === 'Gemini' ? 0.6 : 1 ,
389- cursor : agent === 'Gemini' ? 'not-allowed' : 'text'
390- } }
391- placeholder = { t ( 'about.api_host_placeholder' ) }
392- readOnly = { agent === 'Gemini' }
393- value = { host }
394- onChange = { ( e ) => setHost ( e . target . value ) }
395- />
396- </ div >
389+ { agent === 'Ollama' && (
390+ < div >
391+ < label style = { { display : 'block' , fontSize : '0.9em' , marginBottom : '4px' } } > { t ( 'about.api_host' ) } </ label >
392+ < input
393+ type = "text"
394+ className = "license-name-input"
395+ style = { {
396+ width : '95%' ,
397+ boxSizing : 'border-box' ,
398+ padding : '4px' ,
399+ borderRadius : '4px' ,
400+ border : '1px solid var(--border-color, #ccc)' ,
401+ } }
402+ placeholder = { t ( 'about.api_host_placeholder' ) }
403+ value = { host }
404+ onChange = { ( e ) => setHost ( e . target . value ) }
405+ />
406+ </ div >
407+ ) }
397408 < div >
398409 < label style = { { display : 'block' , fontSize : '0.9em' , marginBottom : '4px' } } > { t ( 'about.api_model' ) } </ label >
399- < input
400- type = "text"
401- className = "license-name-input"
402- style = { { width : '95%' , boxSizing : 'border-box' , padding : '4px' , borderRadius : '4px' , border : '1px solid var(--border-color, #ccc)' } }
403- placeholder = { t ( 'about.api_model_placeholder' ) }
404- value = { model }
405- onChange = { ( e ) => setModel ( e . target . value ) }
406- />
410+ { agent === 'PremiumPlus' ? (
411+ < input
412+ type = "text"
413+ className = "license-name-input"
414+ style = { { width : '95%' , boxSizing : 'border-box' , padding : '4px' , borderRadius : '4px' , border : '1px solid var(--border-color, #ccc)' , opacity : 0.6 , cursor : 'not-allowed' } }
415+ readOnly
416+ value = "Coming soon"
417+ />
418+ ) : (
419+ < input
420+ type = "text"
421+ className = "license-name-input"
422+ style = { { width : '95%' , boxSizing : 'border-box' , padding : '4px' , borderRadius : '4px' , border : '1px solid var(--border-color, #ccc)' } }
423+ placeholder = { t ( 'about.api_model_placeholder' ) }
424+ value = { model }
425+ onChange = { ( e ) => setModel ( e . target . value ) }
426+ />
427+ ) }
407428 </ div >
408429 < div >
409430 < label style = { { display : 'block' , fontSize : '0.9em' , marginBottom : '4px' } } > { t ( 'about.api_key' ) } </ label >
410- < input
411- type = "password"
412- className = "license-name-input"
413- style = { { width : '95%' , boxSizing : 'border-box' , padding : '4px' , borderRadius : '4px' , border : '1px solid var(--border-color, #ccc)' } }
414- placeholder = { t ( 'about.api_key_placeholder' ) }
415- value = { apiKey }
416- onChange = { ( e ) => setApiKey ( e . target . value ) }
417- />
431+ { licenseTier === 'Free' ? (
432+ < input
433+ type = "text"
434+ className = "license-name-input"
435+ style = { { width : '95%' , boxSizing : 'border-box' , padding : '4px' , borderRadius : '4px' , border : '1px solid var(--border-color, #ccc)' , opacity : 0.6 , cursor : 'not-allowed' , fontStyle : 'italic' } }
436+ readOnly
437+ value = "Available with Premium subscription"
438+ />
439+ ) : agent === 'PremiumPlus' ? (
440+ < input
441+ type = "text"
442+ className = "license-name-input"
443+ style = { { width : '95%' , boxSizing : 'border-box' , padding : '4px' , borderRadius : '4px' , border : '1px solid var(--border-color, #ccc)' , opacity : 0.6 , cursor : 'not-allowed' } }
444+ readOnly
445+ value = "Coming soon"
446+ />
447+ ) : (
448+ < input
449+ type = "password"
450+ className = "license-name-input"
451+ style = { { width : '95%' , boxSizing : 'border-box' , padding : '4px' , borderRadius : '4px' , border : '1px solid var(--border-color, #ccc)' } }
452+ placeholder = { t ( 'about.api_key_placeholder' ) }
453+ value = { apiKey }
454+ onChange = { ( e ) => setApiKey ( e . target . value ) }
455+ />
456+ ) }
418457 </ div >
419458 < button
420459 onClick = { handleSaveApiConfig }
0 commit comments