fix: Yield dashboard + Forge UI improvements
Some checks failed
CI / Frontend Lint & Type Check (push) Has been cancelled
CI / Frontend Build (push) Has been cancelled
CI / Backend Lint (push) Has been cancelled
CI / Backend Tests (push) Has been cancelled
CI / Docker Build (push) Has been cancelled
CI / Security Scan (push) Has been cancelled
Deploy / Build & Push Images (push) Has been cancelled
Deploy / Deploy to Server (push) Has been cancelled
Deploy / Notify (push) Has been cancelled

Yield:
- Safely handle partner relationship to prevent errors

Forge:
- AI mode now default for Trader/Tycoon users
- AI mode button moved to left (primary position)
- "Describe your..." label now bright purple
- Textarea border brighter for better visibility
This commit is contained in:
2025-12-18 15:37:24 +01:00
parent 81eeceb856
commit 433b0d6ebd
2 changed files with 42 additions and 34 deletions

View File

@ -774,6 +774,14 @@ async def list_partners(
def _domain_to_response(domain: YieldDomain) -> YieldDomainResponse: def _domain_to_response(domain: YieldDomain) -> YieldDomainResponse:
"""Convert YieldDomain model to response schema.""" """Convert YieldDomain model to response schema."""
# Safely get partner name
partner_name = None
try:
if domain.partner:
partner_name = domain.partner.name
except Exception:
pass
return YieldDomainResponse( return YieldDomainResponse(
id=domain.id, id=domain.id,
domain=domain.domain, domain=domain.domain,
@ -781,7 +789,7 @@ def _domain_to_response(domain: YieldDomain) -> YieldDomainResponse:
detected_intent=domain.detected_intent, detected_intent=domain.detected_intent,
intent_confidence=domain.intent_confidence, intent_confidence=domain.intent_confidence,
active_route=domain.active_route, active_route=domain.active_route,
partner_name=domain.partner.name if domain.partner else None, partner_name=partner_name,
landing_template=getattr(domain, "landing_template", None), landing_template=getattr(domain, "landing_template", None),
landing_headline=getattr(domain, "landing_headline", None), landing_headline=getattr(domain, "landing_headline", None),
landing_intro=getattr(domain, "landing_intro", None), landing_intro=getattr(domain, "landing_intro", None),

View File

@ -47,8 +47,8 @@ export function BrandableForgeTab({ showToast }: { showToast: (msg: string, type
const tier = (subscription?.tier || '').toLowerCase() const tier = (subscription?.tier || '').toLowerCase()
const hasAI = tier === 'trader' || tier === 'tycoon' const hasAI = tier === 'trader' || tier === 'tycoon'
// Mode selection // Mode selection - AI mode by default if available
const [mode, setMode] = useState<'pattern' | 'ai'>('pattern') const [mode, setMode] = useState<'pattern' | 'ai'>(hasAI ? 'ai' : 'pattern')
// Config // Config
const [pattern, setPattern] = useState('cvcvc') const [pattern, setPattern] = useState('cvcvc')
@ -143,7 +143,35 @@ export function BrandableForgeTab({ showToast }: { showToast: (msg: string, type
{/* Mode Selector */} {/* Mode Selector */}
<div className="grid grid-cols-1 sm:grid-cols-2 gap-3"> <div className="grid grid-cols-1 sm:grid-cols-2 gap-3">
{/* Pattern Mode */} {/* AI Mode - First/Left */}
<button
onClick={() => hasAI && setMode('ai')}
disabled={!hasAI}
className={clsx(
"relative p-5 border transition-all duration-300 text-left group overflow-hidden",
!hasAI && "opacity-50 grayscale",
mode === 'ai'
? "border-purple-500 bg-purple-500/5 shadow-[0_0_30px_-10px_rgba(168,85,247,0.3)]"
: "border-white/[0.08] bg-white/[0.01] hover:border-white/20 hover:bg-white/[0.03]"
)}
>
{!hasAI && <div className="absolute top-2 right-2"><Lock className="w-3 h-3 text-white/20" /></div>}
<div className="flex items-center gap-4 relative z-10">
<div className={clsx(
"w-12 h-12 flex items-center justify-center border transition-colors",
mode === 'ai' ? "border-purple-500/40 bg-purple-500/10" : "border-white/10 bg-white/5"
)}>
<Brain className={clsx("w-6 h-6", mode === 'ai' ? "text-purple-400" : "text-white/30")} />
</div>
<div>
<p className={clsx("font-bold font-mono text-sm tracking-tight", mode === 'ai' ? "text-purple-400" : "text-white/70")}>Vision Core AI</p>
<p className="text-[10px] font-mono text-white/30 uppercase tracking-widest mt-0.5">Concept to Name</p>
</div>
</div>
{mode === 'ai' && <div className="absolute top-0 right-0 w-2 h-2 bg-purple-500" />}
</button>
{/* Pattern Mode - Second/Right */}
<button <button
onClick={() => setMode('pattern')} onClick={() => setMode('pattern')}
className={clsx( className={clsx(
@ -167,34 +195,6 @@ export function BrandableForgeTab({ showToast }: { showToast: (msg: string, type
</div> </div>
{mode === 'pattern' && <div className="absolute top-0 right-0 w-2 h-2 bg-accent" />} {mode === 'pattern' && <div className="absolute top-0 right-0 w-2 h-2 bg-accent" />}
</button> </button>
{/* AI Mode */}
<button
onClick={() => hasAI && setMode('ai')}
disabled={!hasAI}
className={clsx(
"relative p-5 border transition-all duration-300 text-left group overflow-hidden",
!hasAI && "opacity-50 grayscale",
mode === 'ai'
? "border-purple-500 bg-purple-500/5 shadow-[0_0_30px_-10px_rgba(168,85,247,0.2)]"
: "border-white/[0.08] bg-white/[0.01] hover:border-white/20 hover:bg-white/[0.03]"
)}
>
{!hasAI && <div className="absolute top-2 right-2"><Lock className="w-3 h-3 text-white/20" /></div>}
<div className="flex items-center gap-4 relative z-10">
<div className={clsx(
"w-12 h-12 flex items-center justify-center border transition-colors",
mode === 'ai' ? "border-purple-500/30 bg-purple-500/10" : "border-white/10 bg-white/5"
)}>
<Brain className={clsx("w-6 h-6", mode === 'ai' ? "text-purple-400" : "text-white/30")} />
</div>
<div>
<p className={clsx("font-bold font-mono text-sm tracking-tight", mode === 'ai' ? "text-purple-400" : "text-white/70")}>Vision Core AI</p>
<p className="text-[10px] font-mono text-white/30 uppercase tracking-widest mt-0.5">Concept to Name</p>
</div>
</div>
{mode === 'ai' && <div className="absolute top-0 right-0 w-2 h-2 bg-purple-500" />}
</button>
</div> </div>
{/* Config Panel */} {/* Config Panel */}
@ -236,13 +236,13 @@ export function BrandableForgeTab({ showToast }: { showToast: (msg: string, type
</div> </div>
) : ( ) : (
<div className="space-y-4"> <div className="space-y-4">
<span className="text-[10px] font-mono text-purple-300/40 uppercase tracking-widest">Describe Your Identity Concept</span> <span className="text-[10px] font-mono text-purple-400 uppercase tracking-widest font-bold">Describe Your Identity Concept</span>
<textarea <textarea
value={concept} value={concept}
onChange={(e) => setConcept(e.target.value)} onChange={(e) => setConcept(e.target.value)}
placeholder="e.g., A minimalist AI agent for legal risk management..." placeholder="e.g., A minimalist AI agent for legal risk management..."
rows={3} rows={3}
className="w-full px-4 py-3 bg-white/[0.03] border border-purple-500/20 text-sm font-mono text-white placeholder:text-white/10 outline-none focus:border-purple-500/50 resize-none transition-all" className="w-full px-4 py-3 bg-white/[0.03] border border-purple-500/40 text-sm font-mono text-white placeholder:text-white/20 outline-none focus:border-purple-500 resize-none transition-all"
/> />
</div> </div>
)} )}